Improve: Huge Details

This commit is contained in:
zt515
2017-06-18 11:22:50 +08:00
parent cec491468b
commit 0980a16ccd
24 changed files with 336 additions and 112 deletions

View File

@ -55,6 +55,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'org.greenrobot:eventbus:3.0.0'
compile project(':chrome-tabs') compile project(':chrome-tabs')
implementation 'com.android.support:design:25.3.1' implementation 'com.android.support:design:25.3.1'
} }

View File

@ -14,6 +14,9 @@ object NeoTermPath {
const val EKS_PATH = "$USR_PATH/share/eks" const val EKS_PATH = "$USR_PATH/share/eks"
const val EKS_DEFAULT_FILE = "$EKS_PATH/default.eks" const val EKS_DEFAULT_FILE = "$EKS_PATH/default.eks"
const val SERVER_BASE_URL = "http://mirror.neoterm.studio" const val SOURCE_FILE = "$USR_PATH/etc/apt/sources.list"
const val DEFAULT_SOURCE = "http://mirror.neoterm.studio"
const val SERVER_BASE_URL = DEFAULT_SOURCE
const val SERVER_BOOT_URL = "$SERVER_BASE_URL/boot" const val SERVER_BOOT_URL = "$SERVER_BASE_URL/boot"
} }

View File

@ -15,7 +15,7 @@ object ShortcutConfigLoader {
extraKeysView.loadDefaultUserDefinedExtraKeys() extraKeysView.loadDefaultUserDefinedExtraKeys()
} }
for (button in config.shortcutKeys) { for (button in config.shortcutKeys) {
extraKeysView.addExternalButton(button) extraKeysView.addUserDefinedButton(button)
} }
} }
} }

View File

@ -2,6 +2,7 @@ package io.neoterm.installer;
import android.app.Activity; import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.system.Os; import android.system.Os;
import android.util.Log; import android.util.Log;
@ -10,7 +11,9 @@ import android.util.Pair;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@ -22,6 +25,7 @@ import java.util.zip.ZipInputStream;
import io.neoterm.R; import io.neoterm.R;
import io.neoterm.backend.EmulatorDebug; import io.neoterm.backend.EmulatorDebug;
import io.neoterm.customize.NeoTermPath; import io.neoterm.customize.NeoTermPath;
import io.neoterm.utils.FileUtils;
public final class BaseFileInstaller { public final class BaseFileInstaller {
public interface ResultListener { public interface ResultListener {
@ -35,7 +39,11 @@ public final class BaseFileInstaller {
return; return;
} }
final ProgressDialog progress = ProgressDialog.show(activity, null, activity.getString(R.string.installer_message), true, false); installHomeFiles(activity);
final ProgressDialog progress = makeProgressDialog(activity);
progress.setMax(100);
progress.show();
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {
@ -47,13 +55,37 @@ public final class BaseFileInstaller {
deleteFolder(STAGING_PREFIX_FILE); deleteFolder(STAGING_PREFIX_FILE);
} }
int totalBytes = 0;
int totalReadBytes = 0;
final byte[] buffer = new byte[8096]; final byte[] buffer = new byte[8096];
final List<Pair<String, String>> symlinks = new ArrayList<>(50); final List<Pair<String, String>> symlinks = new ArrayList<>(50);
final URL zipUrl = determineZipUrl(); final URL zipUrl = determineZipUrl();
try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) { HttpURLConnection connection = (HttpURLConnection) zipUrl.openConnection();
totalBytes = connection.getContentLength();
try (ZipInputStream zipInput = new ZipInputStream(connection.getInputStream())) {
ZipEntry zipEntry; ZipEntry zipEntry;
while ((zipEntry = zipInput.getNextEntry()) != null) { while ((zipEntry = zipInput.getNextEntry()) != null) {
totalReadBytes += zipEntry.getCompressedSize();
final int totalReadBytesFinal = totalReadBytes;
final int totalBytesFinal = totalBytes;
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
double progressFloat = ((double) totalReadBytesFinal) / ((double) totalBytesFinal) * 100.0;
Log.e("NeoTerm-Installer", "total: " + totalBytesFinal + ", read: " + totalReadBytesFinal + ", " + progressFloat);
progress.setProgress((int) progressFloat);
} catch (RuntimeException ignore) {
// activity dismissed
}
}
});
if (zipEntry.getName().equals("SYMLINKS.txt")) { if (zipEntry.getName().equals("SYMLINKS.txt")) {
BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput)); BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput));
String line; String line;
@ -77,9 +109,10 @@ public final class BaseFileInstaller {
} else { } else {
try (FileOutputStream outStream = new FileOutputStream(targetFile)) { try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
int readBytes; int readBytes;
while ((readBytes = zipInput.read(buffer)) != -1) while ((readBytes = zipInput.read(buffer)) != -1) {
outStream.write(buffer, 0, readBytes); outStream.write(buffer, 0, readBytes);
} }
}
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) { if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) {
//noinspection OctalInteger //noinspection OctalInteger
Os.chmod(targetFile.getAbsolutePath(), 0700); Os.chmod(targetFile.getAbsolutePath(), 0700);
@ -89,6 +122,8 @@ public final class BaseFileInstaller {
} }
} }
connection.disconnect();
if (symlinks.isEmpty()) if (symlinks.isEmpty())
throw new RuntimeException("No SYMLINKS.txt encountered"); throw new RuntimeException("No SYMLINKS.txt encountered");
for (Pair<String, String> symlink : symlinks) { for (Pair<String, String> symlink : symlinks) {
@ -133,6 +168,26 @@ public final class BaseFileInstaller {
}.start(); }.start();
} }
private static void installHomeFiles(final Activity activity) {
File HOME_PATH = new File(NeoTermPath.HOME_PATH);
File ZSH_INSTALLER = new File(HOME_PATH, "install-zsh.sh");
if (!HOME_PATH.exists()) {
HOME_PATH.mkdirs();
}
if (!ZSH_INSTALLER.exists()) {
try {
InputStream inputStream = activity.getAssets().open("install-zsh.sh");
FileUtils.INSTANCE.writeFile(ZSH_INSTALLER, inputStream);
inputStream.close();
Os.chmod(ZSH_INSTALLER.getAbsolutePath(), 0700);
} catch (Exception ignore) {
}
}
}
private static URL determineZipUrl() throws MalformedURLException { private static URL determineZipUrl() throws MalformedURLException {
String archName = determineArchName(); String archName = determineArchName();
return new URL(NeoTermPath.SERVER_BOOT_URL + "/" + archName + ".zip"); return new URL(NeoTermPath.SERVER_BOOT_URL + "/" + archName + ".zip");
@ -166,4 +221,13 @@ public final class BaseFileInstaller {
throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath()); throw new RuntimeException("Unable to delete " + (fileOrDirectory.isDirectory() ? "directory " : "file ") + fileOrDirectory.getAbsolutePath());
} }
} }
public static ProgressDialog makeProgressDialog(Context context) {
ProgressDialog dialog = new ProgressDialog(context);
dialog.setMessage(context.getString(R.string.installer_message));
dialog.setIndeterminate(false);
dialog.setCancelable(false);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
return dialog;
}
} }

