├── .editorconfig ├── .github └── workflows │ └── gradle-check.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NeoLang ├── .gitignore ├── build.gradle ├── example │ ├── color-scheme.nl │ ├── extra-key.nl │ └── profile.nl └── src │ └── main │ └── java │ └── io │ └── neolang │ ├── frontend │ ├── abstract-visitors.kt │ ├── frontend.kt │ ├── nodes.kt │ ├── tokens.kt │ └── visitors.kt │ └── runtime │ ├── context.kt │ └── types.kt ├── NeoTermBridge ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── aidl │ │ └── io │ │ │ └── neoterm │ │ │ └── bridge │ │ │ └── ISessionConnection.aidl │ ├── java │ │ └── io │ │ │ └── neoterm │ │ │ └── bridge │ │ │ ├── Bridge.java │ │ │ └── SessionId.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── io │ └── neoterm │ └── bridge │ └── ExampleUnitTest.java ├── README.md ├── Xorg ├── .gitignore ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── neoterm │ │ │ ├── Accelerometer.java │ │ │ ├── Audio.java │ │ │ ├── Clipboard.java │ │ │ ├── GLSurfaceView_SDL.java │ │ │ ├── Globals.java │ │ │ ├── Keycodes.java │ │ │ ├── MainActivity.java │ │ │ ├── NeoAccelerometerReader.java │ │ │ ├── NeoAudioThread.java │ │ │ ├── NeoGLView.java │ │ │ ├── NeoRenderer.java │ │ │ ├── NeoTextInput.java │ │ │ ├── NeoXorgSettings.java │ │ │ ├── Settings.java │ │ │ ├── SettingsMenu.java │ │ │ ├── SettingsMenuKeyboard.java │ │ │ ├── SettingsMenuMisc.java │ │ │ ├── SettingsMenuMouse.java │ │ │ ├── Video.java │ │ │ ├── XZInputStream.java │ │ │ └── xorg │ │ │ └── NeoXorgViewClient.java │ └── res │ │ ├── drawable │ │ ├── b1.png │ │ ├── b2.png │ │ ├── b3.png │ │ ├── b4.png │ │ ├── b5.png │ │ ├── b6.png │ │ ├── calibrate.png │ │ ├── dpad.png │ │ ├── keyboard.png │ │ ├── publisherlogo.png │ │ ├── rectangle.png │ │ ├── sun_b1.png │ │ ├── sun_b2.png │ │ ├── sun_b3.png │ │ ├── sun_b4.png │ │ ├── sun_b5.png │ │ ├── sun_b6.png │ │ ├── sun_dpad.png │ │ ├── sun_keyboard.png │ │ ├── tv_border_left.jpg │ │ └── tv_border_top.jpg │ │ ├── raw │ │ ├── dualshock.raw │ │ ├── gba.raw │ │ ├── keen.raw │ │ ├── n64.raw │ │ ├── psx.raw │ │ ├── retro.raw │ │ ├── simpletheme.raw │ │ ├── snes.raw │ │ ├── sun.raw │ │ └── ultimatedroid.raw │ │ ├── values-zh │ │ └── strings.xml │ │ ├── values │ │ ├── dimen.xml │ │ ├── ids.xml │ │ └── strings.xml │ │ └── xml │ │ ├── amiga.xml │ │ ├── amiga_alt.xml │ │ ├── amiga_alt_shift.xml │ │ ├── amiga_old.xml │ │ ├── amiga_shift.xml │ │ ├── atari800.xml │ │ ├── c64.xml │ │ ├── qwerty.xml │ │ ├── qwerty_alt.xml │ │ ├── qwerty_alt_shift.xml │ │ └── qwerty_shift.xml │ └── test │ └── java │ └── io │ └── neoterm │ └── ExampleUnitTest.java ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── colors │ │ ├── Default.nl │ │ ├── Dracula.nl │ │ ├── Material.nl │ │ ├── SolarizedDark.nl │ │ └── SolarizedLight.nl │ ├── eks │ │ ├── default.nl │ │ └── vim.nl │ ├── eks_font.ttf │ ├── fonts │ │ ├── SourceCodePro.ttf │ │ └── UbuntuMono.ttf │ └── scripts │ │ ├── extract-archive │ │ └── open-in-vim │ ├── cpp │ ├── exec.c │ └── neoterm.cpp │ ├── java │ └── io │ │ └── neoterm │ │ ├── App.kt │ │ ├── backend │ │ ├── ByteQueue.java │ │ ├── EmulatorDebug.java │ │ ├── JNI.java │ │ ├── KeyHandler.java │ │ ├── TerminalBuffer.java │ │ ├── TerminalColorScheme.java │ │ ├── TerminalColors.java │ │ ├── TerminalEmulator.java │ │ ├── TerminalOutput.java │ │ ├── TerminalRow.java │ │ ├── TerminalSession.java │ │ ├── TextStyle.java │ │ └── WcWidth.java │ │ ├── component │ │ ├── codegen │ │ │ ├── comp.kt │ │ │ ├── generators.kt │ │ │ └── interfaces.kt │ │ ├── colorscheme │ │ │ ├── comp.kt │ │ │ └── data.kt │ │ ├── comp.kt │ │ ├── completion │ │ │ ├── comp.kt │ │ │ ├── data.kt │ │ │ ├── listeners.kt │ │ │ └── providers.kt │ │ ├── config │ │ │ ├── comp.kt │ │ │ ├── defaults.kt │ │ │ └── loaders.kt │ │ ├── data.kt │ │ ├── extrakey │ │ │ ├── comp.kt │ │ │ └── data.kt │ │ ├── font │ │ │ ├── comp.kt │ │ │ └── data.kt │ │ ├── pm │ │ │ ├── NeoPackageParser.java │ │ │ ├── PackageComponent.java │ │ │ ├── Source.java │ │ │ ├── data.kt │ │ │ └── helper.kt │ │ ├── profile │ │ │ ├── comp.kt │ │ │ └── data.kt │ │ ├── session │ │ │ ├── comp.kt │ │ │ ├── shell.kt │ │ │ └── x.kt │ │ └── userscript │ │ │ └── comp.kt │ │ ├── framework │ │ ├── NeoTermDatabase.java │ │ ├── database │ │ │ ├── DatabaseDataType.java │ │ │ ├── NeoTermSQLiteConfig.java │ │ │ ├── OnDatabaseUpgradedListener.java │ │ │ ├── SQLStatementHelper.java │ │ │ ├── SQLTypeParser.java │ │ │ ├── TableHelper.java │ │ │ ├── ValueHelper.java │ │ │ ├── annotation │ │ │ │ ├── ID.java │ │ │ │ ├── Ignore.java │ │ │ │ ├── NotNull.java │ │ │ │ └── Table.java │ │ │ └── bean │ │ │ │ └── TableInfo.java │ │ └── reflection │ │ │ ├── NullPointer.java │ │ │ ├── Reflect.java │ │ │ └── ReflectionException.java │ │ ├── frontend │ │ ├── completion │ │ │ ├── CandidatePopupWindow.kt │ │ │ └── MaxHeightView.kt │ │ ├── floating │ │ │ └── dialog.kt │ │ └── session │ │ │ ├── terminal │ │ │ ├── data.kt │ │ │ ├── events.kt │ │ │ ├── term-basic.kt │ │ │ └── term-standard.kt │ │ │ └── view │ │ │ ├── GestureAndScaleRecognizer.kt │ │ │ ├── TerminalRenderer.java │ │ │ ├── TerminalView.java │ │ │ ├── TerminalViewClient.java │ │ │ └── extrakey │ │ │ ├── CombinedSequence.kt │ │ │ ├── ExtraKeysView.kt │ │ │ └── buttons.kt │ │ ├── services │ │ └── NeoTermService.kt │ │ ├── setup │ │ ├── SetupThread.java │ │ ├── SourceConnection.java │ │ ├── connections.kt │ │ └── setup.kt │ │ ├── ui │ │ ├── customize │ │ │ ├── BaseCustomizeActivity.kt │ │ │ ├── ColorSchemeActivity.kt │ │ │ ├── CustomizeActivity.kt │ │ │ └── model.kt │ │ ├── other │ │ │ ├── AboutActivity.kt │ │ │ ├── BonusActivity.kt │ │ │ ├── CrashActivity.kt │ │ │ └── SetupActivity.kt │ │ ├── pm │ │ │ ├── PackageManagerActivity.kt │ │ │ ├── model.kt │ │ │ └── view │ │ │ │ └── RecyclerTabLayout.java │ │ ├── settings │ │ │ ├── BasePreferenceActivity.kt │ │ │ ├── GeneralSettingsActivity.kt │ │ │ ├── SettingActivity.kt │ │ │ └── UISettingsActivity.kt │ │ └── term │ │ │ ├── NeoTermActivity.kt │ │ │ ├── NeoTermRemoteInterface.kt │ │ │ ├── SessionRemover.kt │ │ │ └── tabs.kt │ │ └── utils │ │ ├── CrashHandler.kt │ │ ├── FullScreenHelper.kt │ │ ├── NLog.kt │ │ ├── NeoPermission.kt │ │ ├── StringDistance.java │ │ ├── Terminals.kt │ │ └── utils.kt │ ├── jniLibs │ └── arm64-v8a │ │ └── libnexec.so │ └── res │ ├── drawable-hdpi │ ├── ic_add_box_white_24dp.png │ ├── ic_apps_white_36dp.png │ ├── ic_backup_restore_white_36dp.png │ ├── ic_customization_white_36dp.png │ ├── ic_done.png │ ├── ic_general_white_36dp.png │ ├── ic_guide_white_36dp.png │ ├── ic_info_white_36dp.png │ ├── ic_install_white_36.png │ ├── ic_search.png │ └── ic_ui_white_36dp.png │ ├── drawable-mdpi │ ├── ic_add_box_white_24dp.png │ └── ic_search.png │ ├── drawable-xhdpi │ ├── ic_add_box_white_24dp.png │ └── ic_search.png │ ├── drawable-xxhdpi │ ├── ic_add_box_white_24dp.png │ ├── ic_search.png │ ├── text_select_handle_left_mtrl_alpha.png │ └── text_select_handle_right_mtrl_alpha.png │ ├── drawable-xxxhdpi │ └── ic_add_box_white_24dp.png │ ├── drawable │ ├── banner.png │ ├── ic_description.xml │ ├── ic_donate.xml │ ├── ic_github.xml │ ├── ic_info.xml │ ├── ic_neoterm.xml │ ├── ic_new_session.xml │ ├── ic_person.xml │ ├── ic_tab_icon.png │ ├── ic_terminal_running.xml │ ├── plat_logo.png │ ├── text_select_handle_left_material.xml │ └── text_select_handle_right_material.xml │ ├── layout │ ├── dialog_edit_text.xml │ ├── dialog_edit_two_text.xml │ ├── item_color.xml │ ├── item_complete_candidate.xml │ ├── item_package.xml │ ├── layout_pm_package_list.xml │ ├── popup_auto_complete.xml │ ├── ui_about.xml │ ├── ui_color_scheme.xml │ ├── ui_command_shortcut.xml │ ├── ui_crash.xml │ ├── ui_customize.xml │ ├── ui_faq.xml │ ├── ui_main.xml │ ├── ui_pm.xml │ ├── ui_pm_single_tab.xml │ ├── ui_setup.xml │ ├── ui_term.xml │ ├── ui_term_dialog.xml │ ├── ui_term_embedded.xml │ ├── ui_user_script_list.xml │ └── ui_xorg.xml │ ├── menu │ ├── menu_color_editor.xml │ ├── menu_main.xml │ └── menu_pm.xml │ ├── mipmap-hdpi │ ├── about_logo.png │ └── ic_danger.png │ ├── mipmap-xhdpi │ ├── about_logo.png │ └── ic_danger.png │ ├── mipmap-xxhdpi │ ├── about_logo.png │ └── ic_danger.png │ ├── mipmap-xxxhdpi │ ├── about_logo.png │ ├── ic_danger.png │ └── ic_launcher_neoterm_round.png │ ├── raw │ └── bell.ogg │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ ├── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── preference_keys.xml │ ├── shortcut_configs.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── app_shortcuts.xml │ ├── backup_config.xml │ ├── setting_general.xml │ ├── settings_main.xml │ └── settings_ui.xml ├── artwork ├── NeoTerm_round.psd ├── NeoTerm_round_border.psd ├── neoterm.psd └── old │ ├── icon-512-old.png │ ├── icon-512.png │ └── neoterm.psd ├── build.gradle ├── chrome-tabs ├── .gitignore ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── mrapp │ │ └── android │ │ └── tabswitcher │ │ ├── Animation.java │ │ ├── Layout.java │ │ ├── LayoutPolicy.java │ │ ├── PeekAnimation.java │ │ ├── RevealAnimation.java │ │ ├── SwipeAnimation.java │ │ ├── Tab.java │ │ ├── TabCloseListener.java │ │ ├── TabPreviewListener.java │ │ ├── TabSwitcher.java │ │ ├── TabSwitcherDecorator.java │ │ ├── TabSwitcherListener.java │ │ ├── drawable │ │ └── TabSwitcherDrawable.java │ │ ├── iterator │ │ ├── AbstractTabItemIterator.java │ │ ├── ArrayTabItemIterator.java │ │ └── TabItemIterator.java │ │ ├── layout │ │ ├── AbstractDragHandler.java │ │ ├── AbstractTabSwitcherLayout.java │ │ ├── AbstractTabViewHolder.java │ │ ├── Arithmetics.java │ │ ├── ChildRecyclerAdapter.java │ │ ├── TabSwitcherLayout.java │ │ └── phone │ │ │ ├── PhoneArithmetics.java │ │ │ ├── PhoneDragHandler.java │ │ │ ├── PhoneRecyclerAdapter.java │ │ │ ├── PhoneTabSwitcherLayout.java │ │ │ ├── PhoneTabViewHolder.java │ │ │ └── PreviewDataBinder.java │ │ ├── model │ │ ├── Model.java │ │ ├── Restorable.java │ │ ├── State.java │ │ ├── TabItem.java │ │ ├── TabSwitcherModel.java │ │ └── Tag.java │ │ └── view │ │ └── TabSwitcherButton.java │ └── res │ ├── drawable-hdpi │ ├── phone_close_tab_icon.png │ ├── phone_tab_background.9.png │ ├── phone_tab_border.9.png │ └── tab_switcher_drawable_background.png │ ├── drawable-mdpi │ ├── ic_close_tab_18dp.png │ ├── phone_tab_background.9.png │ ├── phone_tab_border.9.png │ └── tab_switcher_drawable_background.png │ ├── drawable-xhdpi │ ├── ic_close_tab_18dp.png │ ├── phone_tab_background.9.png │ ├── phone_tab_border.9.png │ └── tab_switcher_drawable_background.png │ ├── drawable-xxhdpi │ ├── ic_close_tab_18dp.png │ ├── phone_tab_background.9.png │ ├── phone_tab_border.9.png │ └── tab_switcher_drawable_background.png │ ├── drawable-xxxhdpi │ ├── ic_close_tab_18dp.png │ ├── phone_tab_background.9.png │ ├── phone_tab_border.9.png │ └── tab_switcher_drawable_background.png │ ├── layout-land │ └── phone_tab.xml │ ├── layout │ ├── phone_tab.xml │ ├── phone_toolbar.xml │ └── tab_switcher_menu_item.xml │ ├── values-v21 │ └── styles.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── integers.xml │ └── styles.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/workflows/gradle-check.yml: -------------------------------------------------------------------------------- 1 | name: Gralde check 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Clone repository 16 | uses: actions/checkout@v2 17 | - name: Build 18 | run: | 19 | ./gradlew :app:assembleDebug 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | .cxx/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Intellij project files 27 | *.iml 28 | *.ipr 29 | *.iws 30 | .idea/ 31 | .gradle 32 | /local.properties 33 | /.idea/workspace.xml 34 | /.idea/libraries 35 | .DS_Store 36 | /build 37 | /captures 38 | .externalNativeBuild 39 | 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | # How to 4 | 5 | NeoTerm is a free software, so any contributions and any contribution types are welcomed! 6 | 7 | -------------------------------------------------------------------------------- /NeoLang/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /NeoLang/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'kotlin' 3 | 4 | dependencies { 5 | implementation fileTree(dir: 'libs', include: ['*.jar']) 6 | compile rootProject.ext.deps["kotlin-stdlib"] 7 | } 8 | 9 | buildscript { 10 | repositories { 11 | mavenCentral() 12 | } 13 | dependencies { 14 | classpath rootProject.ext.deps["kotlin-gradle-plugin"] 15 | } 16 | } 17 | repositories { 18 | mavenCentral() 19 | } 20 | compileKotlin { 21 | kotlinOptions { 22 | jvmTarget = "1.8" 23 | } 24 | } 25 | compileTestKotlin { 26 | kotlinOptions { 27 | jvmTarget = "1.8" 28 | } 29 | } 30 | dependencies { 31 | testImplementation rootProject.ext.deps["junit"] 32 | } 33 | -------------------------------------------------------------------------------- /NeoLang/example/color-scheme.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "NeoTerm Default Color Scheme" 3 | version: 1.1 4 | 5 | colors: { 6 | background: #777721 7 | foreground: #111233 8 | color1: #color1 9 | color2: #color2 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /NeoLang/example/extra-key.nl: -------------------------------------------------------------------------------- 1 | extra-key: { 2 | version: 10 3 | with-default: true 4 | 5 | program: [ "vim", "vi" ] 6 | 7 | key: [ 8 | { 9 | display: "Ctrl" 10 | code: "" 11 | with-enter: true 12 | }, 13 | { 14 | display: "Esc" 15 | code: "" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /NeoLang/example/profile.nl: -------------------------------------------------------------------------------- 1 | profile-shell: { 2 | name: "Simple Profile" 3 | bell: true 4 | } 5 | -------------------------------------------------------------------------------- /NeoLang/src/main/java/io/neolang/frontend/tokens.kt: -------------------------------------------------------------------------------- 1 | package io.neolang.frontend 2 | 3 | import io.neolang.runtime.NeoLangValue 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | class NeoLangEOFToken : NeoLangToken(NeoLangTokenType.EOF, NeoLangTokenValue.EOF) 9 | 10 | /** 11 | * @author kiva 12 | */ 13 | 14 | open class NeoLangToken(val tokenType: NeoLangTokenType, val tokenValue: NeoLangTokenValue) { 15 | var lineNumber = 0 16 | 17 | override fun toString(): String { 18 | return "Token { tokenType: $tokenType, tokenValue: $tokenValue };" 19 | } 20 | } 21 | 22 | /** 23 | * @author kiva 24 | */ 25 | 26 | enum class NeoLangTokenType { 27 | NUMBER, 28 | ID, 29 | STRING, 30 | BRACKET_START, 31 | BRACKET_END, 32 | ARRAY_START, 33 | ARRAY_END, 34 | COLON, 35 | COMMA, 36 | EOL, 37 | EOF, 38 | } 39 | 40 | /** 41 | * @author kiva 42 | */ 43 | class NeoLangTokenValue(val value: NeoLangValue) { 44 | 45 | override fun toString(): String { 46 | return value.asString() 47 | } 48 | 49 | companion object { 50 | val COLON = NeoLangTokenValue(NeoLangValue(":")) 51 | val COMMA = NeoLangTokenValue(NeoLangValue(",")) 52 | val QUOTE = NeoLangTokenValue(NeoLangValue("\"")) 53 | val EOF = NeoLangTokenValue(NeoLangValue("")) 54 | 55 | val BRACKET_START = NeoLangTokenValue(NeoLangValue("{")) 56 | val BRACKET_END = NeoLangTokenValue(NeoLangValue("}")) 57 | val ARRAY_START = NeoLangTokenValue(NeoLangValue("[")) 58 | val ARRAY_END = NeoLangTokenValue(NeoLangValue("]")) 59 | 60 | fun wrap(tokenText: String): NeoLangTokenValue { 61 | return when (tokenText) { 62 | COLON.value.asString() -> COLON 63 | COMMA.value.asString() -> COMMA 64 | QUOTE.value.asString() -> QUOTE 65 | BRACKET_START.value.asString() -> BRACKET_START 66 | BRACKET_END.value.asString() -> BRACKET_END 67 | ARRAY_START.value.asString() -> ARRAY_START 68 | ARRAY_END.value.asString() -> ARRAY_END 69 | else -> NeoLangTokenValue(NeoLangValue(tokenText)) 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /NeoLang/src/main/java/io/neolang/runtime/context.kt: -------------------------------------------------------------------------------- 1 | package io.neolang.runtime 2 | 3 | /** 4 | * @author kiva 5 | */ 6 | class NeoLangContext(val contextName: String) { 7 | companion object { 8 | private val emptyContext = NeoLangContext("") 9 | } 10 | 11 | private val attributes = mutableMapOf() 12 | val children = mutableListOf() 13 | var parent: NeoLangContext? = null 14 | 15 | fun defineAttribute(attributeName: String, attributeValue: NeoLangValue): NeoLangContext { 16 | attributes[attributeName] = attributeValue 17 | return this 18 | } 19 | 20 | fun getAttribute(attributeName: String): NeoLangValue { 21 | return attributes[attributeName] ?: parent?.getAttribute(attributeName) ?: NeoLangValue.UNDEFINED 22 | } 23 | 24 | fun getChild(contextName: String): NeoLangContext { 25 | var found: NeoLangContext? = null 26 | children.forEach { 27 | if (it.contextName == contextName) { 28 | found = it 29 | } 30 | } 31 | return found ?: emptyContext 32 | } 33 | 34 | fun getAttributes(): Map { 35 | return attributes 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /NeoTermBridge/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /NeoTermBridge/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def libraryVersionCode = 1 4 | def libraryVersionName = "1.0" 5 | 6 | android { 7 | compileSdkVersion rootProject.ext.android.COMPILE_SDK_VERSION 8 | 9 | defaultConfig { 10 | minSdkVersion rootProject.ext.android.MIN_SDK_VERSION 11 | targetSdkVersion rootProject.ext.android.TARGET_SDK_VERSION 12 | versionCode libraryVersionCode 13 | versionName libraryVersionName 14 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: 'libs', include: ['*.jar']) 26 | implementation 'androidx.appcompat:appcompat:1.2.0' 27 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { 28 | exclude group: 'com.android.support', module: 'support-annotations' 29 | }) 30 | testImplementation rootProject.ext.deps["junit"] 31 | } 32 | -------------------------------------------------------------------------------- /NeoTermBridge/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /NeoTermBridge/src/main/aidl/io/neoterm/bridge/ISessionConnection.aidl: -------------------------------------------------------------------------------- 1 | // ISessionConnection.aidl 2 | package io.neoterm.bridge; 3 | 4 | // Declare any non-default types here with import statements 5 | 6 | interface ISessionConnection { 7 | } 8 | -------------------------------------------------------------------------------- /NeoTermBridge/src/main/java/io/neoterm/bridge/Bridge.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.bridge; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | public class Bridge { 12 | public static final String ACTION_EXECUTE = "neoterm.action.remote.execute"; 13 | public static final String ACTION_SILENT_RUN = "neoterm.action.remote.silent-run"; 14 | public static final String EXTRA_COMMAND = "neoterm.extra.remote.execute.command"; 15 | public static final String EXTRA_SESSION_ID = "neoterm.extra.remote.execute.session"; 16 | public static final String EXTRA_FOREGROUND = "neoterm.extra.remote.execute.foreground"; 17 | private static final String NEOTERM_PACKAGE = "io.neoterm"; 18 | private static final String NEOTERM_REMOTE_INTERFACE = "io.neoterm.ui.term.NeoTermRemoteInterface"; 19 | private static final ComponentName NEOTERM_COMPONENT = new ComponentName(NEOTERM_PACKAGE, NEOTERM_REMOTE_INTERFACE); 20 | 21 | private Bridge() throws IllegalAccessException { 22 | throw new IllegalAccessException(); 23 | } 24 | 25 | public static Intent createExecuteIntent(SessionId sessionId, 26 | String command, 27 | boolean foreground) { 28 | Objects.requireNonNull(command, "command"); 29 | Objects.requireNonNull(sessionId, "session id"); 30 | 31 | Intent intent = new Intent(ACTION_EXECUTE); 32 | intent.setComponent(NEOTERM_COMPONENT); 33 | intent.putExtra(EXTRA_COMMAND, command); 34 | intent.putExtra(EXTRA_SESSION_ID, sessionId.getSessionId()); 35 | intent.putExtra(EXTRA_FOREGROUND, foreground); 36 | return intent; 37 | } 38 | 39 | public static Intent createExecuteIntent(SessionId sessionId, String command) { 40 | return createExecuteIntent(sessionId, command, true); 41 | } 42 | 43 | public static Intent createExecuteIntent(String command) { 44 | return createExecuteIntent(SessionId.NEW_SESSION, command); 45 | } 46 | 47 | public static Intent createExecuteIntent(String command, boolean foreground) { 48 | return createExecuteIntent(SessionId.NEW_SESSION, command, foreground); 49 | } 50 | 51 | public static SessionId parseResult(Intent data) { 52 | Objects.requireNonNull(data, "data"); 53 | 54 | if (data.hasExtra(EXTRA_SESSION_ID)) { 55 | String handle = data.getStringExtra(EXTRA_SESSION_ID); 56 | return SessionId.of(handle); 57 | } 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /NeoTermBridge/src/main/java/io/neoterm/bridge/SessionId.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.bridge; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | public class SessionId { 9 | /** 10 | * Created a new session. 11 | */ 12 | public static final SessionId NEW_SESSION = SessionId.of("new"); 13 | 14 | /** 15 | * Presents current session in NeoTerm. 16 | */ 17 | public static final SessionId CURRENT_SESSION = SessionId.of("current"); 18 | 19 | private final String sessionId; 20 | 21 | SessionId(String sessionId) { 22 | this.sessionId = sessionId; 23 | } 24 | 25 | public String getSessionId() { 26 | return sessionId; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "TerminalSession { id = " + sessionId + " }"; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) return true; 37 | if (o == null || getClass() != o.getClass()) return false; 38 | SessionId sessionId1 = (SessionId) o; 39 | return Objects.equals(sessionId, sessionId1.sessionId); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Objects.hash(sessionId); 45 | } 46 | 47 | public static SessionId of(String sessionId) { 48 | return new SessionId(sessionId); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NeoTermBridge/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NeoTermBridge 3 | 4 | -------------------------------------------------------------------------------- /NeoTermBridge/src/test/java/io/neoterm/bridge/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.bridge; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NeoTerm 2 | ======= 3 | [![Travis build status](https://travis-ci.org/NeoTerm/NeoTerm.svg?branch=master)](https://travis-ci.org/NeoTerm/NeoTerm) 4 | ![](https://img.shields.io/badge/language-Kotlin-green.svg) 5 | ![](https://img.shields.io/badge/license-GPLv3-000000.svg) 6 | 7 | A modern-designed android terminal emulator for the 21st century. 8 | 9 | ### Our Pledge 10 | 11 | Originally, NeoTerm was designed as the front end of Termux to provide some functions that Termux didn't have, but we 12 | found it very convenient. In continuous development, we discovered our goal: to be the best terminal for Android. 13 | 14 | ### Help & Documentation 15 | 16 | View on [GitBook](https://neoterm.gitbooks.io/neoterm-wiki/content) 17 | 18 | View on [GitHub](https://github.com/NeoTerm/NeoTerm-Wiki) 19 | 20 | ### Download 21 | 22 | [GitHub Release Page](https://github.com/NeoTerm/NeoTerm/releases) 23 | 24 | [lzzySoft's F-Droid repo](https://apt.izzysoft.de/fdroid/index/apk/io.neoterm) (thanks to @lzzySoft) 25 | -------------------------------------------------------------------------------- /Xorg/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Xorg/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android.COMPILE_SDK_VERSION 5 | 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.android.MIN_SDK_VERSION 8 | targetSdkVersion rootProject.ext.android.TARGET_SDK_VERSION 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | } 18 | } 19 | 20 | sourceSets { 21 | main { 22 | jniLibs.srcDirs = ['src/main/jniLibs'] 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(dir: 'libs', include: ['*.jar']) 29 | 30 | implementation rootProject.ext.deps["appcompat-v7"] 31 | testImplementation rootProject.ext.deps["junit"] 32 | 33 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { 34 | exclude group: 'com.android.support', module: 'support-annotations' 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /Xorg/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoAccelerometerReader.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | 9 | public class NeoAccelerometerReader extends AccelerometerReader { 10 | public NeoAccelerometerReader(Context context) { 11 | super(context); 12 | } 13 | 14 | public static void setGyroInvertedOrientation(boolean invertedOrientation) { 15 | gyro.invertedOrientation = invertedOrientation; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoAudioThread.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import io.neoterm.xorg.NeoXorgViewClient; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | 9 | public class NeoAudioThread extends AudioThread { 10 | public NeoAudioThread(NeoXorgViewClient client) { 11 | super(client); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoGLView.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import io.neoterm.xorg.NeoXorgViewClient; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | 9 | public class NeoGLView extends DemoGLSurfaceView { 10 | public NeoGLView(NeoXorgViewClient client) { 11 | super(client); 12 | } 13 | 14 | public void callNativeScreenKeyboardShown(int shown) { 15 | nativeScreenKeyboardShown(shown); 16 | } 17 | 18 | public void callNativeScreenVisibleRect(int x, int y, int w, int h) { 19 | nativeScreenVisibleRect(x, y, w, h); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoRenderer.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | /** 4 | * @author kiva 5 | */ 6 | 7 | public class NeoRenderer { 8 | public static void callNativeTextInputFinished() { 9 | DemoRenderer.nativeTextInputFinished(); 10 | } 11 | 12 | public static void callNativeTextInput(int ascii, int unicode) { 13 | DemoRenderer.nativeTextInput(ascii, unicode); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoTextInput.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import io.neoterm.xorg.R; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | 9 | public class NeoTextInput { 10 | public static int TextInputKeyboardList[][] = 11 | { 12 | {0, R.xml.qwerty, R.xml.c64, R.xml.amiga, R.xml.atari800}, 13 | {0, R.xml.qwerty_shift, R.xml.c64, R.xml.amiga_shift, R.xml.atari800}, 14 | {0, R.xml.qwerty_alt, R.xml.c64, R.xml.amiga_alt, R.xml.atari800}, 15 | {0, R.xml.qwerty_alt_shift, R.xml.c64, R.xml.amiga_alt_shift, R.xml.atari800} 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/NeoXorgSettings.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import io.neoterm.xorg.NeoXorgViewClient; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | 9 | public class NeoXorgSettings { 10 | public static void init(NeoXorgViewClient client) { 11 | Settings.Load(client); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Xorg/src/main/java/io/neoterm/xorg/NeoXorgViewClient.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.xorg; 2 | 3 | import android.content.Context; 4 | import android.view.Window; 5 | import android.view.WindowManager; 6 | import io.neoterm.NeoGLView; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | 12 | public interface NeoXorgViewClient { 13 | Context getContext(); 14 | 15 | boolean isKeyboardWithoutTextInputShown(); 16 | 17 | void showScreenKeyboardWithoutTextInputField(int flags); 18 | 19 | void setScreenKeyboardHintMessage(String hideMessage); 20 | 21 | boolean isScreenKeyboardShown(); 22 | 23 | void showScreenKeyboard(String message); 24 | 25 | void hideScreenKeyboard(); 26 | 27 | void runOnUiThread(Runnable runnable); 28 | 29 | void updateScreenOrientation(); 30 | 31 | void initScreenOrientation(); 32 | 33 | boolean isRunningOnOUYA(); 34 | 35 | NeoGLView getGLView(); 36 | 37 | Window getWindow(); 38 | 39 | WindowManager getWindowManager(); 40 | 41 | void setSystemMousePointerVisible(int visible); 42 | 43 | boolean isPaused(); 44 | } 45 | -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b1.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b2.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b3.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b4.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b5.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/b6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/b6.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/calibrate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/calibrate.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/dpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/dpad.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/keyboard.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/publisherlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/publisherlogo.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/rectangle.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b1.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b2.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b3.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b4.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b5.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_b6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_b6.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_dpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_dpad.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/sun_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/sun_keyboard.png -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/tv_border_left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/tv_border_left.jpg -------------------------------------------------------------------------------- /Xorg/src/main/res/drawable/tv_border_top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/drawable/tv_border_top.jpg -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/dualshock.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/dualshock.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/gba.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/gba.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/keen.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/keen.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/n64.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/n64.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/psx.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/psx.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/retro.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/retro.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/simpletheme.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/simpletheme.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/snes.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/snes.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/sun.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/sun.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/raw/ultimatedroid.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/Xorg/src/main/res/raw/ultimatedroid.raw -------------------------------------------------------------------------------- /Xorg/src/main/res/values/dimen.xml: -------------------------------------------------------------------------------- 1 | 2 | 0dp 3 | 0dp 4 | 5 | -------------------------------------------------------------------------------- /Xorg/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Xorg/src/main/res/xml/amiga.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Xorg/src/test/java/io/neoterm/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.neoterm; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | add_library(neoterm 4 | SHARED 5 | src/main/cpp/neoterm.cpp) 6 | 7 | target_link_libraries(neoterm) 8 | 9 | #add_library(nexec 10 | # SHARED 11 | # src/main/cpp/exec.c) 12 | 13 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.android.COMPILE_SDK_VERSION 6 | 7 | defaultConfig { 8 | applicationId "io.neoterm" 9 | minSdkVersion rootProject.ext.android.MIN_SDK_VERSION 10 | targetSdkVersion rootProject.ext.android.TARGET_SDK_VERSION 11 | versionCode 38 12 | versionName "2.0.5" 13 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 14 | resConfigs "zh-rCN", "zh-rTW" 15 | externalNativeBuild { 16 | cmake { 17 | cppFlags "-std=c++11" 18 | abiFilters 'arm64-v8a' 19 | } 20 | } 21 | sourceSets { 22 | main { 23 | jniLibs.srcDirs = ['src/main/jniLibs'] 24 | } 25 | } 26 | } 27 | buildTypes { 28 | release { 29 | minifyEnabled true 30 | zipAlignEnabled true 31 | shrinkResources true 32 | } 33 | } 34 | externalNativeBuild { 35 | cmake { 36 | path "CMakeLists.txt" 37 | } 38 | } 39 | lintOptions { 40 | abortOnError false 41 | checkReleaseBuilds false 42 | } 43 | compileOptions { 44 | targetCompatibility 1.8 45 | sourceCompatibility 1.8 46 | } 47 | kotlinOptions { 48 | freeCompilerArgs = ["-Xallow-result-return-type"] 49 | } 50 | } 51 | 52 | dependencies { 53 | implementation fileTree(include: ['*.jar'], dir: 'libs') 54 | testImplementation rootProject.ext.deps["junit"] 55 | androidTestImplementation project(path: ':NeoLang') 56 | 57 | implementation rootProject.ext.deps["kotlin-stdlib"] 58 | 59 | implementation 'org.greenrobot:eventbus:3.0.0' 60 | implementation 'com.github.wrdlbrnft:modular-adapter:0.2.0.6' 61 | implementation 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.19' 62 | implementation 'com.simplecityapps:recyclerview-fastscroll:1.0.16' 63 | implementation 'de.psdev.licensesdialog:licensesdialog:1.8.3' 64 | implementation 'com.github.GrenderG:Color-O-Matic:1.1.5' 65 | implementation 'androidx.annotation:annotation:1.2.0' 66 | implementation 'androidx.cardview:cardview:1.0.0' 67 | implementation 'androidx.appcompat:appcompat:1.2.0' 68 | implementation 'androidx.appcompat:appcompat-resources:1.2.0' 69 | 70 | implementation project(':chrome-tabs') 71 | implementation project(':NeoLang') 72 | implementation project(':NeoTermBridge') 73 | implementation project(':Xorg') 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/assets/colors/Default.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "Default" 3 | version: 1.1 4 | 5 | colors: { 6 | foreground: #ffffff 7 | cursor: #a9aaa9 8 | background: #14181c 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/assets/colors/Dracula.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "Dracula" 3 | version: 1.1 4 | 5 | colors: { 6 | foreground: #f8f8f2 7 | cursor: #f8f8f2 8 | background: #282a36 9 | 10 | color0: #000000 11 | color8: #4d4d4d 12 | 13 | color1: #ff5555 14 | color9: #ff6e67 15 | 16 | color2: #50fa7b 17 | color10: #5af78e 18 | 19 | color3: #f1fa8c 20 | color11: #f4f99d 21 | 22 | color4: #caa9fa 23 | color12: #caa9fa 24 | 25 | color5: #ff79c6 26 | color13: #ff92d0 27 | 28 | color6: #8be9fd 29 | color14: #9aedfe 30 | 31 | color7: #bfbfbf 32 | color15: #e6e6e6 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/assets/colors/Material.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "Material" 3 | version: 1.1 4 | 5 | colors: { 6 | background: #263238 7 | foreground: #eceff1 8 | 9 | color0: #263238 10 | color8: #37474f 11 | 12 | color1: #ff9800 13 | color9: #ffa74d 14 | 15 | color2: #8bc34a 16 | color10: #9ccc65 17 | 18 | color3: #ffc107 19 | color11: #ffa000 20 | 21 | color4: #03a9f4 22 | color12: #81d4fa 23 | 24 | color5: #e91e63 25 | color13: #ad1457 26 | 27 | color6: #009688 28 | color14: #26a69a 29 | 30 | color7: #cfd8dc 31 | color15: #eceff1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/assets/colors/SolarizedDark.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "Solarized Dark" 3 | version: 1.1 4 | 5 | colors: { 6 | background: #002b36 7 | foreground: #839496 8 | cursor: #93a1a1 9 | 10 | color0: #073642 11 | color1: #dc322f 12 | color2: #859900 13 | color3: #b58900 14 | color4: #268bd2 15 | color5: #d33682 16 | color6: #2aa198 17 | color7: #eee8d5 18 | color9: #cb4b16 19 | color8: #002b36 20 | color10: #586e75 21 | color11: #657b83 22 | color12: #839496 23 | color13: #6c71c4 24 | color14: #93a1a1 25 | color15: #fdf6e3 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/assets/colors/SolarizedLight.nl: -------------------------------------------------------------------------------- 1 | color-scheme: { 2 | name: "Solarized Light" 3 | version: 1.1 4 | 5 | colors: { 6 | background: #fdf6e3 7 | foreground: #657b83 8 | cursor: #586e75 9 | 10 | color0: #073642 11 | color1: #dc322f 12 | color2: #859900 13 | color3: #b58900 14 | color4: #268bd2 15 | color5: #d33682 16 | color6: #2aa198 17 | color7: #eee8d5 18 | color8: #002b36 19 | color9: #cb4b16 20 | color10: #586e75 21 | color11: #657b83 22 | color12: #839496 23 | color13: #6c71c4 24 | color14: #93a1a1 25 | color15: #fdf6e3 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/assets/eks/default.nl: -------------------------------------------------------------------------------- 1 | extra-key: { 2 | version: 20 3 | program: [ default ] 4 | 5 | key: [ 6 | { 7 | code: "-" 8 | }, 9 | { 10 | code: "/" 11 | }, 12 | { 13 | code: "\" 14 | }, 15 | { 16 | code: "|" 17 | }, 18 | { 19 | code: "$" 20 | }, 21 | { 22 | code: "<" 23 | }, 24 | { 25 | code: ">" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /app/src/main/assets/eks/vim.nl: -------------------------------------------------------------------------------- 1 | extra-key: { 2 | version: 20 3 | with-default: true 4 | program: [ vim, vi, nvim ] 5 | 6 | key: [ 7 | { 8 | code: " dd" 9 | display: "dd" 10 | with-enter: true 11 | }, 12 | { 13 | code: " :x" 14 | display: ":x" 15 | with-enter: true 16 | }, 17 | { 18 | code: " :w" 19 | display: ":w" 20 | with-enter: true 21 | }, 22 | { 23 | code: " :q" 24 | display: ":q" 25 | with-enter: true 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /app/src/main/assets/eks_font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/assets/eks_font.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/SourceCodePro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/assets/fonts/SourceCodePro.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/UbuntuMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/assets/fonts/UbuntuMono.ttf -------------------------------------------------------------------------------- /app/src/main/assets/scripts/extract-archive: -------------------------------------------------------------------------------- 1 | #!/data/data/io.neoterm/files/usr/bin/bash 2 | 3 | function file_suffix() { 4 | echo "${1##*.}" 5 | } 6 | 7 | function detect_program() { 8 | case "$1" in 9 | *.tar.* | *.tar ) echo "tar xvf %s" ;; 10 | *.7z ) echo "7za x %s" ;; 11 | *.rar ) echo "unrar x %s" ;; 12 | *.zip ) echo "unzip %s" ;; 13 | * ) echo "" ;; 14 | esac 15 | } 16 | 17 | function do_extract() { 18 | local file="$1" 19 | local dir="$(dirname $file)" 20 | 21 | if [[ ! -f "$file" ]]; then 22 | echo "$file: no such file or directory" 23 | return 1 24 | fi 25 | 26 | local program="$(detect_program $file)" 27 | 28 | if [[ "$program" == "" ]]; then 29 | echo "Unsupported format: $(file_suffix $file)" 30 | return 1 31 | fi 32 | 33 | local command="$(printf "$program" "$file")" 34 | 35 | if [[ ! -w "$dir" || ! -r "$file" ]]; then 36 | command="sudo $command" 37 | fi 38 | 39 | cd "$dir" || { 40 | echo "Failed to cd: $dir" 41 | return 1 42 | } 43 | 44 | eval "$command" 45 | } 46 | 47 | if [[ "$#" == 0 ]]; then 48 | echo "You must specific at least a file to extract." 49 | exit 1 50 | fi 51 | 52 | clear 53 | while [[ "$#" != 0 ]]; do 54 | file="$1"; shift 55 | echo "[Extracting] $(basename $file)" 56 | do_extract "$file" 57 | done 58 | -------------------------------------------------------------------------------- /app/src/main/assets/scripts/open-in-vim: -------------------------------------------------------------------------------- 1 | #!/data/data/io.neoterm/files/usr/bin/bash 2 | 3 | set -e 4 | 5 | VIM="$(which vim)" 6 | 7 | if [[ "$VIM"x == ""x ]]; then 8 | echo "Vim is not installed, now installing..." 9 | apt update && apt install -y vim 10 | fi 11 | 12 | $VIM "$@" 13 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/App.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.view.Gravity 9 | import android.widget.Toast 10 | import androidx.appcompat.app.AlertDialog 11 | import io.neoterm.component.NeoInitializer 12 | import io.neoterm.component.config.NeoPreference 13 | import io.neoterm.ui.other.BonusActivity 14 | import io.neoterm.utils.CrashHandler 15 | 16 | /** 17 | * @author kiva 18 | */ 19 | class App : Application() { 20 | override fun onCreate() { 21 | super.onCreate() 22 | app = this 23 | NeoPreference.init(this) 24 | CrashHandler.init() 25 | NeoInitializer.init(this) 26 | } 27 | 28 | fun errorDialog(context: Context, message: Int, dismissCallback: (() -> Unit)?) { 29 | errorDialog(context, getString(message), dismissCallback) 30 | } 31 | 32 | fun errorDialog(context: Context, message: String, dismissCallback: (() -> Unit)?) { 33 | AlertDialog.Builder(context) 34 | .setTitle(R.string.error) 35 | .setMessage(message) 36 | .setNegativeButton(android.R.string.no, null) 37 | .setPositiveButton(R.string.show_help) { _, _ -> 38 | openHelpLink() 39 | } 40 | .setOnDismissListener { 41 | dismissCallback?.invoke() 42 | } 43 | .show() 44 | } 45 | 46 | fun openHelpLink() { 47 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://neoterm.gitbooks.io/neoterm-wiki/content/")) 48 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 49 | startActivity(intent) 50 | } 51 | 52 | fun easterEgg(context: Context, message: String) { 53 | val happyCount = NeoPreference.loadInt(NeoPreference.KEY_HAPPY_EGG, 0) + 1 54 | NeoPreference.store(NeoPreference.KEY_HAPPY_EGG, happyCount) 55 | 56 | val trigger = NeoPreference.VALUE_HAPPY_EGG_TRIGGER 57 | 58 | if (happyCount == trigger / 2) { 59 | @SuppressLint("ShowToast") 60 | val toast = Toast.makeText(this, message, Toast.LENGTH_LONG) 61 | toast.setGravity(Gravity.CENTER, 0, 0) 62 | toast.show() 63 | } else if (happyCount > trigger) { 64 | NeoPreference.store(NeoPreference.KEY_HAPPY_EGG, 0) 65 | context.startActivity(Intent(context, BonusActivity::class.java)) 66 | } 67 | } 68 | 69 | companion object { 70 | private var app: App? = null 71 | 72 | fun get(): App { 73 | return app!! 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/backend/EmulatorDebug.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.backend; 2 | 3 | import android.util.Log; 4 | 5 | public final class EmulatorDebug { 6 | 7 | /** 8 | * The tag to use with {@link Log}. 9 | */ 10 | public static final String LOG_TAG = "NeoTerm-Emulator"; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/backend/JNI.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.backend; 2 | 3 | /** 4 | * Native methods for creating and managing pseudoterminal subprocesses. C code is in jni/termux.c. 5 | */ 6 | final class JNI { 7 | 8 | static { 9 | System.loadLibrary("neoterm"); 10 | } 11 | 12 | /** 13 | * Create a subprocess. Differs from {@link ProcessBuilder} in that a pseudoterminal is used to communicate with the 14 | * subprocess. 15 | *

