From c9d59c643f3b12b6a2bc0774b5f4c0774130627a Mon Sep 17 00:00:00 2001 From: zt515 Date: Tue, 18 Jul 2017 00:08:08 +0800 Subject: [PATCH] AutoComplete: Popup version. --- .../completion/AutoCompleteManager.kt | 37 ++++++ .../customize/completion/CompleteCandidate.kt | 9 ++ .../io/neoterm/preference/NeoPreference.kt | 2 +- .../io/neoterm/services/NeoTermService.kt | 13 +- .../neoterm/ui/pm/adapter/PackageAdapter.kt | 2 +- .../io/neoterm/ui/term/NeoTermActivity.kt | 3 + .../ui/term/tab/TermCompleteListener.kt | 93 ++++++++++++++ .../java/io/neoterm/ui/term/tab/TermTab.kt | 11 ++ .../neoterm/ui/term/tab/TermTabDecorator.kt | 17 ++- .../java/io/neoterm/utils/TerminalUtils.kt | 3 +- .../neoterm/view/AutoCompletePopupWindow.kt | 117 ++++++++++++++++++ .../neoterm/view/OnAutoCompleteListener.java | 13 ++ .../java/io/neoterm/view/TerminalDialog.kt | 2 +- .../io/neoterm/view/TerminalRenderer.java | 14 +++ .../java/io/neoterm/view/TerminalView.java | 42 ++++++- .../io/neoterm/view/TerminalViewClient.java | 2 +- .../res/layout/item_complete_candidate.xml | 31 +++++ .../{package_item.xml => item_package.xml} | 0 .../main/res/layout/popup_auto_complete.xml | 13 ++ app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimens.xml | 2 + app/src/main/res/values/strings.xml | 1 + .../android/tabswitcher/TabSwitcher.java | 4 +- 24 files changed, 416 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/io/neoterm/customize/completion/AutoCompleteManager.kt create mode 100644 app/src/main/java/io/neoterm/customize/completion/CompleteCandidate.kt create mode 100644 app/src/main/java/io/neoterm/ui/term/tab/TermCompleteListener.kt create mode 100644 app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt create mode 100755 app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java create mode 100644 app/src/main/res/layout/item_complete_candidate.xml rename app/src/main/res/layout/{package_item.xml => item_package.xml} (100%) create mode 100644 app/src/main/res/layout/popup_auto_complete.xml diff --git a/app/src/main/java/io/neoterm/customize/completion/AutoCompleteManager.kt b/app/src/main/java/io/neoterm/customize/completion/AutoCompleteManager.kt new file mode 100644 index 0000000..792c909 --- /dev/null +++ b/app/src/main/java/io/neoterm/customize/completion/AutoCompleteManager.kt @@ -0,0 +1,37 @@ +package io.neoterm.customize.completion + +/** + * @author kiva + */ +object AutoCompleteManager { + private val completeCandidates = mutableListOf() + + init { + val programs = arrayOf("ls", "clean", "exit", "apt", "neoterm-normalize-binary", "less", "ln", "lsof") + val desc = arrayOf("List files and directories", + "Clear screen", + "Exit current shell", + "Installing, Updating, Upgrading packages", + "Fix program error caused by linux shebang", + "View files", + "Create symlinks or hardlinks", + "List opened files") + for (i in programs.indices) { + val candidate = CompleteCandidate(programs[i]) + candidate.description = desc[i] + completeCandidates.add(candidate) + } + } + + fun filter(text: String) : List { + val result = mutableListOf() + if (text.isNotEmpty()) { + completeCandidates.forEach { + if (it.completeString.startsWith(text, ignoreCase = true)) { + result.add(it) + } + } + } + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/customize/completion/CompleteCandidate.kt b/app/src/main/java/io/neoterm/customize/completion/CompleteCandidate.kt new file mode 100644 index 0000000..38a83bf --- /dev/null +++ b/app/src/main/java/io/neoterm/customize/completion/CompleteCandidate.kt @@ -0,0 +1,9 @@ +package io.neoterm.customize.completion + +/** + * @author kiva + */ +class CompleteCandidate(var completeString: String) { + var displayName: String = completeString + var description: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/preference/NeoPreference.kt b/app/src/main/java/io/neoterm/preference/NeoPreference.kt index ffafbfb..c355490 100644 --- a/app/src/main/java/io/neoterm/preference/NeoPreference.kt +++ b/app/src/main/java/io/neoterm/preference/NeoPreference.kt @@ -25,7 +25,7 @@ object NeoPreference { const val VALUE_NEOTERM_FIRST = "NeoTermFirst" const val VALUE_SYSTEM_FIRST = "SystemFirst" - var preference: SharedPreferences? = null + private var preference: SharedPreferences? = null fun init(context: Context) { preference = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/io/neoterm/services/NeoTermService.kt b/app/src/main/java/io/neoterm/services/NeoTermService.kt index adc142b..3540ae5 100644 --- a/app/src/main/java/io/neoterm/services/NeoTermService.kt +++ b/app/src/main/java/io/neoterm/services/NeoTermService.kt @@ -56,8 +56,6 @@ class NeoTermService : Service() { ACTION_ACQUIRE_LOCK -> acquireLock() ACTION_RELEASE_LOCK -> releaseLock() - - null -> Log.e(EmulatorDebug.LOG_TAG, "Unknown NeoTermService action: '$action'") } if (flags and Service.START_FLAG_REDELIVERY == 0) { @@ -124,14 +122,15 @@ class NeoTermService : Service() { builder.setPriority(if (lockAcquired) Notification.PRIORITY_HIGH else Notification.PRIORITY_MIN) val exitIntent = Intent(this, NeoTermService::class.java).setAction(ACTION_SERVICE_STOP) - builder.addAction(android.R.drawable.ic_delete, "Exit", PendingIntent.getService(this, 0, exitIntent, 0)) + builder.addAction(android.R.drawable.ic_delete, getString(R.string.exit), PendingIntent.getService(this, 0, exitIntent, 0)) val newWakeAction = if (lockAcquired) ACTION_RELEASE_LOCK else ACTION_ACQUIRE_LOCK val toggleWakeLockIntent = Intent(this, NeoTermService::class.java).setAction(newWakeAction) - val actionTitle = getString(if (lockAcquired) - R.string.service_release_lock - else - R.string.service_acquire_lock) + val actionTitle = getString( + if (lockAcquired) + R.string.service_release_lock + else + R.string.service_acquire_lock) val actionIcon = if (lockAcquired) android.R.drawable.ic_lock_idle_lock else android.R.drawable.ic_lock_lock builder.addAction(actionIcon, actionTitle, PendingIntent.getService(this, 0, toggleWakeLockIntent, 0)) diff --git a/app/src/main/java/io/neoterm/ui/pm/adapter/PackageAdapter.kt b/app/src/main/java/io/neoterm/ui/pm/adapter/PackageAdapter.kt index 1763276..3f4ac4b 100755 --- a/app/src/main/java/io/neoterm/ui/pm/adapter/PackageAdapter.kt +++ b/app/src/main/java/io/neoterm/ui/pm/adapter/PackageAdapter.kt @@ -21,7 +21,7 @@ class PackageAdapter(context: Context, comparator: Comparator, pri } override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): SortedListAdapter.ViewHolder { - val rootView = inflater.inflate(R.layout.package_item, parent, false) + val rootView = inflater.inflate(R.layout.item_package, parent, false) return PackageViewHolder(rootView, listener) } } diff --git a/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt b/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt index c74f0a1..dac0de6 100644 --- a/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt +++ b/app/src/main/java/io/neoterm/ui/term/NeoTermActivity.kt @@ -6,6 +6,7 @@ import android.app.Activity import android.app.AlertDialog import android.content.* import android.content.pm.PackageManager +import android.graphics.Rect import android.os.Bundle import android.os.IBinder import android.preference.PreferenceManager @@ -77,6 +78,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference } setContentView(R.layout.ui_main) + toolbar = findViewById(R.id.terminal_toolbar) as Toolbar setSupportActionBar(toolbar) @@ -361,6 +363,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference if (tabSwitcher.selectedTab is TermTab) { val tab = tabSwitcher.selectedTab as TermTab tab.requireHideIme() + tab.onFullScreenModeChanged(fullScreen) } NeoPreference.store(R.string.key_ui_fullscreen, fullScreen) this@NeoTermActivity.recreate() diff --git a/app/src/main/java/io/neoterm/ui/term/tab/TermCompleteListener.kt b/app/src/main/java/io/neoterm/ui/term/tab/TermCompleteListener.kt new file mode 100644 index 0000000..cf922b1 --- /dev/null +++ b/app/src/main/java/io/neoterm/ui/term/tab/TermCompleteListener.kt @@ -0,0 +1,93 @@ +package io.neoterm.ui.term.tab + +import android.util.Log +import android.view.KeyEvent +import io.neoterm.customize.completion.AutoCompleteManager +import io.neoterm.customize.completion.CompleteCandidate +import io.neoterm.view.AutoCompletePopupWindow +import io.neoterm.view.OnAutoCompleteListener +import io.neoterm.view.TerminalView +import java.util.* + +/** + * @author kiva + */ +class TermCompleteListener(var terminalView: TerminalView?) : OnAutoCompleteListener { + + private val inputStack = Stack() + private val popupWindow = AutoCompletePopupWindow(terminalView!!.context) + + override fun onKeyCode(keyCode: Int, keyMod: Int) { + when (keyCode) { + KeyEvent.KEYCODE_DEL -> { + Log.e("NeoTerm-AC", "BackSpace") + popChar() + activateAutoCompletion() + } + + KeyEvent.KEYCODE_ENTER -> { + Log.e("NeoTerm-AC", "Clear Chars") + clearChars() + popupWindow.dismiss() + } + } + } + + override fun onAutoComplete(newText: String?) { + if (newText == null || newText.isEmpty()) { + return + } + newText.toCharArray().forEach { pushChar(it) } + activateAutoCompletion() + } + + override fun onCleanUp() { + popupWindow.cleanup() + terminalView = null + } + + private fun activateAutoCompletion() { + val text = getCurrentEditingText() + if (text.isEmpty()) { + return + } + + val candidates = AutoCompleteManager.filter(text) + Log.e("NeoTerm-AC", "Completing for $text") + candidates.forEach { + Log.e("NeoTerm-AC", " Candidate: ${it.completeString}") + } + if (candidates.isNotEmpty()) { + showAutoCompleteCandidates(candidates) + } + } + + private fun showAutoCompleteCandidates(candidates: List) { + popupWindow.candidates = candidates + popupWindow.show(terminalView!!) + } + + private fun getCurrentEditingText(): String { + val builder = StringBuilder() + val size = inputStack.size + (0..(size - 1)) + .map { inputStack[it] } + .takeWhile { !(it == 0.toChar() || it == ' ') } + .forEach { builder.append(it) } + return builder.toString() + } + + private fun clearChars() { + inputStack.clear() + } + + private fun popChar() { + if (inputStack.isNotEmpty()) { + inputStack.pop() + } + } + + private fun pushChar(char: Char) { + inputStack.push(char) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/ui/term/tab/TermTab.kt b/app/src/main/java/io/neoterm/ui/term/tab/TermTab.kt index 7a4f155..4db14fb 100644 --- a/app/src/main/java/io/neoterm/ui/term/tab/TermTab.kt +++ b/app/src/main/java/io/neoterm/ui/term/tab/TermTab.kt @@ -11,6 +11,7 @@ import io.neoterm.preference.NeoPreference import io.neoterm.ui.term.tab.event.TabCloseEvent import io.neoterm.ui.term.tab.event.TitleChangedEvent import io.neoterm.ui.term.tab.event.ToggleFullScreenEvent +import io.neoterm.view.OnAutoCompleteListener import org.greenrobot.eventbus.EventBus /** @@ -21,6 +22,7 @@ class TermTab(title: CharSequence) : Tab(title) { var termSession: TerminalSession? = null var sessionCallback: TermSessionChangedCallback? = null var viewClient: TermViewClient? = null + var onAutoCompleteListener: OnAutoCompleteListener? = null var toolbar: Toolbar? = null fun updateColorScheme() { @@ -29,6 +31,9 @@ class TermTab(title: CharSequence) : Tab(title) { } fun cleanup() { + onAutoCompleteListener?.onCleanUp() + onAutoCompleteListener = null + viewClient?.termTab = null viewClient?.termView = null viewClient?.extraKeysView = null @@ -54,6 +59,12 @@ class TermTab(title: CharSequence) : Tab(title) { viewClient?.sessionFinished = true } + fun onFullScreenModeChanged(fullScreen: Boolean) { + // Window token changed, we need to recreate PopupWindow + onAutoCompleteListener?.onCleanUp() + onAutoCompleteListener = null + } + fun requireCloseTab() { requireHideIme() EventBus.getDefault().post(TabCloseEvent(this)) diff --git a/app/src/main/java/io/neoterm/ui/term/tab/TermTabDecorator.kt b/app/src/main/java/io/neoterm/ui/term/tab/TermTabDecorator.kt index bb95ba7..63eeccf 100644 --- a/app/src/main/java/io/neoterm/ui/term/tab/TermTabDecorator.kt +++ b/app/src/main/java/io/neoterm/ui/term/tab/TermTabDecorator.kt @@ -8,12 +8,14 @@ import android.view.ViewGroup import de.mrapp.android.tabswitcher.Tab import de.mrapp.android.tabswitcher.TabSwitcher import de.mrapp.android.tabswitcher.TabSwitcherDecorator +import io.neoterm.BuildConfig import io.neoterm.R import io.neoterm.customize.color.ColorSchemeManager import io.neoterm.preference.NeoPreference import io.neoterm.ui.term.NeoTermActivity import io.neoterm.utils.TerminalUtils import io.neoterm.view.ExtraKeysView +import io.neoterm.view.OnAutoCompleteListener import io.neoterm.view.TerminalView /** @@ -72,11 +74,24 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() { termTab.viewClient?.updateSuggestions(termTab.termSession?.title, true) } - view.setOnKeyListener(termTab.viewClient) + view.setTerminalViewClient(termTab.viewClient) view.attachSession(termTab.termSession) + + // Still in progress + // Only available for developers. + if (BuildConfig.DEBUG) { + if (termTab.onAutoCompleteListener == null) { + termTab.onAutoCompleteListener = createAutoCompleteListener(view) + } + view.onAutoCompleteListener = termTab.onAutoCompleteListener + } } } + private fun createAutoCompleteListener(view: TerminalView): OnAutoCompleteListener? { + return TermCompleteListener(view) + } + override fun getViewTypeCount(): Int { return 1 } diff --git a/app/src/main/java/io/neoterm/utils/TerminalUtils.kt b/app/src/main/java/io/neoterm/utils/TerminalUtils.kt index 92b80aa..6590738 100644 --- a/app/src/main/java/io/neoterm/utils/TerminalUtils.kt +++ b/app/src/main/java/io/neoterm/utils/TerminalUtils.kt @@ -4,7 +4,6 @@ import android.content.Context import android.widget.Toast import io.neoterm.R import io.neoterm.backend.TerminalSession -import io.neoterm.customize.color.ColorSchemeManager import io.neoterm.preference.NeoTermPath import io.neoterm.customize.font.FontManager import io.neoterm.preference.NeoPreference @@ -21,7 +20,7 @@ object TerminalUtils { terminalView?.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30) terminalView?.setTypeface(FontManager.getCurrentFont().getTypeFace()) if (terminalViewClient != null) { - terminalView?.setOnKeyListener(terminalViewClient) + terminalView?.setTerminalViewClient(terminalViewClient) } } diff --git a/app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt b/app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt new file mode 100644 index 0000000..4d420ab --- /dev/null +++ b/app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt @@ -0,0 +1,117 @@ +package io.neoterm.view + +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.ListView +import android.widget.PopupWindow +import android.widget.TextView +import io.neoterm.R +import io.neoterm.backend.TerminalColors +import io.neoterm.customize.color.ColorSchemeManager +import io.neoterm.customize.completion.CompleteCandidate + +/** + * @author kiva + */ +class AutoCompletePopupWindow(val context: Context) { + var candidates: List? = null + var popupWindow: PopupWindow? = null + var wantsToFinish = false + var candidateAdapter: CandidateAdapter? = null + + fun show(terminalView: TerminalView) { + if (popupWindow == null && !wantsToFinish) { + popupWindow = createPopupWindow() + } + + candidateAdapter?.notifyDataSetChanged() + if (!(popupWindow?.isShowing ?: false)) { + popupWindow?.showAtLocation(terminalView, Gravity.BOTTOM.and(Gravity.START), + terminalView.cursorAbsX, + terminalView.cursorAbsY) + } + } + + fun dismiss() { + popupWindow?.dismiss() + } + + private fun createPopupWindow(): PopupWindow { + val popupWindow = PopupWindow(context) + popupWindow.isOutsideTouchable = true + popupWindow.isTouchable = true + val contentView = LayoutInflater.from(context).inflate(R.layout.popup_auto_complete, null, false) + val candidateListView = contentView.findViewById(R.id.popup_complete_candidate_list) as ListView + candidateAdapter = CandidateAdapter(this) + candidateListView.adapter = candidateAdapter + + popupWindow.contentView = contentView + return popupWindow + } + + fun cleanup() { + wantsToFinish = true + popupWindow = null + candidateAdapter = null + candidates = null + } + + class CandidateAdapter(val autoCompletePopupWindow: AutoCompletePopupWindow) : BaseAdapter() { + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + var convertView = convertView + val viewHolder: CandidateViewHolder = + if (convertView != null) { + convertView.tag as CandidateViewHolder + } else { + convertView = LayoutInflater.from(autoCompletePopupWindow.context) + .inflate(R.layout.item_complete_candidate, null, false) + val viewHolder = CandidateViewHolder(convertView) + convertView.tag = viewHolder + viewHolder + } + + val candidate = getItem(position) as CompleteCandidate + viewHolder.apply { + display.text = candidate.displayName + if (candidate.description != null) { + splitView.visibility = View.VISIBLE + description.visibility = View.VISIBLE + description.text = candidate.description + } else { + splitView.visibility = View.GONE + description.visibility = View.GONE + } + } + return convertView!! + } + + override fun getItem(position: Int): Any? { + return autoCompletePopupWindow.candidates?.get(position) + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getCount(): Int { + return autoCompletePopupWindow.candidates?.size ?: 0 + } + } + + class CandidateViewHolder(rootView: View) { + val display: TextView = rootView.findViewById(R.id.complete_display) as TextView + val description: TextView = rootView.findViewById(R.id.complete_description) as TextView + val splitView: View = rootView.findViewById(R.id.complete_split) + + init { + val colorScheme = ColorSchemeManager.getCurrentColorScheme() + val textColor = TerminalColors.parse(colorScheme.foregroundColor) + display.setTextColor(textColor) + description.setTextColor(textColor) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java b/app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java new file mode 100755 index 0000000..ab8d678 --- /dev/null +++ b/app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java @@ -0,0 +1,13 @@ +package io.neoterm.view; + +/** + * @author Kiva + * @version 1.0 + */ +public interface OnAutoCompleteListener { + void onAutoComplete(String newText); + + void onKeyCode(int keyCode, int keyMod); + + void onCleanUp(); +} diff --git a/app/src/main/java/io/neoterm/view/TerminalDialog.kt b/app/src/main/java/io/neoterm/view/TerminalDialog.kt index d8f1065..67deaa3 100644 --- a/app/src/main/java/io/neoterm/view/TerminalDialog.kt +++ b/app/src/main/java/io/neoterm/view/TerminalDialog.kt @@ -34,7 +34,7 @@ class TerminalDialog(val context: Context) { terminalViewClient = BasicViewClient(terminalView) TerminalUtils.setupTerminalView(terminalView, terminalViewClient) - terminalView.setOnKeyListener(terminalViewClient) + terminalView.setTerminalViewClient(terminalViewClient) terminalSessionCallback = object : BasicSessionCallback(terminalView) { override fun onSessionFinished(finishedSession: TerminalSession?) { sessionFinishedCallback?.onSessionFinished(this@TerminalDialog, finishedSession) diff --git a/app/src/main/java/io/neoterm/view/TerminalRenderer.java b/app/src/main/java/io/neoterm/view/TerminalRenderer.java index 561a7be..79b2eb8 100755 --- a/app/src/main/java/io/neoterm/view/TerminalRenderer.java +++ b/app/src/main/java/io/neoterm/view/TerminalRenderer.java @@ -31,6 +31,10 @@ final class TerminalRenderer { /** The {@link #mFontLineSpacing} + {@link #mFontAscent}. */ final int mFontLineSpacingAndAscent; + /** AutoCompletion PopupWindow need them to show popup window */ + protected float savedLastDrawnLineX; + protected float savedLastDrawnLineY; + private final float[] asciiMeasures = new float[127]; public TerminalRenderer(int textSize, Typeface typeface) { @@ -200,6 +204,8 @@ final class TerminalRenderer { if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.; else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.; canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint); + savedLastDrawnLineX = left; + savedLastDrawnLineY = y; } if ((effect & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) == 0) { @@ -227,4 +233,12 @@ final class TerminalRenderer { if (savedMatrix) canvas.restore(); } + + float getCursorX() { + return savedLastDrawnLineX; + } + + float getCursorY() { + return savedLastDrawnLineY; + } } diff --git a/app/src/main/java/io/neoterm/view/TerminalView.java b/app/src/main/java/io/neoterm/view/TerminalView.java index d763172..1dacf6b 100755 --- a/app/src/main/java/io/neoterm/view/TerminalView.java +++ b/app/src/main/java/io/neoterm/view/TerminalView.java @@ -229,11 +229,11 @@ public final class TerminalView extends View { } /** - * @param onKeyListener Listener for all kinds of key events, both hardware and IME (which makes it different from that + * @param client Listener for all kinds of key events, both hardware and IME (which makes it different from that * available with {@link View#setOnKeyListener(OnKeyListener)}. */ - public void setOnKeyListener(TerminalViewClient onKeyListener) { - this.mClient = onKeyListener; + public void setTerminalViewClient(TerminalViewClient client) { + this.mClient = client; } /** @@ -296,6 +296,9 @@ public final class TerminalView extends View { Editable content = getEditable(); sendTextToTerminal(content); + if (onAutoCompleteListener != null) { + onAutoCompleteListener.onAutoComplete(content.toString()); + } content.clear(); return true; } @@ -678,6 +681,16 @@ public final class TerminalView extends View { if (mCombiningAccent != oldCombiningAccent) invalidate(); + if (onAutoCompleteListener != null) { + if (event.isPrintingKey()) { + char printingChar = (char) event.getUnicodeChar(metaState); + if (printingChar != '\b') { + // ASCII chars + onAutoCompleteListener.onAutoComplete(new String(new char[]{printingChar})); + } + } + } + return true; } @@ -745,6 +758,9 @@ public final class TerminalView extends View { String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode()); if (code == null) return false; mTermSession.write(code); + if (onAutoCompleteListener != null) { + onAutoCompleteListener.onKeyCode(keyCode, keyMod); + } return true; } @@ -960,4 +976,24 @@ public final class TerminalView extends View { return mTermSession; } + + private OnAutoCompleteListener onAutoCompleteListener; + + public OnAutoCompleteListener getOnAutoCompleteListener() { + return onAutoCompleteListener; + } + + public void setOnAutoCompleteListener(OnAutoCompleteListener onAutoCompleteListener) { + this.onAutoCompleteListener = onAutoCompleteListener; + } + + public int getCursorAbsX() { + return (int) mRenderer.getCursorX(); + } + + public int getCursorAbsY() { + int[] locations = new int[2]; + getLocationOnScreen(locations); + return (int) (mRenderer.getCursorY() + locations[1]); + } } diff --git a/app/src/main/java/io/neoterm/view/TerminalViewClient.java b/app/src/main/java/io/neoterm/view/TerminalViewClient.java index 3b96bcc..cb92cd5 100755 --- a/app/src/main/java/io/neoterm/view/TerminalViewClient.java +++ b/app/src/main/java/io/neoterm/view/TerminalViewClient.java @@ -8,7 +8,7 @@ import io.neoterm.backend.TerminalSession; /** * Input and scale listener which may be set on a {@link TerminalView} through - * {@link TerminalView#setOnKeyListener(TerminalViewClient)}. + * {@link TerminalView#setTerminalViewClient(TerminalViewClient)}. *

*/ public interface TerminalViewClient { diff --git a/app/src/main/res/layout/item_complete_candidate.xml b/app/src/main/res/layout/item_complete_candidate.xml new file mode 100644 index 0000000..3d671f9 --- /dev/null +++ b/app/src/main/res/layout/item_complete_candidate.xml @@ -0,0 +1,31 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/package_item.xml b/app/src/main/res/layout/item_package.xml similarity index 100% rename from app/src/main/res/layout/package_item.xml rename to app/src/main/res/layout/item_package.xml diff --git a/app/src/main/res/layout/popup_auto_complete.xml b/app/src/main/res/layout/popup_auto_complete.xml new file mode 100644 index 0000000..3b3cbaa --- /dev/null +++ b/app/src/main/res/layout/popup_auto_complete.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d0c8629..82ad3dc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -81,4 +81,5 @@ (永不休眠) 取得休眠锁 释放休眠锁 + 退出 \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9c69725..b69b0fb 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,6 +15,8 @@ #ef0276 #a7f56e #11c612 + #7f000000 + #efefef @color/roseStart diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 1cd6f6a..0db023f 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -10,4 +10,6 @@ 48dp 36dp 36dp + 24dp + 4dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3da6445..2192808 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,7 @@ Color Scheme Extra Keys Source + Exit sh diff --git a/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabSwitcher.java b/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabSwitcher.java index 5db0382..db08e52 100755 --- a/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabSwitcher.java +++ b/chrome-tabs/src/main/java/de/mrapp/android/tabswitcher/TabSwitcher.java @@ -254,7 +254,9 @@ public class TabSwitcher extends FrameLayout implements TabSwitcherLayout, Model public void onGlobalLayout() { ViewUtil.removeOnGlobalLayoutListener( tabContainer.getViewTreeObserver(), this); - TabSwitcher.this.layout.onGlobalLayout(); + if (TabSwitcher.this.layout != null) { + TabSwitcher.this.layout.onGlobalLayout(); + } } });