View File

@ -3,9 +3,11 @@ package io.neoterm.preference
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.preference.PreferenceManager import android.preference.PreferenceManager
import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.customize.NeoTermPath import io.neoterm.customize.NeoTermPath
import io.neoterm.services.NeoTermService import io.neoterm.services.NeoTermService
import io.neoterm.utils.FileUtils
import java.io.File import java.io.File
@ -13,7 +15,7 @@ import java.io.File
* @author kiva * @author kiva
*/ */
object NeoTermPreference { object NeoPreference {
const val KEY_FONT_SIZE = "neoterm_general_font_size" const val KEY_FONT_SIZE = "neoterm_general_font_size"
const val KEY_CURRENT_SESSION = "neoterm_service_current_session" const val KEY_CURRENT_SESSION = "neoterm_service_current_session"
@ -23,6 +25,17 @@ object NeoTermPreference {
fun init(context: Context) { fun init(context: Context) {
this.context = context this.context = context
preference = PreferenceManager.getDefaultSharedPreferences(context) preference = PreferenceManager.getDefaultSharedPreferences(context)
// load apt source
val sourceFile = File(NeoTermPath.SOURCE_FILE)
val bytes = FileUtils.readFile(sourceFile)
if (bytes != null) {
val source = String(FileUtils.readFile(sourceFile)!!).trim().trimEnd()
val array = source.split(" ")
if (array.size >= 2 && array[0] == "deb") {
store(R.string.key_package_source, array[1])
}
}
} }
fun cleanup() { fun cleanup() {
@ -68,7 +81,7 @@ object NeoTermPreference {
fun storeCurrentSession(session: TerminalSession) { fun storeCurrentSession(session: TerminalSession) {
preference!!.edit() preference!!.edit()
.putString(NeoTermPreference.KEY_CURRENT_SESSION, session.mHandle) .putString(NeoPreference.KEY_CURRENT_SESSION, session.mHandle)
.apply() .apply()
} }

View File

@ -15,7 +15,7 @@ import io.neoterm.R
import io.neoterm.backend.EmulatorDebug import io.neoterm.backend.EmulatorDebug
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.customize.NeoTermPath import io.neoterm.customize.NeoTermPath
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import io.neoterm.ui.NeoTermActivity import io.neoterm.ui.NeoTermActivity
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -83,7 +83,7 @@ class NeoTermService : Service() {
executablePath = if (systemShell) executablePath = if (systemShell)
"/system/bin/sh" "/system/bin/sh"
else else
NeoTermPath.USR_PATH + "/bin/" + NeoTermPreference.loadString(R.string.key_general_shell, "sh") NeoTermPath.USR_PATH + "/bin/" + NeoPreference.loadString(R.string.key_general_shell, "sh")
if (!File(executablePath).exists()) { if (!File(executablePath).exists()) {
Toast.makeText(this, getString(R.string.shell_not_found, executablePath), Toast.LENGTH_LONG).show() Toast.makeText(this, getString(R.string.shell_not_found, executablePath), Toast.LENGTH_LONG).show()
@ -96,7 +96,7 @@ class NeoTermService : Service() {
} }
val session = TerminalSession(executablePath, cwd, arguments, val session = TerminalSession(executablePath, cwd, arguments,
env ?: NeoTermPreference.buildEnvironment(cwd, systemShell, executablePath), env ?: NeoPreference.buildEnvironment(cwd, systemShell, executablePath),
sessionCallback) sessionCallback)
mTerminalSessions.add(session) mTerminalSessions.add(session)
updateNotification() updateNotification()

View File

@ -16,7 +16,6 @@ import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.Toast
import de.mrapp.android.tabswitcher.* import de.mrapp.android.tabswitcher.*
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
@ -25,15 +24,20 @@ import io.neoterm.customize.shortcut.ShortcutConfigLoader
import io.neoterm.customize.shortcut.builtin.BuiltinShortcutKeys import io.neoterm.customize.shortcut.builtin.BuiltinShortcutKeys
import io.neoterm.installer.BaseFileInstaller import io.neoterm.installer.BaseFileInstaller
import io.neoterm.preference.NeoPermission import io.neoterm.preference.NeoPermission
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import io.neoterm.services.NeoTermService import io.neoterm.services.NeoTermService
import io.neoterm.ui.settings.SettingActivity import io.neoterm.ui.settings.SettingActivity
import io.neoterm.utils.NeoTermFullScreen import io.neoterm.utils.FullScreenHelper
import io.neoterm.view.eks.StatedControlButton
import io.neoterm.view.tab.* import io.neoterm.view.tab.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreferences.OnSharedPreferenceChangeListener { class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreferences.OnSharedPreferenceChangeListener {
lateinit var tabSwitcher: TabSwitcher lateinit var tabSwitcher: TabSwitcher
lateinit var fullScreenToggleButton: StatedControlButton
var systemShell = true var systemShell = true
var termService: NeoTermService? = null var termService: NeoTermService? = null
var restartRequired = false var restartRequired = false
@ -43,16 +47,17 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION) NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION)
FontManager.init(this) FontManager.init(this)
NeoTermPreference.init(this) NeoPreference.init(this)
if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false)) { val fullscreen = NeoPreference.loadBoolean(R.string.key_ui_fullscreen, false)
if (fullscreen) {
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN) WindowManager.LayoutParams.FLAG_FULLSCREEN)
} }
setContentView(R.layout.tab_main) setContentView(R.layout.tab_main)
FullScreenHelper.injectActivity(this, peekRecreating())
NeoTermFullScreen.injectActivity(this)
.setKeyBoardListener({ isShow, _ -> .setKeyBoardListener({ isShow, _ ->
var tab: TermTab? = null var tab: TermTab? = null
@ -62,12 +67,24 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
tab?.viewClient?.extraKeysView?.visibility = if (isShow) View.VISIBLE else View.GONE tab?.viewClient?.extraKeysView?.visibility = if (isShow) View.VISIBLE else View.GONE
if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false) if (NeoPreference.loadBoolean(R.string.key_ui_fullscreen, false)
|| NeoTermPreference.loadBoolean(R.string.key_ui_hide_toolbar, false)) { || NeoPreference.loadBoolean(R.string.key_ui_hide_toolbar, false)) {
tab?.toolbar?.visibility = if (isShow) View.GONE else View.VISIBLE tab?.toolbar?.visibility = if (isShow) View.GONE else View.VISIBLE
} }
}) })
fullScreenToggleButton = object : StatedControlButton("FS", fullscreen) {
override fun onClick(view: View?) {
super.onClick(view)
if (tabSwitcher.selectedTab is TermTab) {
val tab = tabSwitcher.selectedTab as TermTab
tab.hideIme()
}
NeoPreference.store(R.string.key_ui_fullscreen, super.toggleButton.isChecked)
this@NeoTermActivity.recreate()
}
}
tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher
tabSwitcher.decorator = TermTabDecorator(this) tabSwitcher.decorator = TermTabDecorator(this)
ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener()) ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener())
@ -92,9 +109,8 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
super.onResume() super.onResume()
if (restartRequired) { if (restartRequired) {
restartRequired = false restartRequired = false
this.recreate() recreate()
} }
PreferenceManager.getDefaultSharedPreferences(this) PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this) .registerOnSharedPreferenceChangeListener(this)
tabSwitcher.addListener(object : TabSwitcherListener { tabSwitcher.addListener(object : TabSwitcherListener {
@ -123,7 +139,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
override fun onSelectionChanged(tabSwitcher: TabSwitcher, selectedTabIndex: Int, selectedTab: Tab?) { override fun onSelectionChanged(tabSwitcher: TabSwitcher, selectedTabIndex: Int, selectedTab: Tab?) {
if (selectedTab is TermTab && selectedTab.termSession != null) { if (selectedTab is TermTab && selectedTab.termSession != null) {
NeoTermPreference.storeCurrentSession(selectedTab.termSession!!) NeoPreference.storeCurrentSession(selectedTab.termSession!!)
} }
} }
@ -143,6 +159,16 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
}) })
} }
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
PreferenceManager.getDefaultSharedPreferences(this) PreferenceManager.getDefaultSharedPreferences(this)
@ -154,7 +180,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
termService = null termService = null
} }
unbindService(this) unbindService(this)
NeoTermPreference.cleanup() NeoPreference.cleanup()
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@ -185,6 +211,12 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
} }
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == getString(R.string.key_ui_fullscreen)) {
restartRequired = true
}
}
override fun onServiceDisconnected(name: ComponentName?) { override fun onServiceDisconnected(name: ComponentName?) {
if (termService != null) { if (termService != null) {
finish() finish()
@ -227,14 +259,35 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
} }
} }
if (!isRecreating()) {
BaseFileInstaller.installBaseFiles(this, resultListener) BaseFileInstaller.installBaseFiles(this, resultListener)
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == getString(R.string.key_ui_fullscreen)) {
Toast.makeText(this, R.string.fullscreen_mode_changed, Toast.LENGTH_SHORT).show()
restartRequired = true
} }
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putBoolean("system_shell", systemShell)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
systemShell = savedInstanceState?.getBoolean("system_shell", true) ?: true
}
override fun recreate() {
NeoPreference.store("recreate", true)
super.recreate()
}
private fun isRecreating(): Boolean {
val result = peekRecreating()
NeoPreference.store("recreate", !result)
return result
}
private fun peekRecreating(): Boolean {
val result = NeoPreference.loadBoolean("recreate", false)
return result
} }
private fun addNewSession(session: TerminalSession?) { private fun addNewSession(session: TerminalSession?) {
@ -299,7 +352,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
} }
private fun getStoredCurrentSessionOrLast(): TerminalSession? { private fun getStoredCurrentSessionOrLast(): TerminalSession? {
val stored = NeoTermPreference.getCurrentSession(termService) val stored = NeoPreference.getCurrentSession(termService)
if (stored != null) return stored if (stored != null) return stored
val numberOfSessions = termService!!.sessions.size val numberOfSessions = termService!!.sessions.size
if (numberOfSessions == 0) return null if (numberOfSessions == 0) return null
@ -340,24 +393,6 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
private fun createTab(tabTitle: String?): Tab { private fun createTab(tabTitle: String?): Tab {
val tab = TermTab(tabTitle ?: "NeoTerm") val tab = TermTab(tabTitle ?: "NeoTerm")
tab.closeTabProvider = object : CloseTabProvider {
override fun closeTab(tab: Tab) {
tabSwitcher.showSwitcher()
tabSwitcher.removeTab(tab)
if (tabSwitcher.count > 1) {
var index = tabSwitcher.indexOf(tab)
if (NeoTermPreference.loadBoolean(R.string.key_ui_next_tab_anim, false)) {
// 关闭当前窗口后,向下一个窗口切换
if (--index < 0) index = tabSwitcher.count - 1
} else {
// 关闭当前窗口后,向上一个窗口切换
if (++index >= tabSwitcher.count) index = 0
}
switchToSession(tabSwitcher.getTab(index))
}
}
}
tab.isCloseable = true tab.isCloseable = true
tab.parameters = Bundle() tab.parameters = Bundle()
tab.setBackgroundColor(ContextCompat.getColor(this, R.color.tab_background_color)) tab.setBackgroundColor(ContextCompat.getColor(this, R.color.tab_background_color))
@ -408,4 +443,24 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
insets insets
} }
} }
@Suppress("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
fun onTabCloseEvent(tabCloseEvent: TabCloseEvent) {
val tab = tabCloseEvent.termTab
tabSwitcher.showSwitcher()
tabSwitcher.removeTab(tab)
if (tabSwitcher.count > 1) {
var index = tabSwitcher.indexOf(tab)
if (NeoPreference.loadBoolean(R.string.key_ui_next_tab_anim, false)) {
// 关闭当前窗口后,向下一个窗口切换
if (--index < 0) index = tabSwitcher.count - 1
} else {
// 关闭当前窗口后,向上一个窗口切换
if (++index >= tabSwitcher.count) index = 0
}
switchToSession(tabSwitcher.getTab(index))
}
}
} }

