Improve: Huge Details
This commit is contained in:
@ -55,6 +55,7 @@ dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
testCompile 'junit:junit:4.12'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
compile 'org.greenrobot:eventbus:3.0.0'
|
||||
compile project(':chrome-tabs')
|
||||
implementation 'com.android.support:design:25.3.1'
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ object NeoTermPath {
|
||||
const val EKS_PATH = "$USR_PATH/share/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"
|
||||
}
|
@ -15,7 +15,7 @@ object ShortcutConfigLoader {
|
||||
extraKeysView.loadDefaultUserDefinedExtraKeys()
|
||||
}
|
||||
for (button in config.shortcutKeys) {
|
||||
extraKeysView.addExternalButton(button)
|
||||
extraKeysView.addUserDefinedButton(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package io.neoterm.installer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
@ -10,7 +11,9 @@ import android.util.Pair;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@ -22,6 +25,7 @@ import java.util.zip.ZipInputStream;
|
||||
import io.neoterm.R;
|
||||
import io.neoterm.backend.EmulatorDebug;
|
||||
import io.neoterm.customize.NeoTermPath;
|
||||
import io.neoterm.utils.FileUtils;
|
||||
|
||||
public final class BaseFileInstaller {
|
||||
public interface ResultListener {
|
||||
@ -35,7 +39,11 @@ public final class BaseFileInstaller {
|
||||
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() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -47,13 +55,37 @@ public final class BaseFileInstaller {
|
||||
deleteFolder(STAGING_PREFIX_FILE);
|
||||
}
|
||||
|
||||
int totalBytes = 0;
|
||||
int totalReadBytes = 0;
|
||||
final byte[] buffer = new byte[8096];
|
||||
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
|
||||
|
||||
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;
|
||||
|
||||
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")) {
|
||||
BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput));
|
||||
String line;
|
||||
@ -77,9 +109,10 @@ public final class BaseFileInstaller {
|
||||
} else {
|
||||
try (FileOutputStream outStream = new FileOutputStream(targetFile)) {
|
||||
int readBytes;
|
||||
while ((readBytes = zipInput.read(buffer)) != -1)
|
||||
while ((readBytes = zipInput.read(buffer)) != -1) {
|
||||
outStream.write(buffer, 0, readBytes);
|
||||
}
|
||||
}
|
||||
if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) {
|
||||
//noinspection OctalInteger
|
||||
Os.chmod(targetFile.getAbsolutePath(), 0700);
|
||||
@ -89,6 +122,8 @@ public final class BaseFileInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
connection.disconnect();
|
||||
|
||||
if (symlinks.isEmpty())
|
||||
throw new RuntimeException("No SYMLINKS.txt encountered");
|
||||
for (Pair<String, String> symlink : symlinks) {
|
||||
@ -133,6 +168,26 @@ public final class BaseFileInstaller {
|
||||
}.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 {
|
||||
String archName = determineArchName();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,11 @@ package io.neoterm.preference
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import io.neoterm.R
|
||||
import io.neoterm.backend.TerminalSession
|
||||
import io.neoterm.customize.NeoTermPath
|
||||
import io.neoterm.services.NeoTermService
|
||||
import io.neoterm.utils.FileUtils
|
||||
import java.io.File
|
||||
|
||||
|
||||
@ -13,7 +15,7 @@ import java.io.File
|
||||
* @author kiva
|
||||
*/
|
||||
|
||||
object NeoTermPreference {
|
||||
object NeoPreference {
|
||||
const val KEY_FONT_SIZE = "neoterm_general_font_size"
|
||||
const val KEY_CURRENT_SESSION = "neoterm_service_current_session"
|
||||
|
||||
@ -23,6 +25,17 @@ object NeoTermPreference {
|
||||
fun init(context: Context) {
|
||||
this.context = 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() {
|
||||
@ -68,7 +81,7 @@ object NeoTermPreference {
|
||||
|
||||
fun storeCurrentSession(session: TerminalSession) {
|
||||
preference!!.edit()
|
||||
.putString(NeoTermPreference.KEY_CURRENT_SESSION, session.mHandle)
|
||||
.putString(NeoPreference.KEY_CURRENT_SESSION, session.mHandle)
|
||||
.apply()
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import io.neoterm.R
|
||||
import io.neoterm.backend.EmulatorDebug
|
||||
import io.neoterm.backend.TerminalSession
|
||||
import io.neoterm.customize.NeoTermPath
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.preference.NeoPreference
|
||||
import io.neoterm.ui.NeoTermActivity
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -83,7 +83,7 @@ class NeoTermService : Service() {
|
||||
executablePath = if (systemShell)
|
||||
"/system/bin/sh"
|
||||
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()) {
|
||||
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,
|
||||
env ?: NeoTermPreference.buildEnvironment(cwd, systemShell, executablePath),
|
||||
env ?: NeoPreference.buildEnvironment(cwd, systemShell, executablePath),
|
||||
sessionCallback)
|
||||
mTerminalSessions.add(session)
|
||||
updateNotification()
|
||||
|
@ -16,7 +16,6 @@ import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageButton
|
||||
import android.widget.Toast
|
||||
import de.mrapp.android.tabswitcher.*
|
||||
import io.neoterm.R
|
||||
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.installer.BaseFileInstaller
|
||||
import io.neoterm.preference.NeoPermission
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.preference.NeoPreference
|
||||
import io.neoterm.services.NeoTermService
|
||||
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 org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
|
||||
class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
lateinit var tabSwitcher: TabSwitcher
|
||||
lateinit var fullScreenToggleButton: StatedControlButton
|
||||
var systemShell = true
|
||||
var termService: NeoTermService? = null
|
||||
var restartRequired = false
|
||||
@ -43,16 +47,17 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
|
||||
NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION)
|
||||
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,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
|
||||
setContentView(R.layout.tab_main)
|
||||
|
||||
NeoTermFullScreen.injectActivity(this)
|
||||
FullScreenHelper.injectActivity(this, peekRecreating())
|
||||
.setKeyBoardListener({ isShow, _ ->
|
||||
var tab: TermTab? = null
|
||||
|
||||
@ -62,12 +67,24 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
|
||||
tab?.viewClient?.extraKeysView?.visibility = if (isShow) View.VISIBLE else View.GONE
|
||||
|
||||
if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false)
|
||||
|| NeoTermPreference.loadBoolean(R.string.key_ui_hide_toolbar, false)) {
|
||||
if (NeoPreference.loadBoolean(R.string.key_ui_fullscreen, false)
|
||||
|| NeoPreference.loadBoolean(R.string.key_ui_hide_toolbar, false)) {
|
||||
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.decorator = TermTabDecorator(this)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener())
|
||||
@ -92,9 +109,8 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
super.onResume()
|
||||
if (restartRequired) {
|
||||
restartRequired = false
|
||||
this.recreate()
|
||||
recreate()
|
||||
}
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.registerOnSharedPreferenceChangeListener(this)
|
||||
tabSwitcher.addListener(object : TabSwitcherListener {
|
||||
@ -123,7 +139,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
|
||||
override fun onSelectionChanged(tabSwitcher: TabSwitcher, selectedTabIndex: Int, selectedTab: Tab?) {
|
||||
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() {
|
||||
super.onDestroy()
|
||||
PreferenceManager.getDefaultSharedPreferences(this)
|
||||
@ -154,7 +180,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
termService = null
|
||||
}
|
||||
unbindService(this)
|
||||
NeoTermPreference.cleanup()
|
||||
NeoPreference.cleanup()
|
||||
}
|
||||
|
||||
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?) {
|
||||
if (termService != null) {
|
||||
finish()
|
||||
@ -227,14 +259,35 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRecreating()) {
|
||||
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?) {
|
||||
@ -299,7 +352,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
}
|
||||
|
||||
private fun getStoredCurrentSessionOrLast(): TerminalSession? {
|
||||
val stored = NeoTermPreference.getCurrentSession(termService)
|
||||
val stored = NeoPreference.getCurrentSession(termService)
|
||||
if (stored != null) return stored
|
||||
val numberOfSessions = termService!!.sessions.size
|
||||
if (numberOfSessions == 0) return null
|
||||
@ -340,24 +393,6 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
|
||||
private fun createTab(tabTitle: String?): Tab {
|
||||
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.parameters = Bundle()
|
||||
tab.setBackgroundColor(ContextCompat.getColor(this, R.color.tab_background_color))
|
||||
@ -408,4 +443,24 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,14 @@
|
||||
package io.neoterm.ui.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatPreferenceActivity
|
||||
import android.view.MenuItem
|
||||
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
|
||||
@ -15,6 +20,33 @@ class PackageSettingsActivity : AppCompatPreferenceActivity() {
|
||||
supportActionBar.title = getString(R.string.package_settings)
|
||||
supportActionBar.setDisplayHomeAsUpEnabled(true)
|
||||
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>?) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.neoterm.ui.settings
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatPreferenceActivity
|
||||
import android.view.MenuItem
|
||||
@ -16,7 +17,13 @@ class UISettingsActivity : AppCompatPreferenceActivity() {
|
||||
supportActionBar.setDisplayHomeAsUpEnabled(true)
|
||||
addPreferencesFromResource(R.xml.settings_ui)
|
||||
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
|
||||
})
|
||||
}
|
||||
|
@ -1,31 +1,37 @@
|
||||
package io.neoterm.utils
|
||||
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
object FileUtils {
|
||||
fun writeFile(path: File, bytes: ByteArray): Boolean {
|
||||
var output: OutputStream? = null
|
||||
var success = true
|
||||
try {
|
||||
output = FileOutputStream(path)
|
||||
output.write(bytes)
|
||||
output.flush()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
success = false
|
||||
} finally {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close()
|
||||
} catch (ignore: Exception) {
|
||||
return FileOutputStream(path).use {
|
||||
it.write(bytes)
|
||||
it.flush()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFile(path: File, inputStream: InputStream): Boolean {
|
||||
val bytes = ByteArray(inputStream.available())
|
||||
inputStream.read(bytes)
|
||||
return writeFile(path, bytes)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -9,11 +9,10 @@ import android.widget.FrameLayout;
|
||||
/**
|
||||
* 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
|
||||
* author @kiva
|
||||
*/
|
||||
public class NeoTermFullScreen {
|
||||
public static NeoTermFullScreen injectActivity(Activity activity) {
|
||||
return new NeoTermFullScreen(activity);
|
||||
public class FullScreenHelper {
|
||||
public static FullScreenHelper injectActivity(Activity activity, boolean isRecreating) {
|
||||
return new FullScreenHelper(activity, isRecreating);
|
||||
}
|
||||
|
||||
public interface KeyBoardListener {
|
||||
@ -33,12 +32,14 @@ public class NeoTermFullScreen {
|
||||
private int mOriginHeight;
|
||||
private int mPreHeight;
|
||||
private KeyBoardListener mKeyBoardListener;
|
||||
private boolean shouldSkipFirstTime;
|
||||
|
||||
public void setKeyBoardListener(KeyBoardListener 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);
|
||||
mChildOfContent = content.getChildAt(0);
|
||||
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@ -52,10 +53,12 @@ public class NeoTermFullScreen {
|
||||
|
||||
private void monitorImeStatus() {
|
||||
int currHeight = mChildOfContent.getHeight();
|
||||
if (currHeight == 0) {
|
||||
if (currHeight == 0 && shouldSkipFirstTime) {
|
||||
// First time
|
||||
return;
|
||||
}
|
||||
|
||||
shouldSkipFirstTime = false;
|
||||
boolean hasChange = false;
|
||||
if (mPreHeight == 0) {
|
||||
mPreHeight = currHeight;
|
@ -50,7 +50,7 @@ public final class ExtraKeysView extends LinearLayout {
|
||||
"define | false\n";
|
||||
|
||||
public static final int NORMAL_TEXT_COLOR = 0xFFFFFFFF;
|
||||
|
||||
public static final int SELECTED_TEXT_COLOR = 0xFF80DEEA;
|
||||
|
||||
private List<ExtraButton> builtinExtraKeys;
|
||||
private List<ExtraButton> userDefinedExtraKeys;
|
||||
@ -85,7 +85,6 @@ public final class ExtraKeysView extends LinearLayout {
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
public boolean readControlButton() {
|
||||
return CTRL.readState();
|
||||
}
|
||||
@ -94,11 +93,21 @@ public final class ExtraKeysView extends LinearLayout {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addExternalButton(ExtraButton button) {
|
||||
userDefinedExtraKeys.add(button);
|
||||
public void addUserDefinedButton(ExtraButton 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();
|
||||
}
|
||||
|
||||
@ -109,7 +118,7 @@ public final class ExtraKeysView extends LinearLayout {
|
||||
generateDefaultFile(defaultFile);
|
||||
}
|
||||
|
||||
clearExternalButton();
|
||||
clearUserDefinedButton();
|
||||
try {
|
||||
ShortcutConfigParser parser = new ShortcutConfigParser();
|
||||
parser.setInput(defaultFile);
|
||||
@ -153,6 +162,10 @@ public final class ExtraKeysView extends LinearLayout {
|
||||
StatedControlButton btn = ((StatedControlButton) extraButton);
|
||||
button = btn.toggleButton = new ToggleButton(getContext(), null, android.R.attr.buttonBarButtonStyle);
|
||||
button.setClickable(true);
|
||||
if (btn.initState) {
|
||||
btn.toggleButton.setChecked(true);
|
||||
btn.toggleButton.setTextColor(SELECTED_TEXT_COLOR);
|
||||
}
|
||||
} else {
|
||||
button = new Button(getContext(), null, android.R.attr.buttonBarButtonStyle);
|
||||
}
|
||||
|
@ -11,15 +11,25 @@ import io.neoterm.view.ExtraKeysView;
|
||||
|
||||
public class StatedControlButton extends ControlButton {
|
||||
public ToggleButton toggleButton;
|
||||
public boolean initState;
|
||||
|
||||
public StatedControlButton(String text, boolean initState) {
|
||||
super(text);
|
||||
this.initState = initState;
|
||||
}
|
||||
|
||||
public StatedControlButton(String text) {
|
||||
super(text);
|
||||
this(text, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
toggleButton.setChecked(toggleButton.isChecked());
|
||||
toggleButton.setTextColor(toggleButton.isChecked() ? 0xFF80DEEA : ExtraKeysView.NORMAL_TEXT_COLOR);
|
||||
setStatus(toggleButton.isChecked());
|
||||
}
|
||||
|
||||
public void setStatus(boolean status) {
|
||||
toggleButton.setChecked(status);
|
||||
toggleButton.setTextColor(status ? ExtraKeysView.SELECTED_TEXT_COLOR : ExtraKeysView.NORMAL_TEXT_COLOR);
|
||||
}
|
||||
|
||||
public boolean readState() {
|
||||
|
@ -1,10 +0,0 @@
|
||||
package io.neoterm.view.tab
|
||||
|
||||
import de.mrapp.android.tabswitcher.Tab
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
interface CloseTabProvider {
|
||||
fun closeTab(tab: Tab)
|
||||
}
|
7
app/src/main/java/io/neoterm/view/tab/TabCloseEvent.kt
Normal file
7
app/src/main/java/io/neoterm/view/tab/TabCloseEvent.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package io.neoterm.view.tab
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
|
||||
class TabCloseEvent(var termTab: TermTab)
|
@ -7,8 +7,7 @@ import android.media.SoundPool
|
||||
import android.os.Vibrator
|
||||
import io.neoterm.R
|
||||
import io.neoterm.backend.TerminalSession
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.view.ExtraKeysView
|
||||
import io.neoterm.preference.NeoPreference
|
||||
import io.neoterm.view.TerminalView
|
||||
|
||||
/**
|
||||
@ -47,7 +46,7 @@ class TermSessionChangedCallback : TerminalSession.SessionChangedCallback {
|
||||
return
|
||||
}
|
||||
|
||||
if (NeoTermPreference.loadBoolean(R.string.key_general_bell, false)) {
|
||||
if (NeoPreference.loadBoolean(R.string.key_general_bell, false)) {
|
||||
if (soundPool == null) {
|
||||
soundPool = SoundPool.Builder().setMaxStreams(1).build()
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
vibrator.vibrate(100)
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package io.neoterm.view.tab
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import de.mrapp.android.tabswitcher.Tab
|
||||
import io.neoterm.R
|
||||
import io.neoterm.backend.TerminalSession
|
||||
import io.neoterm.customize.color.NeoTermColorScheme
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.preference.NeoPreference
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
@ -18,8 +21,6 @@ class TermTab(title: CharSequence) : Tab(title) {
|
||||
var viewClient: TermViewClient? = null
|
||||
var toolbar: Toolbar? = null
|
||||
|
||||
var closeTabProvider: CloseTabProvider? = null
|
||||
|
||||
fun changeColorScheme(colorScheme: NeoTermColorScheme?) {
|
||||
colorScheme?.apply()
|
||||
viewClient?.extraKeysView?.setBackgroundColor(Color.parseColor(colorScheme?.background))
|
||||
@ -31,7 +32,6 @@ class TermTab(title: CharSequence) : Tab(title) {
|
||||
viewClient?.extraKeysView = null
|
||||
sessionCallback?.termView = null
|
||||
sessionCallback?.termTab = null
|
||||
closeTabProvider = null
|
||||
toolbar = null
|
||||
termSession = null
|
||||
}
|
||||
@ -39,7 +39,7 @@ class TermTab(title: CharSequence) : Tab(title) {
|
||||
fun updateTitle(title: String) {
|
||||
this.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)
|
||||
} else {
|
||||
viewClient?.removeSuggestions()
|
||||
@ -51,6 +51,17 @@ class TermTab(title: CharSequence) : Tab(title) {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import de.mrapp.android.tabswitcher.Tab
|
||||
import de.mrapp.android.tabswitcher.TabSwitcher
|
||||
import de.mrapp.android.tabswitcher.TabSwitcherDecorator
|
||||
import io.neoterm.R
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.preference.NeoPreference
|
||||
import io.neoterm.ui.NeoTermActivity
|
||||
import io.neoterm.view.ExtraKeysView
|
||||
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 {
|
||||
val view = inflater.inflate(R.layout.term, parent, false)
|
||||
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())
|
||||
val menu = toolbar.menu
|
||||
TabSwitcher.setupWithMenu(context.tabSwitcher, menu, {
|
||||
@ -64,8 +68,9 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
|
||||
if (view == null) {
|
||||
return
|
||||
}
|
||||
view.textSize = NeoTermPreference.loadInt(NeoTermPreference.KEY_FONT_SIZE, 30)
|
||||
view.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30)
|
||||
view.setTypeface(Typeface.MONOSPACE)
|
||||
context.fullScreenToggleButton.setStatus(NeoPreference.loadBoolean(R.string.key_ui_fullscreen, false))
|
||||
|
||||
if (tab is TermTab) {
|
||||
val termTab = tab
|
||||
|
@ -8,7 +8,7 @@ import android.view.inputmethod.InputMethodManager
|
||||
import io.neoterm.R
|
||||
import io.neoterm.backend.TerminalSession
|
||||
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.TerminalView
|
||||
import io.neoterm.view.TerminalViewClient
|
||||
@ -33,7 +33,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
|
||||
val changedSize = (if (increase) 1 else -1) * 2
|
||||
val fontSize = termView!!.textSize + changedSize
|
||||
termView!!.textSize = fontSize
|
||||
NeoTermPreference.store(NeoTermPreference.KEY_FONT_SIZE, fontSize)
|
||||
NeoPreference.store(NeoPreference.KEY_FONT_SIZE, fontSize)
|
||||
return 1.0f
|
||||
}
|
||||
return scale
|
||||
@ -45,7 +45,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -118,7 +118,7 @@ class TermViewClient(val context: Context) : TerminalViewClient {
|
||||
}
|
||||
|
||||
fun removeSuggestions() {
|
||||
extraKeysView?.clearExternalButton()
|
||||
extraKeysView?.clearUserDefinedButton()
|
||||
}
|
||||
|
||||
}
|
@ -20,7 +20,8 @@
|
||||
android:layout_height="@dimen/eks_height_two_line"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@color/terminal_background"
|
||||
android:orientation="horizontal" />
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone" />
|
||||
|
||||
<io.neoterm.view.TerminalView
|
||||
android:id="@+id/terminal_view"
|
||||
|
@ -33,9 +33,11 @@
|
||||
<string name="ui_settings">界面设置</string>
|
||||
<string name="shell_not_found">Shell %s 未找到, 请先安装.</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="permission_denied">NeoTerm 无法取得必需的权限,正在退出</string>
|
||||
<string name="error">还有这种操作?</string>
|
||||
<string name="use_system_shell">使用系统Shell</string>
|
||||
<string name="retry">重试</string>
|
||||
<string name="source_changed">APT 源已更改,你可能需要执行 apt update 来更新</string>
|
||||
</resources>
|
@ -14,6 +14,7 @@
|
||||
<string name="package_settings">Package Settings</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_desc">Bell when receiving \'\\a\'</string>
|
||||
@ -42,6 +43,7 @@
|
||||
<string name="error">Error</string>
|
||||
<string name="use_system_shell">System Shell</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">
|
||||
<item>Default</item>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<EditTextPreference
|
||||
android:enabled="false"
|
||||
android:defaultValue="http://mirror.neoterm.studio"
|
||||
android:key="@string/key_package_source"
|
||||
android:title="@string/pref_package_source" />
|
||||
</PreferenceScreen>
|
Reference in New Issue
Block a user