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"
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'
}

View File

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

View File

@ -15,7 +15,7 @@ object ShortcutConfigLoader {
extraKeysView.loadDefaultUserDefinedExtraKeys()
}
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.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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.
* 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;

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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