View File

@ -1,9 +1,14 @@
package io.neoterm.ui.settings package io.neoterm.ui.settings
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatPreferenceActivity import android.support.v7.app.AppCompatPreferenceActivity
import android.view.MenuItem import android.view.MenuItem
import io.neoterm.R import io.neoterm.R
import io.neoterm.customize.NeoTermPath
import io.neoterm.preference.NeoPreference
import io.neoterm.utils.FileUtils
import java.io.File
/** /**
* @author kiva * @author kiva
@ -15,6 +20,33 @@ class PackageSettingsActivity : AppCompatPreferenceActivity() {
supportActionBar.title = getString(R.string.package_settings) supportActionBar.title = getString(R.string.package_settings)
supportActionBar.setDisplayHomeAsUpEnabled(true) supportActionBar.setDisplayHomeAsUpEnabled(true)
addPreferencesFromResource(R.xml.settings_package) addPreferencesFromResource(R.xml.settings_package)
val preference = findPreference(getString(R.string.key_package_source))
preference.summary = NeoPreference.loadString(R.string.key_package_source, NeoTermPath.DEFAULT_SOURCE)
preference.setOnPreferenceChangeListener { preference, newValue ->
val newSource = newValue as String
preference.summary = newSource
if (newSource.isNotEmpty()) {
val sourceFile = File(NeoTermPath.SOURCE_FILE)
FileUtils.writeFile(sourceFile, generateSourceFile(newSource).toByteArray())
AlertDialog.Builder(this@PackageSettingsActivity)
.setMessage(R.string.source_changed)
.setPositiveButton(android.R.string.yes, null)
.show()
}
return@setOnPreferenceChangeListener true
}
}
private fun generateSourceFile(source: String): String {
return StringBuilder().append("# Generated by NeoTerm-Preference\n")
.append("deb ")
.append(source)
.append(" stable main")
.append("\n")
.toString()
} }
override fun onBuildHeaders(target: MutableList<Header>?) { override fun onBuildHeaders(target: MutableList<Header>?) {

View File

@ -1,5 +1,6 @@
package io.neoterm.ui.settings package io.neoterm.ui.settings
import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatPreferenceActivity import android.support.v7.app.AppCompatPreferenceActivity
import android.view.MenuItem import android.view.MenuItem
@ -16,7 +17,13 @@ class UISettingsActivity : AppCompatPreferenceActivity() {
supportActionBar.setDisplayHomeAsUpEnabled(true) supportActionBar.setDisplayHomeAsUpEnabled(true)
addPreferencesFromResource(R.xml.settings_ui) addPreferencesFromResource(R.xml.settings_ui)
findPreference(getString(R.string.key_ui_suggestions)) findPreference(getString(R.string.key_ui_suggestions))
.setOnPreferenceChangeListener({preference, newValue -> .setOnPreferenceChangeListener({_, newValue ->
if (newValue as Boolean) {
AlertDialog.Builder(this@UISettingsActivity)
.setMessage(R.string.installer_install_zsh_manually)
.setPositiveButton(android.R.string.yes, null)
.show()
}
return@setOnPreferenceChangeListener true return@setOnPreferenceChangeListener true
}) })
} }

View File

@ -1,31 +1,37 @@
package io.neoterm.utils package io.neoterm.utils
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.OutputStream import java.io.InputStream
/** /**
* @author kiva * @author kiva
*/ */
object FileUtils { object FileUtils {
fun writeFile(path: File, bytes: ByteArray): Boolean { fun writeFile(path: File, bytes: ByteArray): Boolean {
var output: OutputStream? = null return FileOutputStream(path).use {
var success = true it.write(bytes)
try { it.flush()
output = FileOutputStream(path) true
output.write(bytes) }
output.flush() }
} catch (e: Exception) {
e.printStackTrace() fun writeFile(path: File, inputStream: InputStream): Boolean {
success = false val bytes = ByteArray(inputStream.available())
} finally { inputStream.read(bytes)
if (output != null) { return writeFile(path, bytes)
try { }
output.close()
} catch (ignore: Exception) { fun readFile(path: File): ByteArray? {
if (!path.canRead()) {
return null
}
return FileInputStream(path).use {
val bytes = ByteArray(it.available())
it.read(bytes)
bytes
} }
} }
} }
return success
}
}