16 | * Callers are responsible for calling {@link #close(int)} on the returned file descriptor. 17 | * 18 | * @param cmd The command to execute 19 | * @param cwd The current working directory for the executed command 20 | * @param args An array of arguments to the command 21 | * @param envVars An array of strings of the form "VAR=value" to be added to the environment of the process 22 | * @param processId A one-element array to which the process ID of the started process will be written. 23 | * @return the file descriptor resulting from opening /dev/ptmx master device. The sub process will have opened the 24 | * slave device counterpart (/dev/pts/$N) and have it as stdint, stdout and stderr. 25 | */ 26 | public static native int createSubprocess(String cmd, String cwd, String[] args, String[] envVars, int[] processId, int rows, int columns); 27 | 28 | /** 29 | * Set the window size for a given pty, which allows connected programs to learn how large their screen is. 30 | */ 31 | public static native void setPtyWindowSize(int fd, int rows, int cols); 32 | 33 | /** 34 | * Causes the calling thread to wait for the process associated with the receiver to finish executing. 35 | * 36 | * @return if >= 0, the exit status of the process. If < 0, the signal causing the process to stop negated. 37 | */ 38 | public static native int waitFor(int processId); 39 | 40 | /** 41 | * Close a file descriptor through the close(2) system call. 42 | */ 43 | public static native void close(int fileDescriptor); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/backend/TerminalOutput.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.backend; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | 5 | /** 6 | * A client which receives callbacks from events triggered by feeding input to a {@link TerminalEmulator}. 7 | */ 8 | public abstract class TerminalOutput { 9 | 10 | /** 11 | * Write a string using the UTF-8 encoding to the terminal client. 12 | */ 13 | public final void write(String data) { 14 | byte[] bytes = data.getBytes(StandardCharsets.UTF_8); 15 | write(bytes, 0, bytes.length); 16 | } 17 | 18 | /** 19 | * Write bytes to the terminal client. 20 | */ 21 | public abstract void write(byte[] data, int offset, int count); 22 | 23 | /** 24 | * Notify the terminal client that the terminal title has changed. 25 | */ 26 | public abstract void titleChanged(String oldTitle, String newTitle); 27 | 28 | /** 29 | * Notify the terminal client that the terminal title has changed. 30 | */ 31 | public abstract void clipboardText(String text); 32 | 33 | /** 34 | * Notify the terminal client that a bell character (ASCII 7, bell, BEL, \a, ^G)) has been received. 35 | */ 36 | public abstract void onBell(); 37 | 38 | public abstract void onColorsChanged(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/codegen/comp.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.codegen 2 | 3 | import io.neoterm.component.NeoComponent 4 | 5 | 6 | class CodeGenComponent : NeoComponent { 7 | override fun onServiceInit() { 8 | } 9 | 10 | override fun onServiceDestroy() { 11 | } 12 | 13 | override fun onServiceObtained() { 14 | } 15 | 16 | fun newGenerator(codeObject: CodeGenObject): CodeGenerator { 17 | val parameter = CodeGenParameter() 18 | return codeObject.getCodeGenerator(parameter) 19 | } 20 | } 21 | 22 | class CodeGenParameter 23 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/codegen/generators.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.codegen 2 | 3 | import io.neoterm.component.ComponentManager 4 | import io.neoterm.component.colorscheme.NeoColorScheme 5 | import io.neoterm.component.config.ConfigureComponent 6 | 7 | class NeoColorGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter) { 8 | override fun getGeneratorName(): String { 9 | return "NeoColorScheme-Generator" 10 | } 11 | 12 | override fun generateCode(codeGenObject: CodeGenObject): String { 13 | if (codeGenObject !is NeoColorScheme) { 14 | throw RuntimeException("Invalid object type, expected NeoColorScheme, got ${codeGenObject.javaClass.simpleName}") 15 | } 16 | 17 | return buildString { 18 | start(this) 19 | generateMetaData(this, codeGenObject) 20 | generateColors(this, codeGenObject) 21 | end(this) 22 | } 23 | } 24 | 25 | private fun start(builder: StringBuilder) { 26 | builder.append("${NeoColorScheme.CONTEXT_META_NAME}: {\n") 27 | } 28 | 29 | private fun end(builder: StringBuilder) { 30 | builder.append("}\n") 31 | } 32 | 33 | private fun generateMetaData(builder: StringBuilder, colorScheme: NeoColorScheme) { 34 | val component = ComponentManager.getComponent() 35 | 36 | builder.append(" ${NeoColorScheme.COLOR_META_NAME}: \"${colorScheme.colorName}\"\n") 37 | builder.append( 38 | " ${NeoColorScheme.COLOR_META_VERSION}: ${ 39 | colorScheme.colorVersion 40 | ?: component.getLoaderVersion() 41 | }\n", 42 | ) 43 | builder.append("\n") 44 | } 45 | 46 | private fun generateColors(builder: StringBuilder, colorScheme: NeoColorScheme) { 47 | builder.append(" ${NeoColorScheme.CONTEXT_COLOR_NAME}: {\n") 48 | 49 | builder.append(" ${NeoColorScheme.COLOR_DEF_BACKGROUND}: ${colorScheme.backgroundColor}\n") 50 | builder.append(" ${NeoColorScheme.COLOR_DEF_FOREGROUND}: ${colorScheme.foregroundColor}\n") 51 | builder.append(" ${NeoColorScheme.COLOR_DEF_CURSOR}: ${colorScheme.cursorColor}\n") 52 | colorScheme.color.entries.forEach { 53 | builder.append(" ${NeoColorScheme.COLOR_PREFIX}${it.key}: ${it.value}\n") 54 | } 55 | 56 | builder.append(" }\n") 57 | } 58 | } 59 | 60 | class NeoProfileGenerator(parameter: CodeGenParameter) : CodeGenerator(parameter) { 61 | override fun getGeneratorName(): String { 62 | return "NeoProfile-Generator" 63 | } 64 | 65 | override fun generateCode(codeGenObject: CodeGenObject): String { 66 | return "" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/codegen/interfaces.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.codegen 2 | 3 | abstract class CodeGenerator(parameter: CodeGenParameter) { 4 | abstract fun getGeneratorName(): String 5 | abstract fun generateCode(codeGenObject: CodeGenObject): String 6 | } 7 | 8 | interface CodeGenObject { 9 | fun getCodeGenerator(parameter: CodeGenParameter): CodeGenerator 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/completion/comp.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.completion 2 | 3 | import io.neoterm.component.NeoComponent 4 | 5 | class CompletionComponent : NeoComponent { 6 | override fun onServiceInit() { 7 | CompletionManager.registerProvider(FileCompletionProvider()) 8 | CompletionManager.registerProvider(ProgramCompletionProvider()) 9 | } 10 | 11 | override fun onServiceDestroy() { 12 | } 13 | 14 | override fun onServiceObtained() { 15 | } 16 | } 17 | 18 | object CompletionManager { 19 | private val candidateProviders = mutableMapOf() 20 | 21 | fun registerProvider(provider: ICandidateProvider) { 22 | this.candidateProviders[provider.providerName] = provider 23 | } 24 | 25 | fun unregisterProvider(providerName: String) { 26 | this.candidateProviders.remove(providerName) 27 | } 28 | 29 | fun unregisterProvider(provider: ICandidateProvider) { 30 | unregisterProvider(provider.providerName) 31 | } 32 | 33 | fun getProvider(providerName: String): ICandidateProvider? { 34 | return candidateProviders[providerName] 35 | } 36 | 37 | fun tryCompleteFor(text: String): CompletionResult { 38 | val detector = detectProviders(text) 39 | val provider = detector.detectBest() 40 | 41 | val candidates = provider?.provideCandidates(text) ?: listOf() 42 | return CompletionResult(candidates, detector) 43 | } 44 | 45 | private fun detectProviders(text: String): ProviderDetector { 46 | return ProviderDetector(candidateProviders.values 47 | .takeWhile { it.canComplete(text) }) 48 | } 49 | } 50 | 51 | class ProviderDetector(val providers: List) : MarkScoreListener { 52 | private var detectedProvider: ICandidateProvider? = null 53 | 54 | override fun onMarkScore(score: Int) { 55 | // TODO: Save provider score 56 | } 57 | 58 | fun detectBest(): ICandidateProvider? { 59 | // TODO: detect best 60 | detectedProvider = if (providers.isEmpty()) 61 | null 62 | else 63 | providers[0] 64 | 65 | return detectedProvider 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/completion/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.completion 2 | 3 | class CompletionCandidate(val completeString: String) { 4 | var displayName: String = completeString 5 | var description: String? = null 6 | } 7 | 8 | class CompletionResult(val candidates: List, var scoreMarker: MarkScoreListener) { 9 | fun markScore(score: Int) { 10 | scoreMarker.onMarkScore(score) 11 | } 12 | 13 | fun hasResult(): Boolean { 14 | return candidates.isNotEmpty() 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/completion/listeners.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.completion 2 | 3 | interface MarkScoreListener { 4 | fun onMarkScore(score: Int) 5 | } 6 | 7 | interface OnAutoCompleteListener { 8 | fun onCompletionRequired(newText: String?) 9 | fun onKeyCode(keyCode: Int, keyMod: Int) 10 | fun onCleanUp() 11 | fun onFinishCompletion(): Boolean 12 | } 13 | 14 | interface OnCandidateSelectedListener { 15 | fun onCandidateSelected(candidate: CompletionCandidate) 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/completion/providers.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.completion 2 | 3 | import java.io.File 4 | 5 | interface ICandidateProvider { 6 | val providerName: String 7 | fun provideCandidates(text: String): List 8 | fun canComplete(text: String): Boolean 9 | } 10 | 11 | open class FileCompletionProvider : ICandidateProvider { 12 | override val providerName: String 13 | get() = "NeoTermProvider.FileCompletionProvider" 14 | 15 | override fun provideCandidates(text: String): List { 16 | var file = File(text) 17 | var filter: ((File) -> Boolean)? = null 18 | 19 | if (!file.isDirectory) { 20 | val partName = file.name 21 | file = file.parentFile 22 | filter = { pathname -> pathname.name.startsWith(partName) } 23 | } 24 | 25 | return generateCandidateList(file, filter) 26 | } 27 | 28 | override fun canComplete(text: String): Boolean { 29 | return text.startsWith(File.separatorChar) || text.startsWith("\\./") 30 | } 31 | 32 | private fun listDirectory(path: File, filter: ((File) -> Boolean)?): Array { 33 | return if (filter != null) path.listFiles(filter) else path.listFiles() 34 | } 35 | 36 | private fun generateCandidateList(file: File, filter: ((File) -> Boolean)?) = 37 | if (file.canRead()) listDirectory(file, filter).map { 38 | val candidate = CompletionCandidate(it.name) 39 | candidate.description = generateDesc(it) 40 | candidate.displayName = generateDisplayName(it) 41 | candidate 42 | }.toList() 43 | else listOf() 44 | 45 | open fun generateDisplayName(file: File): String { 46 | return if (file.isDirectory) "${file.name}/" else file.name 47 | } 48 | 49 | open fun generateDesc(file: File): String? { 50 | return null 51 | } 52 | } 53 | 54 | class ProgramCompletionProvider : FileCompletionProvider() { 55 | override val providerName: String 56 | get() = "NeoTermProvider.ProgramCompletionProvider" 57 | 58 | 59 | override fun generateDesc(file: File): String? { 60 | return if (file.canExecute()) "" else super.generateDesc(file) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/config/defaults.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.config 2 | 3 | import android.annotation.SuppressLint 4 | 5 | object DefaultValues { 6 | const val fontSize = 30 7 | 8 | const val enableBell = false 9 | const val enableVibrate = false 10 | const val enableExecveWrapper = true 11 | const val enableAutoCompletion = false 12 | const val enableFullScreen = false 13 | const val enableAutoHideToolbar = false 14 | const val enableSwitchNextTab = false 15 | const val enableExtraKeys = true 16 | const val enableExplicitExtraKeysWeight = false 17 | const val enableBackButtonBeMappedToEscape = false 18 | const val enableSpecialVolumeKeys = false 19 | const val enableWordBasedIme = false 20 | 21 | const val loginShell = "bash" 22 | const val initialCommand = "" 23 | const val defaultFont = "SourceCodePro" 24 | } 25 | 26 | object NeoTermPath { 27 | @SuppressLint("SdCardPath") 28 | const val ROOT_PATH = "/data/data/io.neoterm/files" 29 | const val USR_PATH = "$ROOT_PATH/usr" 30 | const val HOME_PATH = "$ROOT_PATH/home" 31 | const val APT_BIN_PATH = "$USR_PATH/bin/apt" 32 | const val LIB_PATH = "$USR_PATH/lib" 33 | 34 | const val CUSTOM_PATH = "$HOME_PATH/.neoterm" 35 | const val NEOTERM_LOGIN_SHELL_PATH = "$CUSTOM_PATH/shell" 36 | const val EKS_PATH = "$CUSTOM_PATH/eks" 37 | const val EKS_DEFAULT_FILE = "$EKS_PATH/default.nl" 38 | const val FONT_PATH = "$CUSTOM_PATH/font" 39 | const val COLORS_PATH = "$CUSTOM_PATH/color" 40 | const val USER_SCRIPT_PATH = "$CUSTOM_PATH/script" 41 | const val PROFILE_PATH = "$CUSTOM_PATH/profile" 42 | 43 | const val SOURCE_FILE = "$USR_PATH/etc/apt/sources.list" 44 | const val PACKAGE_LIST_DIR = "$USR_PATH/var/lib/apt/lists" 45 | 46 | private const val SOURCE = "https://raw.githubusercontent.com/NeoTerm/NeoTerm-repo/main" 47 | 48 | val DEFAULT_MAIN_PACKAGE_SOURCE: String 49 | 50 | init { 51 | DEFAULT_MAIN_PACKAGE_SOURCE = SOURCE 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component 2 | 3 | import io.neolang.frontend.ConfigVisitor 4 | import io.neoterm.component.config.ConfigureComponent 5 | import io.neoterm.utils.NLog 6 | import java.io.File 7 | import java.io.FileFilter 8 | 9 | interface ConfigFileBasedObject { 10 | @Throws(RuntimeException::class) 11 | fun onConfigLoaded(configVisitor: ConfigVisitor) 12 | } 13 | 14 | abstract class ConfigFileBasedComponent(protected val baseDir: String) : NeoComponent { 15 | companion object { 16 | private val TAG = ConfigFileBasedComponent::class.java.simpleName 17 | 18 | val NEOLANG_FILTER = FileFilter { 19 | it.extension == "nl" 20 | } 21 | } 22 | 23 | open val checkComponentFileWhenObtained = false 24 | 25 | override fun onServiceInit() { 26 | val baseDirFile = File(this.baseDir) 27 | if (!baseDirFile.exists()) { 28 | if (!baseDirFile.mkdirs()) { 29 | throw RuntimeException("Cannot create component config directory: ${baseDirFile.absolutePath}") 30 | } 31 | } 32 | onCheckComponentFiles() 33 | } 34 | 35 | override fun onServiceDestroy() { 36 | } 37 | 38 | override fun onServiceObtained() { 39 | if (checkComponentFileWhenObtained) { 40 | onCheckComponentFiles() 41 | } 42 | } 43 | 44 | fun loadConfigure(file: File): T? { 45 | return try { 46 | val loaderService = ComponentManager.getComponent() 47 | val configure = loaderService.newLoader(file).loadConfigure() 48 | ?: throw RuntimeException("Parse configuration failed.") 49 | 50 | val configVisitor = configure.getVisitor() 51 | val componentObject = onCreateComponentObject(configVisitor) 52 | componentObject.onConfigLoaded(configVisitor) 53 | componentObject 54 | } catch (e: RuntimeException) { 55 | NLog.e(TAG, "Failed to load config: ${file.absolutePath}: ${e.localizedMessage}") 56 | null 57 | } 58 | } 59 | 60 | abstract fun onCheckComponentFiles() 61 | 62 | abstract fun onCreateComponentObject(configVisitor: ConfigVisitor): T 63 | } 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/extrakey/comp.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.extrakey 2 | 3 | import android.content.Context 4 | import io.neolang.frontend.ConfigVisitor 5 | import io.neoterm.App 6 | import io.neoterm.component.ConfigFileBasedComponent 7 | import io.neoterm.component.config.NeoTermPath 8 | import io.neoterm.frontend.session.view.extrakey.ExtraKeysView 9 | import io.neoterm.utils.NLog 10 | import io.neoterm.utils.extractAssetsDir 11 | import java.io.File 12 | 13 | class ExtraKeyComponent : ConfigFileBasedComponent(NeoTermPath.EKS_PATH) { 14 | override val checkComponentFileWhenObtained 15 | get() = true 16 | 17 | private val extraKeys: MutableMap = mutableMapOf() 18 | 19 | override fun onCheckComponentFiles() { 20 | val defaultFile = File(NeoTermPath.EKS_DEFAULT_FILE) 21 | if (!defaultFile.exists()) { 22 | extractDefaultConfig(App.get()) 23 | } 24 | reloadExtraKeyConfig() 25 | } 26 | 27 | override fun onCreateComponentObject(configVisitor: ConfigVisitor): NeoExtraKey { 28 | return NeoExtraKey() 29 | } 30 | 31 | fun showShortcutKeys(program: String, extraKeysView: ExtraKeysView?) { 32 | if (extraKeysView == null) { 33 | return 34 | } 35 | 36 | val extraKey = extraKeys[program] 37 | if (extraKey != null) { 38 | extraKey.applyExtraKeys(extraKeysView) 39 | return 40 | } 41 | 42 | extraKeysView.loadDefaultUserKeys() 43 | } 44 | 45 | private fun registerShortcutKeys(extraKey: NeoExtraKey) = 46 | extraKey.programNames.forEach { 47 | extraKeys[it] = extraKey 48 | } 49 | 50 | private fun extractDefaultConfig(context: Context) { 51 | try { 52 | context.extractAssetsDir("eks", baseDir) 53 | } catch (e: Exception) { 54 | NLog.e("ExtraKey", "Failed to extract configure: ${e.localizedMessage}") 55 | } 56 | } 57 | 58 | private fun reloadExtraKeyConfig() { 59 | extraKeys.clear() 60 | File(baseDir) 61 | .listFiles(NEOLANG_FILTER) 62 | .filter { it.absolutePath != NeoTermPath.EKS_DEFAULT_FILE } 63 | .mapNotNull { this.loadConfigure(it) } 64 | .forEach { 65 | registerShortcutKeys(it) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/font/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.font 2 | 3 | import android.graphics.Typeface 4 | import io.neoterm.frontend.session.view.TerminalView 5 | import io.neoterm.frontend.session.view.extrakey.ExtraKeysView 6 | import java.io.File 7 | 8 | class NeoFont { 9 | private var fontFile: File? = null 10 | private var typeface: Typeface? = null 11 | 12 | constructor(fontFile: File) { 13 | this.fontFile = fontFile 14 | } 15 | 16 | constructor(typeface: Typeface) { 17 | this.typeface = typeface 18 | } 19 | 20 | internal fun applyFont(terminalView: TerminalView?, extraKeysView: ExtraKeysView?) { 21 | val typeface = getTypeFace() 22 | terminalView?.setTypeface(typeface) 23 | extraKeysView?.setTypeface(typeface) 24 | } 25 | 26 | private fun getTypeFace(): Typeface? { 27 | if (typeface == null && fontFile == null) { 28 | return null 29 | } 30 | 31 | if (typeface == null) { 32 | typeface = Typeface.createFromFile(fontFile) 33 | } 34 | return typeface 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/pm/Source.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.pm; 2 | 3 | import io.neoterm.framework.database.annotation.ID; 4 | import io.neoterm.framework.database.annotation.Table; 5 | 6 | /** 7 | * @author kiva 8 | */ 9 | @Table 10 | public class Source { 11 | @ID(autoIncrement = true) 12 | private int id; 13 | 14 | public String url; 15 | 16 | public String repo; 17 | 18 | public boolean enabled; 19 | 20 | public Source() { 21 | // for Database 22 | } 23 | 24 | public Source(String url, String repo, boolean enabled) { 25 | this.url = url; 26 | this.repo = repo; 27 | this.enabled = enabled; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/pm/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.pm 2 | 3 | enum class Architecture { 4 | ALL, ARM, AARCH64, X86, X86_64; 5 | 6 | companion object { 7 | fun parse(arch: String): Architecture { 8 | return when (arch) { 9 | "arm" -> ARM 10 | "aarch64" -> AARCH64 11 | "x86" -> X86 12 | "x86_64" -> X86_64 13 | else -> ALL 14 | } 15 | } 16 | } 17 | } 18 | 19 | class NeoPackageInfo { 20 | var packageName: String? = null 21 | var isEssential: Boolean = false 22 | var version: String? = null 23 | var architecture: Architecture = Architecture.ALL 24 | var maintainer: String? = null 25 | var installedSizeInBytes: Long = 0L 26 | var fileName: String? = null 27 | var dependenciesString: String? = null 28 | var dependencies: Array? = null 29 | var sizeInBytes: Long = 0L 30 | var md5: String? = null 31 | var sha1: String? = null 32 | var sha256: String? = null 33 | var homePage: String? = null 34 | var description: String? = null 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/profile/comp.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.profile 2 | 3 | import io.neolang.frontend.ConfigVisitor 4 | import io.neoterm.component.ConfigFileBasedComponent 5 | import io.neoterm.component.config.NeoTermPath 6 | import io.neoterm.utils.NLog 7 | import java.io.File 8 | 9 | class ProfileComponent : ConfigFileBasedComponent(NeoTermPath.PROFILE_PATH) { 10 | override val checkComponentFileWhenObtained 11 | get() = true 12 | 13 | private val profileRegistry = mutableMapOf>() 14 | private val profileList = mutableMapOf>() 15 | 16 | override fun onCheckComponentFiles() = reloadProfiles() 17 | 18 | override fun onCreateComponentObject(configVisitor: ConfigVisitor): NeoProfile { 19 | val rootContext = configVisitor.getRootContext() 20 | 21 | val profileClass = rootContext.children 22 | .mapNotNull { 23 | profileRegistry[it.contextName] 24 | } 25 | .singleOrNull() 26 | 27 | if (profileClass != null) { 28 | NLog.e("ProfileComponent", "Loaded profile: " + profileClass.name) 29 | return profileClass.newInstance() 30 | } 31 | 32 | throw IllegalArgumentException("No proper profile registry found") 33 | } 34 | 35 | fun getProfiles(metaName: String): List = profileList[metaName] ?: listOf() 36 | 37 | fun reloadProfiles() { 38 | profileList.clear() 39 | File(baseDir) 40 | .listFiles(NEOLANG_FILTER) 41 | .mapNotNull { 42 | this.loadConfigure(it) 43 | } 44 | .forEach { 45 | val list = profileList[it.profileMetaName] 46 | if (list != null) { 47 | list.add(it) 48 | } else { 49 | val newList = mutableListOf(it) 50 | profileList.put(it.profileMetaName, newList) 51 | } 52 | } 53 | } 54 | 55 | fun registerProfile(metaName: String, prototype: Class) { 56 | profileRegistry[metaName] = prototype 57 | reloadProfiles() 58 | } 59 | 60 | fun unregisterProfile(metaName: String) { 61 | profileRegistry.remove(metaName) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/profile/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.profile 2 | 3 | import io.neolang.frontend.ConfigVisitor 4 | import io.neoterm.component.ComponentManager 5 | import io.neoterm.component.ConfigFileBasedObject 6 | import io.neoterm.component.codegen.CodeGenObject 7 | import io.neoterm.component.codegen.CodeGenParameter 8 | import io.neoterm.component.codegen.CodeGenerator 9 | import io.neoterm.component.codegen.NeoProfileGenerator 10 | import io.neoterm.component.config.ConfigureComponent 11 | import io.neoterm.component.config.NeoConfigureFile 12 | import io.neoterm.utils.NLog 13 | import org.jetbrains.annotations.TestOnly 14 | import java.io.File 15 | 16 | abstract class NeoProfile : CodeGenObject, ConfigFileBasedObject { 17 | companion object { 18 | private const val PROFILE_NAME = "name" 19 | } 20 | 21 | abstract val profileMetaName: String 22 | private val profileMetaPath 23 | get() = arrayOf(profileMetaName) 24 | 25 | var profileName = "Unknown Profile" 26 | 27 | override fun onConfigLoaded(configVisitor: ConfigVisitor) { 28 | profileName = configVisitor.getProfileString(PROFILE_NAME, profileName) 29 | } 30 | 31 | override fun getCodeGenerator(parameter: CodeGenParameter): CodeGenerator { 32 | return NeoProfileGenerator(parameter) 33 | } 34 | 35 | @TestOnly 36 | fun testLoadConfigure(file: File): Boolean { 37 | val loaderService = ComponentManager.getComponent() 38 | 39 | val configure: NeoConfigureFile? 40 | try { 41 | configure = loaderService.newLoader(file).loadConfigure() 42 | if (configure == null) { 43 | throw RuntimeException("Parse configuration failed.") 44 | } 45 | } catch (e: Exception) { 46 | NLog.e("Profile", "Failed to load profile: ${file.absolutePath}: ${e.localizedMessage}") 47 | return false 48 | } 49 | 50 | val visitor = configure.getVisitor() 51 | onConfigLoaded(visitor) 52 | return true 53 | } 54 | 55 | protected fun ConfigVisitor.getProfileString(key: String, fallback: String): String { 56 | return getProfileString(key) ?: fallback 57 | } 58 | 59 | protected fun ConfigVisitor.getProfileBoolean(key: String, fallback: Boolean): Boolean { 60 | return getProfileBoolean(key) ?: fallback 61 | } 62 | 63 | protected fun ConfigVisitor.getProfileString(key: String): String? { 64 | return this.getStringValue(profileMetaPath, key) 65 | } 66 | 67 | protected fun ConfigVisitor.getProfileBoolean(key: String): Boolean? { 68 | return this.getBooleanValue(profileMetaPath, key) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/component/userscript/comp.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.component.userscript 2 | 3 | import android.content.Context 4 | import android.system.Os 5 | import io.neoterm.App 6 | import io.neoterm.component.NeoComponent 7 | import io.neoterm.component.config.NeoTermPath 8 | import io.neoterm.utils.NLog 9 | import io.neoterm.utils.extractAssetsDir 10 | import java.io.File 11 | 12 | class UserScript(val scriptFile: File) 13 | 14 | class UserScriptComponent : NeoComponent { 15 | var userScripts = listOf() 16 | private val scriptDir = File(NeoTermPath.USER_SCRIPT_PATH) 17 | 18 | override fun onServiceInit() = checkForFiles() 19 | 20 | override fun onServiceDestroy() { 21 | } 22 | 23 | override fun onServiceObtained() = checkForFiles() 24 | 25 | private fun extractDefaultScript(context: Context) = kotlin.runCatching { 26 | context.extractAssetsDir("scripts", NeoTermPath.USER_SCRIPT_PATH) 27 | scriptDir.listFiles().forEach { 28 | Os.chmod(it.absolutePath, 448 /*Dec of 0700*/) 29 | } 30 | }.onFailure { 31 | NLog.e("UserScript", "Failed to extract default user scripts: ${it.localizedMessage}") 32 | } 33 | 34 | private fun checkForFiles() { 35 | extractDefaultScript(App.get()) 36 | reloadScripts() 37 | } 38 | 39 | private fun reloadScripts() { 40 | userScripts = scriptDir.listFiles() 41 | .takeWhile { it.canExecute() } 42 | .map { UserScript(it) } 43 | .toList() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/DatabaseDataType.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database; 2 | 3 | /** 4 | * @author kiva 5 | */ 6 | public enum DatabaseDataType { 7 | /** 8 | * int类型 9 | */ 10 | INTEGER, 11 | /** 12 | * String类型 13 | */ 14 | TEXT, 15 | /** 16 | * float类型 17 | */ 18 | FLOAT, 19 | /** 20 | * long类型 21 | */ 22 | BIGINT, 23 | /** 24 | * double类型 25 | */ 26 | DOUBLE; 27 | 28 | boolean nullable = true; 29 | 30 | /** 31 | * 数据类型是否允许为null 32 | */ 33 | public DatabaseDataType nullable(boolean nullable) { 34 | this.nullable = nullable; 35 | return this; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/OnDatabaseUpgradedListener.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database; 2 | 3 | import android.database.sqlite.SQLiteDatabase; 4 | 5 | /** 6 | * @author kiva 7 | */ 8 | public interface OnDatabaseUpgradedListener { 9 | /** 10 | * @param db 数据库 11 | * @param oldVersion 旧版本 12 | * @param newVersion 新版本 13 | */ 14 | void onDatabaseUpgraded(SQLiteDatabase db, int oldVersion, int newVersion); 15 | } -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/annotation/ID.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface ID { 14 | /** 15 | * 只对Integer类型的ID字段有效 16 | * 17 | * @return 是否为自增长 18 | */ 19 | boolean autoIncrement() default false; 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/annotation/Ignore.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target({ElementType.FIELD}) 13 | public @interface Ignore { 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/annotation/NotNull.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | @Target(ElementType.FIELD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface NotNull { 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/annotation/Table.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author kiva 10 | */ 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Table { 14 | /** 15 | * @return 表名 16 | */ 17 | String name() default ""; 18 | 19 | /** 20 | * @return 在表创建后需要回调的方法 21 | */ 22 | String afterTableCreate() default ""; 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/database/bean/TableInfo.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.database.bean; 2 | 3 | 4 | import io.neoterm.framework.database.DatabaseDataType; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author kiva 12 | */ 13 | public class TableInfo { 14 | 15 | /** 16 | * 是否包含ID 17 | */ 18 | public boolean containID; 19 | /** 20 | * 主键字段 21 | */ 22 | public Field primaryField; 23 | 24 | /** 25 | * 表名 26 | */ 27 | public String tableName; 28 | 29 | /** 30 | * 字段表 31 | */ 32 | public Map fieldToDataTypeMap; 33 | 34 | /** 35 | * 创建table的语句 36 | */ 37 | public String createTableStatement; 38 | 39 | /** 40 | * 是否已经创建 41 | */ 42 | public boolean isCreate = false; 43 | 44 | public Method afterTableCreateMethod; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/reflection/NullPointer.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.reflection; 2 | 3 | /** 4 | * class representing null pointer. 5 | * 6 | * @author kiva 7 | */ 8 | public class NullPointer { 9 | } -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/framework/reflection/ReflectionException.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.framework.reflection; 2 | 3 | /** 4 | * @author kiva 5 | */ 6 | public class ReflectionException extends RuntimeException { 7 | ReflectionException(Throwable cause) { 8 | super(cause); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/frontend/completion/MaxHeightView.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.frontend.completion 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.widget.LinearLayout 7 | 8 | class MaxHeightView : LinearLayout { 9 | var maxHeight = -1 10 | 11 | constructor(context: Context) : super(context) 12 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) 13 | constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) 14 | 15 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 16 | var finalHeightMeasureSpec = heightMeasureSpec 17 | 18 | if (maxHeight > 0) { 19 | val heightMode = View.MeasureSpec.getMode(heightMeasureSpec) 20 | var heightSize = View.MeasureSpec.getSize(heightMeasureSpec) 21 | 22 | if (heightMode == View.MeasureSpec.EXACTLY) { 23 | heightSize = if (heightSize <= maxHeight) 24 | heightSize 25 | else 26 | maxHeight 27 | } 28 | 29 | if (heightMode == View.MeasureSpec.UNSPECIFIED) { 30 | heightSize = if (heightSize <= maxHeight) 31 | heightSize 32 | else 33 | maxHeight 34 | } 35 | if (heightMode == View.MeasureSpec.AT_MOST) { 36 | heightSize = if (heightSize <= maxHeight) 37 | heightSize 38 | else 39 | maxHeight 40 | } 41 | finalHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec( 42 | heightSize, 43 | heightMode 44 | ) 45 | } 46 | 47 | super.onMeasure(widthMeasureSpec, finalHeightMeasureSpec) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/frontend/session/terminal/data.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.frontend.session.terminal 2 | 3 | import io.neoterm.backend.TerminalSession 4 | import io.neoterm.component.completion.OnAutoCompleteListener 5 | import io.neoterm.component.session.ShellProfile 6 | import io.neoterm.component.session.ShellTermSession 7 | import io.neoterm.frontend.session.view.TerminalView 8 | import io.neoterm.frontend.session.view.extrakey.ExtraKeysView 9 | 10 | class TermSessionData { 11 | var termSession: TerminalSession? = null 12 | var sessionCallback: TermSessionCallback? = null 13 | var viewClient: TermViewClient? = null 14 | var onAutoCompleteListener: OnAutoCompleteListener? = null 15 | 16 | var termUI: TermUiPresenter? = null 17 | var termView: TerminalView? = null 18 | var extraKeysView: ExtraKeysView? = null 19 | 20 | var profile: ShellProfile? = null 21 | 22 | fun cleanup() { 23 | onAutoCompleteListener?.onCleanUp() 24 | onAutoCompleteListener = null 25 | 26 | sessionCallback?.termSessionData = null 27 | viewClient?.termSessionData = null 28 | 29 | termUI = null 30 | termView = null 31 | extraKeysView = null 32 | termSession = null 33 | 34 | profile = null 35 | } 36 | 37 | fun initializeSessionWith( 38 | session: TerminalSession, 39 | sessionCallback: TermSessionCallback?, 40 | viewClient: TermViewClient? 41 | ) { 42 | this.termSession = session 43 | this.sessionCallback = sessionCallback 44 | this.viewClient = viewClient 45 | this.sessionCallback?.termSessionData = this 46 | this.viewClient?.termSessionData = this 47 | 48 | if (session is ShellTermSession) { 49 | profile = session.shellProfile 50 | } 51 | } 52 | 53 | fun initializeViewWith(termUI: TermUiPresenter?, termView: TerminalView?, eks: ExtraKeysView?) { 54 | this.termUI = termUI 55 | this.termView = termView 56 | this.extraKeysView = eks 57 | } 58 | } 59 | 60 | interface TermUiPresenter { 61 | fun requireClose() 62 | fun requireToggleFullScreen() 63 | fun requirePaste() 64 | fun requireUpdateTitle(title: String?) 65 | fun requireOnSessionFinished() 66 | fun requireHideIme() 67 | fun requireFinishAutoCompletion(): Boolean 68 | fun requireCreateNew() 69 | fun requireSwitchToPrevious() 70 | fun requireSwitchToNext() 71 | fun requireSwitchTo(index: Int) 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/frontend/session/terminal/events.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.frontend.session.terminal 2 | 3 | import io.neoterm.ui.term.TermTab 4 | 5 | class CreateNewSessionEvent 6 | class SwitchIndexedSessionEvent(val index: Int) 7 | class SwitchSessionEvent(val toNext: Boolean) 8 | class TabCloseEvent(val termTab: TermTab) 9 | class TitleChangedEvent(val title: String) 10 | class ToggleFullScreenEvent 11 | class ToggleImeEvent 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/frontend/session/view/TerminalViewClient.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.frontend.session.view; 2 | 3 | import android.view.KeyEvent; 4 | import android.view.MotionEvent; 5 | import android.view.ScaleGestureDetector; 6 | import io.neoterm.backend.TerminalSession; 7 | 8 | /** 9 | * Input and scale listener which may be set on a {@link TerminalView} through 10 | * {@link TerminalView#setTerminalViewClient(TerminalViewClient)}. 11 | *

12 | */ 13 | public interface TerminalViewClient { 14 | /** 15 | * Callback function on scale events according to {@link ScaleGestureDetector#getScaleFactor()}. 16 | */ 17 | float onScale(float scale); 18 | 19 | /** 20 | * On a single tap on the terminal if terminal mouse reporting not enabled. 21 | */ 22 | void onSingleTapUp(MotionEvent e); 23 | 24 | boolean shouldBackButtonBeMappedToEscape(); 25 | 26 | void copyModeChanged(boolean copyMode); 27 | 28 | boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session); 29 | 30 | boolean onKeyUp(int keyCode, KeyEvent e); 31 | 32 | boolean readControlKey(); 33 | 34 | boolean readAltKey(); 35 | 36 | boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session); 37 | 38 | boolean onLongPress(MotionEvent event); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/frontend/session/view/extrakey/CombinedSequence.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.frontend.session.view.extrakey 2 | 3 | /** 4 | * 5 | * C 6 | * :q 7 | * 8 | * @author kiva 9 | */ 10 | class CombinedSequence private constructor() { 11 | val keys = mutableListOf() 12 | 13 | companion object { 14 | fun solveString(keyString: String): CombinedSequence { 15 | val seq = CombinedSequence() 16 | keyString.split(' ').forEach { 17 | val key = if (it.startsWith('<') && it.endsWith('>')) { 18 | // is a sequence 19 | it.substring(1, it.length - 1) 20 | } else { 21 | // is a normal string 22 | it 23 | } 24 | seq.keys.add(key) 25 | } 26 | return seq 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/setup/SourceConnection.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.setup; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * @author kiva 8 | */ 9 | public interface SourceConnection { 10 | InputStream getInputStream() throws IOException; 11 | int getSize(); 12 | void close(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/setup/setup.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.setup 2 | 3 | import android.app.ProgressDialog 4 | import android.content.Context 5 | import android.os.Build 6 | import androidx.appcompat.app.AlertDialog 7 | import androidx.appcompat.app.AppCompatActivity 8 | import io.neoterm.App 9 | import io.neoterm.R 10 | import io.neoterm.component.config.NeoTermPath 11 | import java.io.File 12 | import java.util.* 13 | 14 | /** 15 | * @author kiva 16 | */ 17 | interface ResultListener { 18 | fun onResult(error: Exception?) 19 | } 20 | 21 | /** 22 | * @author kiva 23 | */ 24 | object SetupHelper { 25 | fun needSetup(): Boolean { 26 | val PREFIX_FILE = File(NeoTermPath.USR_PATH) 27 | return !PREFIX_FILE.isDirectory 28 | } 29 | 30 | fun setup( 31 | activity: AppCompatActivity, connection: SourceConnection, 32 | resultListener: ResultListener 33 | ) { 34 | if (!needSetup()) { 35 | resultListener.onResult(null) 36 | return 37 | } 38 | 39 | val prefixFile = File(NeoTermPath.USR_PATH) 40 | 41 | val progress = makeProgressDialog(activity) 42 | progress.max = 100 43 | progress.show() 44 | 45 | SetupThread(activity, connection, prefixFile, resultListener, progress).start() 46 | } 47 | 48 | private fun makeProgressDialog(context: Context): ProgressDialog { 49 | return makeProgressDialog(context, context.getString(R.string.installer_message)) 50 | } 51 | 52 | fun makeProgressDialog(context: Context, message: String): ProgressDialog { 53 | val dialog = ProgressDialog(context) 54 | dialog.setMessage(message) 55 | dialog.isIndeterminate = false 56 | dialog.setCancelable(false) 57 | dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL) 58 | return dialog 59 | } 60 | 61 | fun makeErrorDialog(context: Context, messageId: Int): AlertDialog { 62 | return makeErrorDialog(context, context.getString(messageId)) 63 | } 64 | 65 | fun makeErrorDialog(context: Context, message: String): AlertDialog { 66 | return AlertDialog.Builder(context) 67 | .setTitle(R.string.error) 68 | .setMessage(message) 69 | .setPositiveButton(android.R.string.yes, null) 70 | .setNeutralButton(R.string.show_help) { _, _ -> App.get().openHelpLink() } 71 | .create() 72 | } 73 | 74 | fun determineArchName(): String { 75 | for (androidArch in Build.SUPPORTED_ABIS) { 76 | when (androidArch) { 77 | "arm64-v8a" -> return "aarch64" 78 | "armeabi-v7a" -> return "arm" 79 | "x86_64" -> return "x86_64" 80 | } 81 | } 82 | throw RuntimeException( 83 | "Unable to determine arch from Build.SUPPORTED_ABIS = " 84 | + Arrays.toString(Build.SUPPORTED_ABIS) 85 | ) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/customize/BaseCustomizeActivity.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.customize 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.MenuItem 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.appcompat.widget.Toolbar 7 | import io.neoterm.R 8 | import io.neoterm.backend.TerminalSession 9 | import io.neoterm.component.config.NeoTermPath 10 | import io.neoterm.component.session.ShellParameter 11 | import io.neoterm.frontend.session.terminal.BasicSessionCallback 12 | import io.neoterm.frontend.session.terminal.BasicViewClient 13 | import io.neoterm.frontend.session.view.TerminalView 14 | import io.neoterm.frontend.session.view.extrakey.ExtraKeysView 15 | import io.neoterm.utils.Terminals 16 | 17 | /** 18 | * @author kiva 19 | */ 20 | @SuppressLint("Registered") 21 | open class BaseCustomizeActivity : AppCompatActivity() { 22 | lateinit var terminalView: TerminalView 23 | lateinit var viewClient: BasicViewClient 24 | lateinit var sessionCallback: BasicSessionCallback 25 | lateinit var session: TerminalSession 26 | lateinit var extraKeysView: ExtraKeysView 27 | 28 | fun initCustomizationComponent(layoutId: Int) { 29 | setContentView(layoutId) 30 | 31 | val toolbar = findViewById(R.id.custom_toolbar) 32 | setSupportActionBar(toolbar) 33 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 34 | 35 | terminalView = findViewById(R.id.terminal_view) 36 | extraKeysView = findViewById(R.id.custom_extra_keys) 37 | viewClient = BasicViewClient(terminalView) 38 | sessionCallback = BasicSessionCallback(terminalView) 39 | Terminals.setupTerminalView(terminalView, viewClient) 40 | Terminals.setupExtraKeysView(extraKeysView) 41 | 42 | val script = resources.getStringArray(R.array.custom_preview_script_colors) 43 | val parameter = ShellParameter() 44 | .executablePath("${NeoTermPath.USR_PATH}/bin/echo") 45 | .arguments(arrayOf("echo", "-e", *script)) 46 | .callback(sessionCallback) 47 | .systemShell(false) 48 | 49 | session = Terminals.createSession(this, parameter) 50 | terminalView.attachSession(session) 51 | } 52 | 53 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 54 | when (item?.itemId) { 55 | android.R.id.home -> finish() 56 | } 57 | return super.onOptionsItemSelected(item) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/other/CrashActivity.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.other 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import android.widget.TextView 6 | import androidx.appcompat.app.AppCompatActivity 7 | import io.neoterm.R 8 | import java.io.ByteArrayOutputStream 9 | import java.io.PrintStream 10 | 11 | /** 12 | * @author kiva 13 | */ 14 | class CrashActivity : AppCompatActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.ui_crash) 18 | setSupportActionBar(findViewById(R.id.crash_toolbar)) 19 | 20 | (findViewById(R.id.crash_model)).text = getString(R.string.crash_model, collectModelInfo()) 21 | (findViewById(R.id.crash_app_version)).text = getString(R.string.crash_app, collectAppInfo()) 22 | (findViewById(R.id.crash_details)).text = collectExceptionInfo() 23 | } 24 | 25 | private fun collectExceptionInfo(): String { 26 | val extra = intent.getSerializableExtra("exception") 27 | if (extra != null && extra is Throwable) { 28 | val byteArrayOutput = ByteArrayOutputStream() 29 | val printStream = PrintStream(byteArrayOutput) 30 | (extra.cause ?: extra).printStackTrace(printStream) 31 | return byteArrayOutput.use { 32 | byteArrayOutput.toString("utf-8") 33 | } 34 | } 35 | return "are.you.kidding.me.NoExceptionFoundException: This is a bug, please contact developers!" 36 | } 37 | 38 | private fun collectAppInfo(): String { 39 | val pm = packageManager 40 | val info = pm.getPackageInfo(packageName, 0) 41 | return "${info.versionName} (${info.versionCode})" 42 | } 43 | 44 | private fun collectModelInfo(): String { 45 | return "${Build.MODEL} (Android ${Build.VERSION.RELEASE} ${determineArchName()})" 46 | } 47 | 48 | private fun determineArchName(): String { 49 | for (androidArch in Build.SUPPORTED_ABIS) { 50 | when (androidArch) { 51 | "arm64-v8a" -> return "aarch64" 52 | "armeabi-v7a" -> return "arm" 53 | "x86_64" -> return "x86_64" 54 | "x86" -> return "i686" 55 | } 56 | } 57 | return "Unknown Arch" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/pm/model.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.pm 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter 9 | import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView 10 | import io.neoterm.R 11 | import io.neoterm.component.pm.NeoPackageInfo 12 | import io.neoterm.utils.formatSizeInKB 13 | 14 | class PackageAdapter( 15 | context: Context, 16 | comparator: Comparator, 17 | private val listener: PackageAdapter.Listener 18 | ) : SortedListAdapter(context, PackageModel::class.java, comparator), 19 | FastScrollRecyclerView.SectionedAdapter { 20 | 21 | override fun getSectionName(position: Int): String { 22 | return getItem(position).packageInfo.packageName?.substring(0, 1) ?: "#" 23 | } 24 | 25 | interface Listener { 26 | fun onModelClicked(model: PackageModel) 27 | } 28 | 29 | override fun onCreateViewHolder( 30 | inflater: LayoutInflater, 31 | parent: ViewGroup, 32 | viewType: Int 33 | ): ViewHolder { 34 | val rootView = inflater.inflate(R.layout.item_package, parent, false) 35 | return PackageViewHolder(rootView, listener) 36 | } 37 | } 38 | 39 | class PackageViewHolder(private val rootView: View, private val listener: PackageAdapter.Listener) : 40 | SortedListAdapter.ViewHolder(rootView) { 41 | private val packageNameView: TextView = rootView.findViewById(R.id.package_item_name) 42 | private val packageDescView: TextView = rootView.findViewById(R.id.package_item_desc) 43 | 44 | override fun performBind(item: PackageModel) { 45 | rootView.setOnClickListener { listener.onModelClicked(item) } 46 | packageNameView.text = item.packageInfo.packageName 47 | packageDescView.text = item.packageInfo.description 48 | } 49 | } 50 | 51 | /** 52 | * @author kiva 53 | */ 54 | 55 | class PackageModel(val packageInfo: NeoPackageInfo) : SortedListAdapter.ViewModel { 56 | override fun isSameModelAs(t: T): Boolean { 57 | if (t is PackageModel) { 58 | return t.packageInfo.packageName == packageInfo.packageName 59 | } 60 | return false 61 | } 62 | 63 | override fun isContentTheSameAs(t: T): Boolean { 64 | return isSameModelAs(t) 65 | } 66 | 67 | fun getPackageDetails(context: Context): String { 68 | return context.getString( 69 | R.string.package_details, 70 | packageInfo.packageName, packageInfo.version, 71 | packageInfo.dependenciesString, 72 | packageInfo.installedSizeInBytes.formatSizeInKB(), 73 | packageInfo.description, packageInfo.homePage 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/settings/GeneralSettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.settings 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.appcompat.app.AlertDialog 6 | import io.neoterm.R 7 | import io.neoterm.component.config.NeoPreference 8 | import io.neoterm.utils.runApt 9 | 10 | /** 11 | * @author kiva 12 | */ 13 | class GeneralSettingsActivity : BasePreferenceActivity() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | supportActionBar?.title = getString(R.string.general_settings) 18 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 19 | addPreferencesFromResource(R.xml.setting_general) 20 | 21 | val currentShell = NeoPreference.getLoginShellName() 22 | findPreference(getString(R.string.key_general_shell)).setOnPreferenceChangeListener { _, value -> 23 | val shellName = value.toString() 24 | val newShell = NeoPreference.findLoginProgram(shellName) 25 | if (newShell == null) { 26 | requestInstallShell(shellName, currentShell) 27 | } else { 28 | postChangeShell(shellName) 29 | } 30 | return@setOnPreferenceChangeListener true 31 | } 32 | } 33 | 34 | private fun postChangeShell(shellName: String) = NeoPreference.setLoginShellName(shellName) 35 | 36 | private fun requestInstallShell(shellName: String, currentShell: String) { 37 | AlertDialog.Builder(this) 38 | .setTitle(getString(R.string.shell_not_found, shellName)) 39 | .setMessage(R.string.shell_not_found_message) 40 | .setPositiveButton(R.string.install) { _, _ -> 41 | runApt("install", "-y", shellName) { 42 | it.onSuccess { postChangeShell(shellName) } 43 | } 44 | } 45 | .setNegativeButton(android.R.string.no, null) 46 | .setOnDismissListener { postChangeShell(currentShell) } 47 | .show() 48 | } 49 | 50 | override fun onBuildHeaders(target: MutableList

?) { 51 | } 52 | 53 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 54 | when (item?.itemId) { 55 | android.R.id.home -> finish() 56 | } 57 | return super.onOptionsItemSelected(item) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/settings/SettingActivity.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.settings 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import io.neoterm.R 6 | 7 | /** 8 | * @author Lody 9 | */ 10 | class SettingActivity : BasePreferenceActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | supportActionBar?.title = getString(R.string.settings) 15 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 16 | addPreferencesFromResource(R.xml.settings_main) 17 | } 18 | 19 | override fun onBuildHeaders(target: MutableList
?) { 20 | } 21 | 22 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 23 | when (item?.itemId) { 24 | android.R.id.home -> 25 | finish() 26 | } 27 | return super.onOptionsItemSelected(item) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/settings/UISettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.settings 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import io.neoterm.R 6 | 7 | /** 8 | * @author kiva 9 | */ 10 | class UISettingsActivity : BasePreferenceActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | supportActionBar?.title = getString(R.string.ui_settings) 15 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 16 | addPreferencesFromResource(R.xml.settings_ui) 17 | } 18 | 19 | override fun onBuildHeaders(target: MutableList
?) { 20 | } 21 | 22 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 23 | when (item?.itemId) { 24 | android.R.id.home -> 25 | finish() 26 | } 27 | return super.onOptionsItemSelected(item) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/ui/term/SessionRemover.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.ui.term 2 | 3 | import io.neoterm.backend.TerminalSession 4 | import io.neoterm.component.session.XSession 5 | import io.neoterm.services.NeoTermService 6 | 7 | /** 8 | * @author kiva 9 | */ 10 | object SessionRemover { 11 | fun removeSession(termService: NeoTermService?, tab: TermTab) { 12 | tab.termData.termSession?.finishIfRunning() 13 | removeFinishedSession(termService, tab.termData.termSession) 14 | tab.cleanup() 15 | } 16 | 17 | fun removeXSession(termService: NeoTermService?, tab: XSessionTab?) { 18 | removeFinishedSession(termService, tab?.session) 19 | } 20 | 21 | private fun removeFinishedSession(termService: NeoTermService?, finishedSession: TerminalSession?) { 22 | if (termService == null || finishedSession == null) { 23 | return 24 | } 25 | 26 | termService.removeTermSession(finishedSession) 27 | } 28 | 29 | private fun removeFinishedSession(termService: NeoTermService?, finishedSession: XSession?) { 30 | if (termService == null || finishedSession == null) { 31 | return 32 | } 33 | 34 | termService.removeXSession(finishedSession) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/utils/CrashHandler.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.utils 2 | 3 | import android.content.Intent 4 | import io.neoterm.App 5 | import io.neoterm.ui.other.CrashActivity 6 | 7 | /** 8 | * @author kiva 9 | */ 10 | object CrashHandler : Thread.UncaughtExceptionHandler { 11 | private lateinit var defaultHandler: Thread.UncaughtExceptionHandler 12 | 13 | fun init() { 14 | defaultHandler = Thread.getDefaultUncaughtExceptionHandler() 15 | Thread.setDefaultUncaughtExceptionHandler(this) 16 | } 17 | 18 | override fun uncaughtException(t: Thread?, e: Throwable?) { 19 | e?.printStackTrace() 20 | 21 | val intent = Intent(App.get(), CrashActivity::class.java) 22 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 23 | intent.putExtra("exception", e) 24 | App.get().startActivity(intent) 25 | defaultHandler.uncaughtException(t, e) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/utils/NeoPermission.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.utils 2 | 3 | import android.Manifest 4 | import android.content.ActivityNotFoundException 5 | import android.content.DialogInterface 6 | import android.content.pm.PackageManager 7 | import androidx.appcompat.app.AlertDialog 8 | import androidx.appcompat.app.AppCompatActivity 9 | import androidx.core.app.ActivityCompat 10 | import androidx.core.content.ContextCompat 11 | 12 | /** 13 | * @author kiva 14 | */ 15 | object NeoPermission { 16 | const val REQUEST_APP_PERMISSION = 10086 17 | 18 | fun initAppPermission(context: AppCompatActivity, requestCode: Int) { 19 | if (ContextCompat.checkSelfPermission( 20 | context, 21 | Manifest.permission.READ_EXTERNAL_STORAGE 22 | ) 23 | != PackageManager.PERMISSION_GRANTED 24 | ) { 25 | 26 | if (ActivityCompat.shouldShowRequestPermissionRationale( 27 | context, 28 | Manifest.permission.READ_EXTERNAL_STORAGE 29 | ) 30 | ) { 31 | AlertDialog.Builder(context).setMessage("需要存储权限来访问存储设备上的文件") 32 | .setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> 33 | doRequestPermission(context, requestCode) 34 | }) 35 | .show() 36 | 37 | } else { 38 | doRequestPermission(context, requestCode) 39 | } 40 | } 41 | } 42 | 43 | private fun doRequestPermission(context: AppCompatActivity, requestCode: Int) { 44 | try { 45 | ActivityCompat.requestPermissions( 46 | context, 47 | arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 48 | requestCode 49 | ) 50 | } catch (ignore: ActivityNotFoundException) { 51 | // for MIUI, we ignore it. 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/utils/StringDistance.java: -------------------------------------------------------------------------------- 1 | package io.neoterm.utils; 2 | 3 | /** 4 | * @author kiva 5 | */ 6 | 7 | public class StringDistance { 8 | public static int distance(String source, String target) { 9 | char[] sources = source.toCharArray(); 10 | char[] targets = target.toCharArray(); 11 | int sourceLen = sources.length; 12 | int targetLen = targets.length; 13 | 14 | int[][] d = new int[sourceLen + 1][targetLen + 1]; 15 | for (int i = 0; i <= sourceLen; i++) { 16 | d[i][0] = i; 17 | } 18 | for (int i = 0; i <= targetLen; i++) { 19 | d[0][i] = i; 20 | } 21 | 22 | for (int i = 1; i <= sourceLen; i++) { 23 | for (int j = 1; j <= targetLen; j++) { 24 | if (sources[i - 1] == targets[j - 1]) { 25 | d[i][j] = d[i - 1][j - 1]; 26 | } else { 27 | int insert = d[i][j - 1] + 1; 28 | int delete = d[i - 1][j] + 1; 29 | int replace = d[i - 1][j - 1] + 1; 30 | d[i][j] = Math.min(Math.min(insert, delete), Math.min(delete, replace)); 31 | } 32 | } 33 | } 34 | return d[sourceLen][targetLen]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/io/neoterm/utils/Terminals.kt: -------------------------------------------------------------------------------- 1 | package io.neoterm.utils 2 | 3 | import android.content.Context 4 | import androidx.appcompat.app.AppCompatActivity 5 | import io.neoterm.backend.TerminalSession 6 | import io.neoterm.component.ComponentManager 7 | import io.neoterm.component.config.NeoPreference 8 | import io.neoterm.component.font.FontComponent 9 | import io.neoterm.component.session.SessionComponent 10 | import io.neoterm.component.session.ShellParameter 11 | import io.neoterm.component.session.XParameter 12 | import io.neoterm.component.session.XSession 13 | import io.neoterm.frontend.session.view.TerminalView 14 | import io.neoterm.frontend.session.view.TerminalViewClient 15 | import io.neoterm.frontend.session.view.extrakey.ExtraKeysView 16 | 17 | /** 18 | * @author kiva 19 | */ 20 | object Terminals { 21 | fun setupTerminalView(terminalView: TerminalView?, terminalViewClient: TerminalViewClient? = null) { 22 | terminalView?.textSize = NeoPreference.getFontSize(); 23 | 24 | val fontComponent = ComponentManager.getComponent() 25 | fontComponent.applyFont(terminalView, null, fontComponent.getCurrentFont()) 26 | 27 | if (terminalViewClient != null) { 28 | terminalView?.setTerminalViewClient(terminalViewClient) 29 | } 30 | } 31 | 32 | fun setupExtraKeysView(extraKeysView: ExtraKeysView?) { 33 | val fontComponent = ComponentManager.getComponent() 34 | val font = fontComponent.getCurrentFont() 35 | fontComponent.applyFont(null, extraKeysView, font) 36 | } 37 | 38 | fun createSession(context: Context, parameter: ShellParameter): TerminalSession { 39 | val sessionComponent = ComponentManager.getComponent() 40 | return sessionComponent.createSession(context, parameter) 41 | } 42 | 43 | fun createSession(activity: AppCompatActivity, parameter: XParameter): XSession { 44 | val sessionComponent = ComponentManager.getComponent() 45 | return sessionComponent.createSession(activity, parameter) 46 | } 47 | 48 | fun escapeString(s: String?): String { 49 | if (s == null) { 50 | return "" 51 | } 52 | 53 | val builder = StringBuilder() 54 | val specialChars = "\"\\$`!" 55 | builder.append('"') 56 | val length = s.length 57 | for (i in 0 until length) { 58 | val c = s[i] 59 | if (specialChars.indexOf(c) >= 0) { 60 | builder.append('\\') 61 | } 62 | builder.append(c) 63 | } 64 | builder.append('"') 65 | return builder.toString() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libnexec.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/jniLibs/arm64-v8a/libnexec.so -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_box_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_add_box_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_apps_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_apps_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_backup_restore_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_backup_restore_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_customization_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_customization_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_done.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_general_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_general_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_guide_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_guide_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_info_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_info_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_install_white_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_install_white_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_ui_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-hdpi/ic_ui_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_box_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-mdpi/ic_add_box_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-mdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_box_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xhdpi/ic_add_box_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xhdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_box_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xxhdpi/ic_add_box_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xxhdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_add_box_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable-xxxhdpi/ic_add_box_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable/banner.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_description.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_donate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_neoterm.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 15 | 39 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_new_session.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_tab_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable/ic_tab_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_terminal_running.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/plat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/drawable/plat_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_select_handle_left_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_select_handle_right_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_edit_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_edit_two_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 21 | 22 | 28 | 29 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 27 | 28 | 36 | 37 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_complete_candidate.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 22 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_package.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_pm_package_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/popup_auto_complete.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_command_shortcut.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 23 | 24 | 25 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_crash.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 22 | 23 | 29 | 30 | 36 | 37 | 45 | 46 | 49 | 50 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_pm.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 22 | 23 | 28 | 29 | 30 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_pm_single_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_term.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 15 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_term_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_term_embedded.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_user_script_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 19 | 20 | 24 | 25 | 26 | 27 | 33 | 34 | 38 | 39 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/ui_xorg.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_color_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 16 | 19 | 20 | 24 | 25 | 29 | 30 | 35 | 36 | 37 | 38 | 42 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_pm.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 16 | 17 | 21 | 22 | 26 | 27 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/about_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-hdpi/about_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-hdpi/ic_danger.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/about_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xhdpi/about_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xhdpi/ic_danger.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/about_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xxhdpi/about_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xxhdpi/ic_danger.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/about_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xxxhdpi/about_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_danger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xxxhdpi/ic_danger.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_neoterm_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_neoterm_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/bell.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/app/src/main/res/raw/bell.ogg -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #607D8B 4 | #455A64 5 | #42a5f5 6 | #ff14181c 7 | #fefefe 8 | #ced7db 9 | 10 | #7f000000 11 | #efefef 12 | #363636 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 180dp 3 | 16dp 4 | 16dp 5 | 8dp 6 | 32dp 7 | 32dp 8 | 32dp 9 | 32dp 10 | 72dp 11 | 36dp 12 | 256dp 13 | 48dp 14 | 36dp 15 | 36dp 16 | 24dp 17 | 4dp 18 | 48dp 19 | 48dp 20 | 21 | 16dp 22 | 4dp 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/preference_keys.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | neoterm_general_bell 4 | neoterm_general_vibrate 5 | neoterm_general_backspace_map_to_esc 6 | neoterm_general_shell 7 | neoterm_general_initial_command 8 | neoterm_general_auto_completion 9 | neoterm_general_volume_as_control 10 | neoterm_general_use_execve_wrapper 11 | neoterm_general_enable_word_based_ime 12 | 13 | neoterm_ui_fullscreen 14 | neoterm_ui_hide_toolbar 15 | neoterm_ui_next_tab_anim 16 | neoterm_ui_eks_weight_explicit 17 | neoterm_ui_eks_enabled 18 | 19 | neoterm_package_source 20 | 21 | neoterm_ui_font 22 | neoterm_ui_color_scheme 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/shortcut_configs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | id_create_new_session 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 22 | 23 | 27 | 28 | 43 | 44 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/xml/app_shortcuts.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_general.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 15 | 16 | 21 | 22 | 27 | 28 | 33 | 34 | 39 | 40 | 45 | 46 | 51 | 52 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 11 | 12 | 13 | 17 | 20 | 21 | 22 | 26 | 29 | 30 | 31 | 35 | 38 | 39 | 40 | 43 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings_ui.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 32 | -------------------------------------------------------------------------------- /artwork/NeoTerm_round.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/NeoTerm_round.psd -------------------------------------------------------------------------------- /artwork/NeoTerm_round_border.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/NeoTerm_round_border.psd -------------------------------------------------------------------------------- /artwork/neoterm.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/neoterm.psd -------------------------------------------------------------------------------- /artwork/old/icon-512-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/old/icon-512-old.png -------------------------------------------------------------------------------- /artwork/old/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/old/icon-512.png -------------------------------------------------------------------------------- /artwork/old/neoterm.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/artwork/old/neoterm.psd -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.4.32' 5 | ext.android = [ 6 | KOTLIN_VERSION : '1.4.32', 7 | MIN_SDK_VERSION : 26, 8 | COMPILE_SDK_VERSION: 28, 9 | TARGET_SDK_VERSION : 28, 10 | JUNIT_VERSION : "4.12" 11 | ] 12 | 13 | ext.deps = [ 14 | "annotations" : "androidx.annotation:annotation:1.0.0", 15 | "appcompat-v7" : "androidx.appcompat:appcompat:1.0.0", 16 | "design" : "com.android.support:design:${ext.android.ANDROID_SUPPORT_VERSION}", 17 | "cardview-v7" : "com.android.support:cardview-v7:${ext.android.ANDROID_SUPPORT_VERSION}", 18 | "kotlin-stdlib" : "org.jetbrains.kotlin:kotlin-stdlib:${ext.android.KOTLIN_VERSION}", 19 | "kotlin-gradle-plugin": "org.jetbrains.kotlin:kotlin-gradle-plugin:${ext.android.KOTLIN_VERSION}", 20 | "junit" : "junit:junit:${ext.android.JUNIT_VERSION}" 21 | ] 22 | 23 | repositories { 24 | maven { url 'https://dl.google.com/dl/android/maven2/' } 25 | jcenter() 26 | google() 27 | } 28 | 29 | dependencies { 30 | classpath 'com.android.tools.build:gradle:4.1.3' 31 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 32 | classpath rootProject.ext.deps["kotlin-gradle-plugin"] 33 | 34 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 35 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 36 | 37 | // NOTE: Do not place your application dependencies here; they belong 38 | // in the individual module build.gradle files 39 | } 40 | } 41 | 42 | allprojects { 43 | repositories { 44 | jcenter() 45 | maven { url 'https://dl.google.com/dl/android/maven2/' } 46 | mavenCentral() 47 | maven { url "https://jitpack.io" } 48 | google() 49 | } 50 | } 51 | 52 | task clean(type: Delete) { 53 | delete rootProject.buildDir 54 | } 55 | -------------------------------------------------------------------------------- /chrome-tabs/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chrome-tabs/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android.COMPILE_SDK_VERSION 5 | 6 | defaultConfig { 7 | minSdkVersion rootProject.ext.android.MIN_SDK_VERSION 8 | targetSdkVersion rootProject.ext.android.TARGET_SDK_VERSION 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled true 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | api 'com.github.michael-rapp:android-util:1.15.0' 21 | implementation rootProject.ext.deps["annotations"] 22 | testImplementation rootProject.ext.deps["junit"] 23 | 24 | implementation 'androidx.appcompat:appcompat:1.2.0' 25 | implementation 'androidx.appcompat:appcompat-resources:1.2.0' 26 | } 27 | -------------------------------------------------------------------------------- /chrome-tabs/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=ChromeLikeTabSwitcher 2 | POM_ARTIFACT_ID=chrome-like-tab-switcher 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /chrome-tabs/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/Layout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher; 15 | 16 | /** 17 | * Contains all possible layouts of a {@link TabSwitcher}. 18 | * 19 | * @author Michael Rapp 20 | * @since 0.1.0 21 | */ 22 | public enum Layout { 23 | 24 | /** 25 | * The layout, which is used on smartphones and phablet devices, when in portrait mode. 26 | */ 27 | PHONE_PORTRAIT, 28 | 29 | /** 30 | * The layout, which is used on smartphones and phablet devices, when in landscape mode. 31 | */ 32 | PHONE_LANDSCAPE, 33 | 34 | /** 35 | * The layout, which is used on tablets. 36 | */ 37 | TABLET 38 | 39 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/LayoutPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher; 15 | 16 | /** 17 | * Contains all possible layout policies of a {@link TabSwitcher}. 18 | * 19 | * @author Michael Rapp 20 | * @since 0.1.0 21 | */ 22 | public enum LayoutPolicy { 23 | 24 | /** 25 | * If the layout should automatically adapted, depending on whether the device is a smartphone 26 | * or tablet. 27 | */ 28 | AUTO(0), 29 | 30 | /** 31 | * If the smartphone layout should be used, regardless of the device. 32 | */ 33 | PHONE(1), 34 | 35 | /** 36 | * If the tablet layout should be used, regardless of the device. 37 | */ 38 | TABLET(2); 39 | 40 | /** 41 | * The value of the layout policy. 42 | */ 43 | private int value; 44 | 45 | /** 46 | * Creates a new layout policy. 47 | * 48 | * @param value The value of the layout policy as an {@link Integer} value 49 | */ 50 | LayoutPolicy(final int value) { 51 | this.value = value; 52 | } 53 | 54 | /** 55 | * Returns the value of the layout policy. 56 | * 57 | * @return The value of the layout policy as an {@link Integer} value 58 | */ 59 | public final int getValue() { 60 | return value; 61 | } 62 | 63 | /** 64 | * Returns the layout policy, which corresponds to a specific value. 65 | * 66 | * @param value The value of the layout policy, which should be returned, as an {@link Integer} 67 | * value 68 | * @return The layout policy, which corresponds to the given value, as a value of the enum 69 | * {@link LayoutPolicy} 70 | */ 71 | public static LayoutPolicy fromValue(final int value) { 72 | for (LayoutPolicy layoutPolicy : values()) { 73 | if (layoutPolicy.getValue() == value) { 74 | return layoutPolicy; 75 | } 76 | } 77 | 78 | throw new IllegalArgumentException("Invalid enum value: " + value); 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabCloseListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher; 15 | 16 | import androidx.annotation.NonNull; 17 | 18 | /** 19 | * Defines the interface, a class, which should be notified, when a tab is about to be closed by 20 | * clicking its close button, must implement. 21 | * 22 | * @author Michael Rapp 23 | * @since 0.1.0 24 | */ 25 | public interface TabCloseListener { 26 | 27 | /** 28 | * The method, which is invoked, when a tab is about to be closed by clicking its close button. 29 | * 30 | * @param tabSwitcher The tab switcher, the tab belongs to, as an instance of the class {@link 31 | * TabSwitcher}. The tab switcher may not be null 32 | * @param tab The tab, which is about to be closed, as an instance of the class {@link Tab}. The 33 | * tab may not be null 34 | * @return True, if the tab should be closed, false otherwise 35 | */ 36 | boolean onCloseTab(@NonNull TabSwitcher tabSwitcher, @NonNull Tab tab); 37 | 38 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabPreviewListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher; 15 | 16 | import androidx.annotation.NonNull; 17 | 18 | /** 19 | * Defines the interface, a class, which should be notified, when the preview of a tab is about to 20 | * be loaded, must implement. 21 | * 22 | * @author Michael Rapp 23 | * @since 0.1.0 24 | */ 25 | public interface TabPreviewListener { 26 | 27 | /** 28 | * The method, which is invoked, when the preview of a tab is about to be loaded. 29 | * 30 | * @param tabSwitcher The tab switcher, which contains the tab, whose preview is about to be loaded, as an 31 | * instance of the class {@link TabSwitcher}. The tab switcher may not be null 32 | * @param tab The tab, whose preview is about to be loaded, as an instance of the class {@link 33 | * Tab}. The tab may not be null 34 | * @return True, if loading the preview should be proceeded, false otherwise. When returning 35 | * false, the method gets invoked repeatedly until true is returned. 36 | */ 37 | boolean onLoadTabPreview(@NonNull TabSwitcher tabSwitcher, @NonNull Tab tab); 38 | 39 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/layout/AbstractTabViewHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher.layout; 15 | 16 | import android.widget.ImageButton; 17 | import android.widget.TextView; 18 | import de.mrapp.android.tabswitcher.TabSwitcher; 19 | 20 | /** 21 | * An abstract base class for all view holders, which allow to store references to the views, a tab 22 | * of a {@link TabSwitcher} consists of. 23 | * 24 | * @author Michael Rapp 25 | * @since 0.1.0 26 | */ 27 | public abstract class AbstractTabViewHolder { 28 | 29 | /** 30 | * The text view, which is used to display the title of a tab. 31 | */ 32 | public TextView titleTextView; 33 | 34 | /** 35 | * The close button of a tab. 36 | */ 37 | public ImageButton closeButton; 38 | 39 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/layout/TabSwitcherLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher.layout; 15 | 16 | import android.view.Menu; 17 | import android.view.ViewGroup; 18 | import androidx.annotation.Nullable; 19 | import androidx.appcompat.widget.Toolbar; 20 | import de.mrapp.android.tabswitcher.TabSwitcher; 21 | 22 | /** 23 | * Defines the interface, a layout, which implements the functionality of a {@link TabSwitcher}, 24 | * must implement. 25 | * 26 | * @author Michael Rapp 27 | * @since 0.1.0 28 | */ 29 | public interface TabSwitcherLayout { 30 | 31 | /** 32 | * Returns, whether an animation is currently running, or not. 33 | * 34 | * @return True, if an animation is currently running, false otherwise 35 | */ 36 | boolean isAnimationRunning(); 37 | 38 | /** 39 | * Returns the view group, which contains the tab switcher's tabs. 40 | * 41 | * @return The view group, which contains the tab switcher's tabs, as an instance of the class 42 | * {@link ViewGroup} or null, if the view has not been laid out yet 43 | */ 44 | @Nullable 45 | ViewGroup getTabContainer(); 46 | 47 | /** 48 | * Returns the toolbars, which are shown, when the tab switcher is shown. When using the 49 | * smartphone layout, only one toolbar is shown. When using the tablet layout, a primary and 50 | * secondary toolbar is shown. In such case, the first index of the returned array corresponds 51 | * to the primary toolbar. 52 | * 53 | * @return An array, which contains the toolbars, which are shown, when the tab switcher is 54 | * shown, as an array of the type Toolbar or null, if the view has not been laid out yet 55 | */ 56 | @Nullable 57 | Toolbar[] getToolbars(); 58 | 59 | /** 60 | * Returns the menu of the toolbar, which is shown, when the tab switcher is shown. When using 61 | * the tablet layout, the menu corresponds to the secondary toolbar. 62 | * 63 | * @return The menu of the toolbar as an instance of the type {@link Menu} or null, if the view 64 | * has not been laid out yet 65 | */ 66 | @Nullable 67 | Menu getToolbarMenu(); 68 | 69 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/layout/phone/PhoneTabViewHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher.layout.phone; 15 | 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.ImageView; 19 | import de.mrapp.android.tabswitcher.TabSwitcher; 20 | import de.mrapp.android.tabswitcher.layout.AbstractTabViewHolder; 21 | 22 | /** 23 | * A view holder, which allows to store references to the views, a tab of a {@link TabSwitcher} 24 | * consists of, when using the smartphone layout. 25 | * 26 | * @author Michael Rapp 27 | * @since 0.1.0 28 | */ 29 | public class PhoneTabViewHolder extends AbstractTabViewHolder { 30 | 31 | /** 32 | * The view group, which contains the title and close button of a tab. 33 | */ 34 | public ViewGroup titleContainer; 35 | 36 | /** 37 | * The view group, which contains the child view of a tab. 38 | */ 39 | public ViewGroup childContainer; 40 | 41 | /** 42 | * The child view, which contains the tab's content. 43 | */ 44 | public View child; 45 | 46 | /** 47 | * The image view, which is used to display the preview of a tab. 48 | */ 49 | public ImageView previewImageView; 50 | 51 | /** 52 | * The view, which is used to display a border around the preview of a tab. 53 | */ 54 | public View borderView; 55 | 56 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/model/Restorable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher.model; 15 | 16 | import android.os.Bundle; 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | 20 | /** 21 | * Defines the interface, a class, whose state should be stored and restored, must implement. 22 | * 23 | * @author Michael Rapp 24 | * @since 0.1.0 25 | */ 26 | public interface Restorable { 27 | 28 | /** 29 | * Saves the current state. 30 | * 31 | * @param outState The bundle, which should be used to store the saved state, as an instance of the 32 | * class {@link Bundle}. The bundle may not be null 33 | */ 34 | void saveInstanceState(@NonNull Bundle outState); 35 | 36 | /** 37 | * Restores a previously saved state. 38 | * 39 | * @param savedInstanceState The saved state as an instance of the class {@link Bundle} or null, if no saved state 40 | * is available 41 | */ 42 | void restoreInstanceState(@Nullable Bundle savedInstanceState); 43 | 44 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/model/State.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 - 2017 Michael Rapp 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package de.mrapp.android.tabswitcher.model; 15 | 16 | /** 17 | * Contains all possible states of a tab, while the switcher is shown. 18 | * 19 | * @author Michael Rapp 20 | * @since 0.1.0 21 | */ 22 | public enum State { 23 | 24 | /** 25 | * When the tab is part of the stack, which is located at the start of the switcher. 26 | */ 27 | STACKED_START, 28 | 29 | /** 30 | * When the tab is displayed atop of the stack, which is located at the start of the switcher. 31 | */ 32 | STACKED_START_ATOP, 33 | 34 | /** 35 | * When the tab is floating and freely movable. 36 | */ 37 | FLOATING, 38 | 39 | /** 40 | * When the tab is part of the stack, which is located at the end of the switcher. 41 | */ 42 | STACKED_END, 43 | 44 | /** 45 | * When the tab is currently not visible, i.e. if no view is inflated to visualize it. 46 | */ 47 | HIDDEN 48 | 49 | } -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-hdpi/phone_close_tab_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-hdpi/phone_close_tab_icon.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-hdpi/phone_tab_background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-hdpi/phone_tab_background.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-hdpi/phone_tab_border.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-hdpi/phone_tab_border.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-hdpi/tab_switcher_drawable_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-hdpi/tab_switcher_drawable_background.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-mdpi/ic_close_tab_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-mdpi/ic_close_tab_18dp.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-mdpi/phone_tab_background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-mdpi/phone_tab_background.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-mdpi/phone_tab_border.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-mdpi/phone_tab_border.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-mdpi/tab_switcher_drawable_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-mdpi/tab_switcher_drawable_background.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xhdpi/ic_close_tab_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xhdpi/ic_close_tab_18dp.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xhdpi/phone_tab_background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xhdpi/phone_tab_background.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xhdpi/phone_tab_border.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xhdpi/phone_tab_border.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xhdpi/tab_switcher_drawable_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xhdpi/tab_switcher_drawable_background.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxhdpi/ic_close_tab_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxhdpi/ic_close_tab_18dp.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxhdpi/phone_tab_background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxhdpi/phone_tab_background.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxhdpi/phone_tab_border.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxhdpi/phone_tab_border.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxhdpi/tab_switcher_drawable_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxhdpi/tab_switcher_drawable_background.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxxhdpi/ic_close_tab_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxxhdpi/ic_close_tab_18dp.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxxhdpi/phone_tab_background.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxxhdpi/phone_tab_background.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxxhdpi/phone_tab_border.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxxhdpi/phone_tab_border.9.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/drawable-xxxhdpi/tab_switcher_drawable_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/chrome-tabs/src/main/res/drawable-xxxhdpi/tab_switcher_drawable_background.png -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/layout-land/phone_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 22 | 23 | 28 | 29 | 37 | 38 | 47 | 48 | 49 | 50 | 55 | 56 | 63 | 64 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/layout/phone_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 22 | 23 | 28 | 29 | 38 | 39 | 47 | 48 | 49 | 50 | 55 | 56 | 63 | 64 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/layout/phone_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/layout/tab_switcher_menu_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | #ff14181c 19 | #fff2f2f2 20 | #a8000000 21 | 22 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 16dp 19 | 4dp 20 | 48dp 21 | 12dp 22 | 8dp 23 | 128dp 24 | 4dp 25 | 48dp 26 | 1280dp 27 | 2048dp 28 | 8dp 29 | 24dp 30 | 32dp 31 | 10sp 32 | 7sp 33 | 0.5 34 | 0 35 | 36 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 3 19 | 2 20 | 3 21 | @android:integer/config_shortAnimTime 22 | @android:integer/config_shortAnimTime 23 | @android:integer/config_mediumAnimTime 24 | @android:integer/config_shortAnimTime 25 | @android:integer/config_longAnimTime 26 | @android:integer/config_shortAnimTime 27 | @android:integer/config_mediumAnimTime 28 | @android:integer/config_shortAnimTime 29 | @android:integer/config_shortAnimTime 30 | @android:integer/config_shortAnimTime 31 | 1200 32 | 33 | -------------------------------------------------------------------------------- /chrome-tabs/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 25 | 26 | -------------------------------------------------------------------------------- /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 | android.enableJetifier=true 10 | android.useAndroidX=true 11 | org.gradle.jvmargs=-Xmx1536m 12 | # When configured, Gradle will run in incubating parallel mode. 13 | # This option should only be used with decoupled projects. More details, visit 14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 15 | # org.gradle.parallel=true 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeoTerrm/NeoTerm/236072395ce056d2d2cccf950d3f243f099a178f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 20 22:03:06 EEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT executablePath 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT executablePath 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':chrome-tabs', ':NeoLang', ':Xorg', ':NeoTermBridge' 2 | --------------------------------------------------------------------------------