View File

@ -9,11 +9,10 @@ import android.widget.FrameLayout;
/** /**
* Helper class to "adjustResize" Activity when we are in full screen mode and check IME status. * Helper class to "adjustResize" Activity when we are in full screen mode and check IME status.
* Android Bug 5497: https://code.google.com/p/android/issues/detail?id=5497 * Android Bug 5497: https://code.google.com/p/android/issues/detail?id=5497
* author @kiva
*/ */
public class NeoTermFullScreen { public class FullScreenHelper {
public static NeoTermFullScreen injectActivity(Activity activity) { public static FullScreenHelper injectActivity(Activity activity, boolean isRecreating) {
return new NeoTermFullScreen(activity); return new FullScreenHelper(activity, isRecreating);
} }
public interface KeyBoardListener { public interface KeyBoardListener {
@ -33,12 +32,14 @@ public class NeoTermFullScreen {
private int mOriginHeight; private int mOriginHeight;
private int mPreHeight; private int mPreHeight;
private KeyBoardListener mKeyBoardListener; private KeyBoardListener mKeyBoardListener;
private boolean shouldSkipFirstTime;
public void setKeyBoardListener(KeyBoardListener mKeyBoardListener) { public void setKeyBoardListener(KeyBoardListener mKeyBoardListener) {
this.mKeyBoardListener = mKeyBoardListener; this.mKeyBoardListener = mKeyBoardListener;
} }
private NeoTermFullScreen(Activity activity) { private FullScreenHelper(Activity activity, boolean isRecreating) {
this.shouldSkipFirstTime = isRecreating;
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0); mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@ -52,10 +53,12 @@ public class NeoTermFullScreen {
private void monitorImeStatus() { private void monitorImeStatus() {
int currHeight = mChildOfContent.getHeight(); int currHeight = mChildOfContent.getHeight();
if (currHeight == 0) { if (currHeight == 0 && shouldSkipFirstTime) {
// First time // First time
return; return;
} }
shouldSkipFirstTime = false;
boolean hasChange = false; boolean hasChange = false;
if (mPreHeight == 0) { if (mPreHeight == 0) {
mPreHeight = currHeight; mPreHeight = currHeight;

View File

@ -50,7 +50,7 @@ public final class ExtraKeysView extends LinearLayout {
"define | false\n"; "define | false\n";
public static final int NORMAL_TEXT_COLOR = 0xFFFFFFFF; public static final int NORMAL_TEXT_COLOR = 0xFFFFFFFF;
public static final int SELECTED_TEXT_COLOR = 0xFF80DEEA;
private List<ExtraButton> builtinExtraKeys; private List<ExtraButton> builtinExtraKeys;
private List<ExtraButton> userDefinedExtraKeys; private List<ExtraButton> userDefinedExtraKeys;
@ -85,7 +85,6 @@ public final class ExtraKeysView extends LinearLayout {
return line; return line;
} }
public boolean readControlButton() { public boolean readControlButton() {
return CTRL.readState(); return CTRL.readState();
} }
@ -94,11 +93,21 @@ public final class ExtraKeysView extends LinearLayout {
return false; return false;
} }
public void addExternalButton(ExtraButton button) { public void addUserDefinedButton(ExtraButton button) {
userDefinedExtraKeys.add(button); addButton(userDefinedExtraKeys, button);
} }
public void clearExternalButton() { public void addBuiltinButton(ExtraButton button) {
addButton(builtinExtraKeys, button);
}
private void addButton(List<ExtraButton> buttons, ExtraButton button) {
if (!buttons.contains(button)) {
buttons.add(button);
}
}
public void clearUserDefinedButton() {
userDefinedExtraKeys.clear(); userDefinedExtraKeys.clear();
} }
@ -109,7 +118,7 @@ public final class ExtraKeysView extends LinearLayout {
generateDefaultFile(defaultFile); generateDefaultFile(defaultFile);
} }
clearExternalButton(); clearUserDefinedButton();
try { try {
ShortcutConfigParser parser = new ShortcutConfigParser(); ShortcutConfigParser parser = new ShortcutConfigParser();
parser.setInput(defaultFile); parser.setInput(defaultFile);
@ -153,6 +162,10 @@ public final class ExtraKeysView extends LinearLayout {
StatedControlButton btn = ((StatedControlButton) extraButton); StatedControlButton btn = ((StatedControlButton) extraButton);
button = btn.toggleButton = new ToggleButton(getContext(), null, android.R.attr.buttonBarButtonStyle); button = btn.toggleButton = new ToggleButton(getContext(), null, android.R.attr.buttonBarButtonStyle);
button.setClickable(true); button.setClickable(true);
if (btn.initState) {
btn.toggleButton.setChecked(true);
btn.toggleButton.setTextColor(SELECTED_TEXT_COLOR);
}
} else { } else {
button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle); button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
} }

View File

@ -11,15 +11,25 @@ import io.neoterm.view.ExtraKeysView;
public class StatedControlButton extends ControlButton { public class StatedControlButton extends ControlButton {
public ToggleButton toggleButton; public ToggleButton toggleButton;
public boolean initState;
public StatedControlButton(String text, boolean initState) {
super(text);
this.initState = initState;
}
public StatedControlButton(String text) { public StatedControlButton(String text) {
super(text); this(text, false);
} }
@Override @Override
public void onClick(View view) { public void onClick(View view) {
toggleButton.setChecked(toggleButton.isChecked()); setStatus(toggleButton.isChecked());
toggleButton.setTextColor(toggleButton.isChecked() ? 0xFF80DEEA : ExtraKeysView.NORMAL_TEXT_COLOR); }
public void setStatus(boolean status) {
toggleButton.setChecked(status);
toggleButton.setTextColor(status ? ExtraKeysView.SELECTED_TEXT_COLOR : ExtraKeysView.NORMAL_TEXT_COLOR);
} }
public boolean readState() { public boolean readState() {

View File

@ -1,10 +0,0 @@
package io.neoterm.view.tab
import de.mrapp.android.tabswitcher.Tab
/**
* @author kiva
*/
interface CloseTabProvider {
fun closeTab(tab: Tab)
}

View File

@ -0,0 +1,7 @@
package io.neoterm.view.tab
/**
* @author kiva
*/
class TabCloseEvent(var termTab: TermTab)

View File

@ -7,8 +7,7 @@ import android.media.SoundPool
import android.os.Vibrator import android.os.Vibrator
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import io.neoterm.view.ExtraKeysView
import io.neoterm.view.TerminalView import io.neoterm.view.TerminalView
/** /**
@ -47,7 +46,7 @@ class TermSessionChangedCallback : TerminalSession.SessionChangedCallback {
return return
} }
if (NeoTermPreference.loadBoolean(R.string.key_general_bell, false)) { if (NeoPreference.loadBoolean(R.string.key_general_bell, false)) {
if (soundPool == null) { if (soundPool == null) {
soundPool = SoundPool.Builder().setMaxStreams(1).build() soundPool = SoundPool.Builder().setMaxStreams(1).build()
bellId = soundPool!!.load(termView!!.context, R.raw.bell, 1) bellId = soundPool!!.load(termView!!.context, R.raw.bell, 1)
@ -55,7 +54,7 @@ class TermSessionChangedCallback : TerminalSession.SessionChangedCallback {
soundPool?.play(bellId, 1f, 1f, 0, 0, 1f) soundPool?.play(bellId, 1f, 1f, 0, 0, 1f)
} }
if (NeoTermPreference.loadBoolean(R.string.key_general_vibrate, false)) { if (NeoPreference.loadBoolean(R.string.key_general_vibrate, false)) {
val vibrator = termView!!.context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator val vibrator = termView!!.context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator.vibrate(100) vibrator.vibrate(100)
} }

View File

@ -1,12 +1,15 @@
package io.neoterm.view.tab package io.neoterm.view.tab
import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.support.v7.widget.Toolbar import android.support.v7.widget.Toolbar
import android.view.inputmethod.InputMethodManager
import de.mrapp.android.tabswitcher.Tab import de.mrapp.android.tabswitcher.Tab
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.customize.color.NeoTermColorScheme import io.neoterm.customize.color.NeoTermColorScheme
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import org.greenrobot.eventbus.EventBus
/** /**
* @author kiva * @author kiva
@ -18,8 +21,6 @@ class TermTab(title: CharSequence) : Tab(title) {
var viewClient: TermViewClient? = null var viewClient: TermViewClient? = null
var toolbar: Toolbar? = null var toolbar: Toolbar? = null
var closeTabProvider: CloseTabProvider? = null
fun changeColorScheme(colorScheme: NeoTermColorScheme?) { fun changeColorScheme(colorScheme: NeoTermColorScheme?) {
colorScheme?.apply() colorScheme?.apply()
viewClient?.extraKeysView?.setBackgroundColor(Color.parseColor(colorScheme?.background)) viewClient?.extraKeysView?.setBackgroundColor(Color.parseColor(colorScheme?.background))
@ -31,7 +32,6 @@ class TermTab(title: CharSequence) : Tab(title) {
viewClient?.extraKeysView = null viewClient?.extraKeysView = null
sessionCallback?.termView = null sessionCallback?.termView = null
sessionCallback?.termTab = null sessionCallback?.termTab = null
closeTabProvider = null
toolbar = null toolbar = null
termSession = null termSession = null
} }
@ -39,7 +39,7 @@ class TermTab(title: CharSequence) : Tab(title) {
fun updateTitle(title: String) { fun updateTitle(title: String) {
this.title = title this.title = title
toolbar?.title = title toolbar?.title = title
if (NeoTermPreference.loadBoolean(R.string.key_ui_suggestions, true)) { if (NeoPreference.loadBoolean(R.string.key_ui_suggestions, true)) {
viewClient?.updateSuggestions(title) viewClient?.updateSuggestions(title)
} else { } else {
viewClient?.removeSuggestions() viewClient?.removeSuggestions()
@ -51,6 +51,17 @@ class TermTab(title: CharSequence) : Tab(title) {
} }
fun requiredCloseTab() { fun requiredCloseTab() {
closeTabProvider?.closeTab(this) hideIme()
EventBus.getDefault().post(TabCloseEvent(this))
}
fun hideIme() {
val terminalView = viewClient?.termView
if (terminalView != null) {
val imm = terminalView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (imm.isActive) {
imm.hideSoftInputFromWindow(terminalView.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
}
}
} }
} }

View File

@ -12,7 +12,7 @@ import de.mrapp.android.tabswitcher.Tab
import de.mrapp.android.tabswitcher.TabSwitcher import de.mrapp.android.tabswitcher.TabSwitcher
import de.mrapp.android.tabswitcher.TabSwitcherDecorator import de.mrapp.android.tabswitcher.TabSwitcherDecorator
import io.neoterm.R import io.neoterm.R
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import io.neoterm.ui.NeoTermActivity import io.neoterm.ui.NeoTermActivity
import io.neoterm.view.ExtraKeysView import io.neoterm.view.ExtraKeysView
import io.neoterm.view.TerminalView import io.neoterm.view.TerminalView
@ -24,8 +24,12 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
override fun onInflateView(inflater: LayoutInflater, parent: ViewGroup?, viewType: Int): View { override fun onInflateView(inflater: LayoutInflater, parent: ViewGroup?, viewType: Int): View {
val view = inflater.inflate(R.layout.term, parent, false) val view = inflater.inflate(R.layout.term, parent, false)
val toolbar = view.findViewById(R.id.terminal_toolbar) as Toolbar val toolbar = view.findViewById(R.id.terminal_toolbar) as Toolbar
toolbar.inflateMenu(R.menu.tab_switcher) val extraKeysView = view.findViewById(R.id.extra_keys) as ExtraKeysView
extraKeysView.addBuiltinButton(context.fullScreenToggleButton)
extraKeysView.updateButtons()
toolbar.inflateMenu(R.menu.tab_switcher)
toolbar.setOnMenuItemClickListener(context.createToolbarMenuListener()) toolbar.setOnMenuItemClickListener(context.createToolbarMenuListener())
val menu = toolbar.menu val menu = toolbar.menu
TabSwitcher.setupWithMenu(context.tabSwitcher, menu, { TabSwitcher.setupWithMenu(context.tabSwitcher, menu, {
@ -64,8 +68,9 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
if (view == null) { if (view == null) {
return return
} }
view.textSize = NeoTermPreference.loadInt(NeoTermPreference.KEY_FONT_SIZE, 30) view.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30)
view.setTypeface(Typeface.MONOSPACE) view.setTypeface(Typeface.MONOSPACE)
context.fullScreenToggleButton.setStatus(NeoPreference.loadBoolean(R.string.key_ui_fullscreen, false))
if (tab is TermTab) { if (tab is TermTab) {
val termTab = tab val termTab = tab

View File

@ -8,7 +8,7 @@ import android.view.inputmethod.InputMethodManager
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.customize.shortcut.ShortcutKeysManager import io.neoterm.customize.shortcut.ShortcutKeysManager
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoPreference
import io.neoterm.view.ExtraKeysView import io.neoterm.view.ExtraKeysView
import io.neoterm.view.TerminalView import io.neoterm.view.TerminalView
import io.neoterm.view.TerminalViewClient import io.neoterm.view.TerminalViewClient
@ -33,7 +33,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
val changedSize = (if (increase) 1 else -1) * 2 val changedSize = (if (increase) 1 else -1) * 2
val fontSize = termView!!.textSize + changedSize val fontSize = termView!!.textSize + changedSize
termView!!.textSize = fontSize termView!!.textSize = fontSize
NeoTermPreference.store(NeoTermPreference.KEY_FONT_SIZE, fontSize) NeoPreference.store(NeoPreference.KEY_FONT_SIZE, fontSize)
return 1.0f return 1.0f
} }
return scale return scale
@ -45,7 +45,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
} }
override fun shouldBackButtonBeMappedToEscape(): Boolean { override fun shouldBackButtonBeMappedToEscape(): Boolean {
return NeoTermPreference.loadBoolean(R.string.key_generaL_backspace_map_to_esc, false) return NeoPreference.loadBoolean(R.string.key_generaL_backspace_map_to_esc, false)
} }
override fun copyModeChanged(copyMode: Boolean) { override fun copyModeChanged(copyMode: Boolean) {
@ -118,7 +118,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
} }
fun removeSuggestions() { fun removeSuggestions() {
extraKeysView?.clearExternalButton() extraKeysView?.clearUserDefinedButton()
} }
} }

View File

@ -20,7 +20,8 @@
android:layout_height="@dimen/eks_height_two_line" android:layout_height="@dimen/eks_height_two_line"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:background="@color/terminal_background" android:background="@color/terminal_background"
android:orientation="horizontal" /> android:orientation="horizontal"
android:visibility="gone" />
<io.neoterm.view.TerminalView <io.neoterm.view.TerminalView
android:id="@+id/terminal_view" android:id="@+id/terminal_view"

View File

@ -33,9 +33,11 @@
<string name="ui_settings">界面设置</string> <string name="ui_settings">界面设置</string>
<string name="shell_not_found">Shell %s 未找到, 请先安装.</string> <string name="shell_not_found">Shell %s 未找到, 请先安装.</string>
<string name="installer_message">正在安装</string> <string name="installer_message">正在安装</string>
<string name="installer_install_zsh_manually">你可以通过执行以下命令来配置 zsh 和快捷提示 ~/install-zsh.sh</string>
<string name="fullscreen_mode_changed">全屏模式已改变,请重启 NeoTerm</string> <string name="fullscreen_mode_changed">全屏模式已改变,请重启 NeoTerm</string>
<string name="permission_denied">NeoTerm 无法取得必需的权限,正在退出</string> <string name="permission_denied">NeoTerm 无法取得必需的权限,正在退出</string>
<string name="error">还有这种操作?</string> <string name="error">还有这种操作?</string>
<string name="use_system_shell">使用系统Shell</string> <string name="use_system_shell">使用系统Shell</string>
<string name="retry">重试</string> <string name="retry">重试</string>
<string name="source_changed">APT 源已更改,你可能需要执行 apt update 来更新</string>
</resources> </resources>

View File

@ -14,6 +14,7 @@
<string name="package_settings">Package Settings</string> <string name="package_settings">Package Settings</string>
<string name="installer_message">Installing</string> <string name="installer_message">Installing</string>
<string name="installer_install_zsh_manually">You may install zsh and setup suggestions by executing: ~/install-zsh.sh</string>
<string name="pref_general_bell">Bell</string> <string name="pref_general_bell">Bell</string>
<string name="pref_general_bell_desc">Bell when receiving \'\\a\'</string> <string name="pref_general_bell_desc">Bell when receiving \'\\a\'</string>
@ -42,6 +43,7 @@
<string name="error">Error</string> <string name="error">Error</string>
<string name="use_system_shell">System Shell</string> <string name="use_system_shell">System Shell</string>
<string name="retry">Retry</string> <string name="retry">Retry</string>
<string name="source_changed">APT source changed, you may get it updated by executing: apt update</string>
<string-array name="pref_ui_color_scheme_entries" translatable="false"> <string-array name="pref_ui_color_scheme_entries" translatable="false">
<item>Default</item> <item>Default</item>

View File

@ -2,7 +2,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference <EditTextPreference
android:enabled="false" android:defaultValue="http://mirror.neoterm.studio"
android:key="@string/key_package_source" android:key="@string/key_package_source"
android:title="@string/pref_package_source" /> android:title="@string/pref_package_source" />
</PreferenceScreen> </PreferenceScreen>