AutoComplete: Popup version.
This commit is contained in:
parent
f8f6fce0a5
commit
c9d59c643f
@ -0,0 +1,37 @@
|
|||||||
|
package io.neoterm.customize.completion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author kiva
|
||||||
|
*/
|
||||||
|
object AutoCompleteManager {
|
||||||
|
private val completeCandidates = mutableListOf<CompleteCandidate>()
|
||||||
|
|
||||||
|
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<CompleteCandidate> {
|
||||||
|
val result = mutableListOf<CompleteCandidate>()
|
||||||
|
if (text.isNotEmpty()) {
|
||||||
|
completeCandidates.forEach {
|
||||||
|
if (it.completeString.startsWith(text, ignoreCase = true)) {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package io.neoterm.customize.completion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author kiva
|
||||||
|
*/
|
||||||
|
class CompleteCandidate(var completeString: String) {
|
||||||
|
var displayName: String = completeString
|
||||||
|
var description: String? = null
|
||||||
|
}
|
@ -25,7 +25,7 @@ object NeoPreference {
|
|||||||
const val VALUE_NEOTERM_FIRST = "NeoTermFirst"
|
const val VALUE_NEOTERM_FIRST = "NeoTermFirst"
|
||||||
const val VALUE_SYSTEM_FIRST = "SystemFirst"
|
const val VALUE_SYSTEM_FIRST = "SystemFirst"
|
||||||
|
|
||||||
var preference: SharedPreferences? = null
|
private var preference: SharedPreferences? = null
|
||||||
|
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
preference = PreferenceManager.getDefaultSharedPreferences(context)
|
preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -56,8 +56,6 @@ class NeoTermService : Service() {
|
|||||||
ACTION_ACQUIRE_LOCK -> acquireLock()
|
ACTION_ACQUIRE_LOCK -> acquireLock()
|
||||||
|
|
||||||
ACTION_RELEASE_LOCK -> releaseLock()
|
ACTION_RELEASE_LOCK -> releaseLock()
|
||||||
|
|
||||||
null -> Log.e(EmulatorDebug.LOG_TAG, "Unknown NeoTermService action: '$action'")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags and Service.START_FLAG_REDELIVERY == 0) {
|
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)
|
builder.setPriority(if (lockAcquired) Notification.PRIORITY_HIGH else Notification.PRIORITY_MIN)
|
||||||
|
|
||||||
val exitIntent = Intent(this, NeoTermService::class.java).setAction(ACTION_SERVICE_STOP)
|
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 newWakeAction = if (lockAcquired) ACTION_RELEASE_LOCK else ACTION_ACQUIRE_LOCK
|
||||||
val toggleWakeLockIntent = Intent(this, NeoTermService::class.java).setAction(newWakeAction)
|
val toggleWakeLockIntent = Intent(this, NeoTermService::class.java).setAction(newWakeAction)
|
||||||
val actionTitle = getString(if (lockAcquired)
|
val actionTitle = getString(
|
||||||
R.string.service_release_lock
|
if (lockAcquired)
|
||||||
else
|
R.string.service_release_lock
|
||||||
R.string.service_acquire_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
|
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))
|
builder.addAction(actionIcon, actionTitle, PendingIntent.getService(this, 0, toggleWakeLockIntent, 0))
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class PackageAdapter(context: Context, comparator: Comparator<PackageModel>, pri
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): SortedListAdapter.ViewHolder<out PackageModel> {
|
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): SortedListAdapter.ViewHolder<out PackageModel> {
|
||||||
val rootView = inflater.inflate(R.layout.package_item, parent, false)
|
val rootView = inflater.inflate(R.layout.item_package, parent, false)
|
||||||
return PackageViewHolder(rootView, listener)
|
return PackageViewHolder(rootView, listener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.app.Activity
|
|||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Rect
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
@ -77,6 +78,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContentView(R.layout.ui_main)
|
setContentView(R.layout.ui_main)
|
||||||
|
|
||||||
toolbar = findViewById(R.id.terminal_toolbar) as Toolbar
|
toolbar = findViewById(R.id.terminal_toolbar) as Toolbar
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
@ -361,6 +363,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
|||||||
if (tabSwitcher.selectedTab is TermTab) {
|
if (tabSwitcher.selectedTab is TermTab) {
|
||||||
val tab = tabSwitcher.selectedTab as TermTab
|
val tab = tabSwitcher.selectedTab as TermTab
|
||||||
tab.requireHideIme()
|
tab.requireHideIme()
|
||||||
|
tab.onFullScreenModeChanged(fullScreen)
|
||||||
}
|
}
|
||||||
NeoPreference.store(R.string.key_ui_fullscreen, fullScreen)
|
NeoPreference.store(R.string.key_ui_fullscreen, fullScreen)
|
||||||
this@NeoTermActivity.recreate()
|
this@NeoTermActivity.recreate()
|
||||||
|
@ -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<Char>()
|
||||||
|
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<CompleteCandidate>) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import io.neoterm.preference.NeoPreference
|
|||||||
import io.neoterm.ui.term.tab.event.TabCloseEvent
|
import io.neoterm.ui.term.tab.event.TabCloseEvent
|
||||||
import io.neoterm.ui.term.tab.event.TitleChangedEvent
|
import io.neoterm.ui.term.tab.event.TitleChangedEvent
|
||||||
import io.neoterm.ui.term.tab.event.ToggleFullScreenEvent
|
import io.neoterm.ui.term.tab.event.ToggleFullScreenEvent
|
||||||
|
import io.neoterm.view.OnAutoCompleteListener
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,6 +22,7 @@ class TermTab(title: CharSequence) : Tab(title) {
|
|||||||
var termSession: TerminalSession? = null
|
var termSession: TerminalSession? = null
|
||||||
var sessionCallback: TermSessionChangedCallback? = null
|
var sessionCallback: TermSessionChangedCallback? = null
|
||||||
var viewClient: TermViewClient? = null
|
var viewClient: TermViewClient? = null
|
||||||
|
var onAutoCompleteListener: OnAutoCompleteListener? = null
|
||||||
var toolbar: Toolbar? = null
|
var toolbar: Toolbar? = null
|
||||||
|
|
||||||
fun updateColorScheme() {
|
fun updateColorScheme() {
|
||||||
@ -29,6 +31,9 @@ class TermTab(title: CharSequence) : Tab(title) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
|
onAutoCompleteListener?.onCleanUp()
|
||||||
|
onAutoCompleteListener = null
|
||||||
|
|
||||||
viewClient?.termTab = null
|
viewClient?.termTab = null
|
||||||
viewClient?.termView = null
|
viewClient?.termView = null
|
||||||
viewClient?.extraKeysView = null
|
viewClient?.extraKeysView = null
|
||||||
@ -54,6 +59,12 @@ class TermTab(title: CharSequence) : Tab(title) {
|
|||||||
viewClient?.sessionFinished = true
|
viewClient?.sessionFinished = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onFullScreenModeChanged(fullScreen: Boolean) {
|
||||||
|
// Window token changed, we need to recreate PopupWindow
|
||||||
|
onAutoCompleteListener?.onCleanUp()
|
||||||
|
onAutoCompleteListener = null
|
||||||
|
}
|
||||||
|
|
||||||
fun requireCloseTab() {
|
fun requireCloseTab() {
|
||||||
requireHideIme()
|
requireHideIme()
|
||||||
EventBus.getDefault().post(TabCloseEvent(this))
|
EventBus.getDefault().post(TabCloseEvent(this))
|
||||||
|
@ -8,12 +8,14 @@ import android.view.ViewGroup
|
|||||||
import de.mrapp.android.tabswitcher.Tab
|
import de.mrapp.android.tabswitcher.Tab
|
||||||
import de.mrapp.android.tabswitcher.TabSwitcher
|
import de.mrapp.android.tabswitcher.TabSwitcher
|
||||||
import de.mrapp.android.tabswitcher.TabSwitcherDecorator
|
import de.mrapp.android.tabswitcher.TabSwitcherDecorator
|
||||||
|
import io.neoterm.BuildConfig
|
||||||
import io.neoterm.R
|
import io.neoterm.R
|
||||||
import io.neoterm.customize.color.ColorSchemeManager
|
import io.neoterm.customize.color.ColorSchemeManager
|
||||||
import io.neoterm.preference.NeoPreference
|
import io.neoterm.preference.NeoPreference
|
||||||
import io.neoterm.ui.term.NeoTermActivity
|
import io.neoterm.ui.term.NeoTermActivity
|
||||||
import io.neoterm.utils.TerminalUtils
|
import io.neoterm.utils.TerminalUtils
|
||||||
import io.neoterm.view.ExtraKeysView
|
import io.neoterm.view.ExtraKeysView
|
||||||
|
import io.neoterm.view.OnAutoCompleteListener
|
||||||
import io.neoterm.view.TerminalView
|
import io.neoterm.view.TerminalView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,11 +74,24 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
|
|||||||
termTab.viewClient?.updateSuggestions(termTab.termSession?.title, true)
|
termTab.viewClient?.updateSuggestions(termTab.termSession?.title, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
view.setOnKeyListener(termTab.viewClient)
|
view.setTerminalViewClient(termTab.viewClient)
|
||||||
view.attachSession(termTab.termSession)
|
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 {
|
override fun getViewTypeCount(): Int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import io.neoterm.R
|
import io.neoterm.R
|
||||||
import io.neoterm.backend.TerminalSession
|
import io.neoterm.backend.TerminalSession
|
||||||
import io.neoterm.customize.color.ColorSchemeManager
|
|
||||||
import io.neoterm.preference.NeoTermPath
|
import io.neoterm.preference.NeoTermPath
|
||||||
import io.neoterm.customize.font.FontManager
|
import io.neoterm.customize.font.FontManager
|
||||||
import io.neoterm.preference.NeoPreference
|
import io.neoterm.preference.NeoPreference
|
||||||
@ -21,7 +20,7 @@ object TerminalUtils {
|
|||||||
terminalView?.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30)
|
terminalView?.textSize = NeoPreference.loadInt(NeoPreference.KEY_FONT_SIZE, 30)
|
||||||
terminalView?.setTypeface(FontManager.getCurrentFont().getTypeFace())
|
terminalView?.setTypeface(FontManager.getCurrentFont().getTypeFace())
|
||||||
if (terminalViewClient != null) {
|
if (terminalViewClient != null) {
|
||||||
terminalView?.setOnKeyListener(terminalViewClient)
|
terminalView?.setTerminalViewClient(terminalViewClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
117
app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt
Normal file
117
app/src/main/java/io/neoterm/view/AutoCompletePopupWindow.kt
Normal file
@ -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<CompleteCandidate>? = 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java
Executable file
13
app/src/main/java/io/neoterm/view/OnAutoCompleteListener.java
Executable file
@ -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();
|
||||||
|
}
|
@ -34,7 +34,7 @@ class TerminalDialog(val context: Context) {
|
|||||||
terminalViewClient = BasicViewClient(terminalView)
|
terminalViewClient = BasicViewClient(terminalView)
|
||||||
TerminalUtils.setupTerminalView(terminalView, terminalViewClient)
|
TerminalUtils.setupTerminalView(terminalView, terminalViewClient)
|
||||||
|
|
||||||
terminalView.setOnKeyListener(terminalViewClient)
|
terminalView.setTerminalViewClient(terminalViewClient)
|
||||||
terminalSessionCallback = object : BasicSessionCallback(terminalView) {
|
terminalSessionCallback = object : BasicSessionCallback(terminalView) {
|
||||||
override fun onSessionFinished(finishedSession: TerminalSession?) {
|
override fun onSessionFinished(finishedSession: TerminalSession?) {
|
||||||
sessionFinishedCallback?.onSessionFinished(this@TerminalDialog, finishedSession)
|
sessionFinishedCallback?.onSessionFinished(this@TerminalDialog, finishedSession)
|
||||||
|
@ -31,6 +31,10 @@ final class TerminalRenderer {
|
|||||||
/** The {@link #mFontLineSpacing} + {@link #mFontAscent}. */
|
/** The {@link #mFontLineSpacing} + {@link #mFontAscent}. */
|
||||||
final int mFontLineSpacingAndAscent;
|
final int mFontLineSpacingAndAscent;
|
||||||
|
|
||||||
|
/** AutoCompletion PopupWindow need them to show popup window */
|
||||||
|
protected float savedLastDrawnLineX;
|
||||||
|
protected float savedLastDrawnLineY;
|
||||||
|
|
||||||
private final float[] asciiMeasures = new float[127];
|
private final float[] asciiMeasures = new float[127];
|
||||||
|
|
||||||
public TerminalRenderer(int textSize, Typeface typeface) {
|
public TerminalRenderer(int textSize, Typeface typeface) {
|
||||||
@ -200,6 +204,8 @@ final class TerminalRenderer {
|
|||||||
if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
|
if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
|
||||||
else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
|
else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
|
||||||
canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint);
|
canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint);
|
||||||
|
savedLastDrawnLineX = left;
|
||||||
|
savedLastDrawnLineY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((effect & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) == 0) {
|
if ((effect & TextStyle.CHARACTER_ATTRIBUTE_INVISIBLE) == 0) {
|
||||||
@ -227,4 +233,12 @@ final class TerminalRenderer {
|
|||||||
|
|
||||||
if (savedMatrix) canvas.restore();
|
if (savedMatrix) canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getCursorX() {
|
||||||
|
return savedLastDrawnLineX;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getCursorY() {
|
||||||
|
return savedLastDrawnLineY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)}.
|
* available with {@link View#setOnKeyListener(OnKeyListener)}.
|
||||||
*/
|
*/
|
||||||
public void setOnKeyListener(TerminalViewClient onKeyListener) {
|
public void setTerminalViewClient(TerminalViewClient client) {
|
||||||
this.mClient = onKeyListener;
|
this.mClient = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,6 +296,9 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
Editable content = getEditable();
|
Editable content = getEditable();
|
||||||
sendTextToTerminal(content);
|
sendTextToTerminal(content);
|
||||||
|
if (onAutoCompleteListener != null) {
|
||||||
|
onAutoCompleteListener.onAutoComplete(content.toString());
|
||||||
|
}
|
||||||
content.clear();
|
content.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -678,6 +681,16 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
if (mCombiningAccent != oldCombiningAccent) invalidate();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,6 +758,9 @@ public final class TerminalView extends View {
|
|||||||
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
|
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
|
||||||
if (code == null) return false;
|
if (code == null) return false;
|
||||||
mTermSession.write(code);
|
mTermSession.write(code);
|
||||||
|
if (onAutoCompleteListener != null) {
|
||||||
|
onAutoCompleteListener.onKeyCode(keyCode, keyMod);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,4 +976,24 @@ public final class TerminalView extends View {
|
|||||||
return mTermSession;
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import io.neoterm.backend.TerminalSession;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Input and scale listener which may be set on a {@link TerminalView} through
|
* Input and scale listener which may be set on a {@link TerminalView} through
|
||||||
* {@link TerminalView#setOnKeyListener(TerminalViewClient)}.
|
* {@link TerminalView#setTerminalViewClient(TerminalViewClient)}.
|
||||||
* <p/>
|
* <p/>
|
||||||
*/
|
*/
|
||||||
public interface TerminalViewClient {
|
public interface TerminalViewClient {
|
||||||
|
31
app/src/main/res/layout/item_complete_candidate.xml
Normal file
31
app/src/main/res/layout/item_complete_candidate.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minHeight="@dimen/min_popup_height"
|
||||||
|
android:padding="@dimen/popup_padding"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/complete_display"
|
||||||
|
style="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Display" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/complete_split"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/popup_split_background" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/complete_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/text_margin"
|
||||||
|
android:layout_marginStart="@dimen/text_margin"
|
||||||
|
tools:text="Description" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
13
app/src/main/res/layout/popup_auto_complete.xml
Normal file
13
app/src/main/res/layout/popup_auto_complete.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/popup_background"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/popup_complete_candidate_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -81,4 +81,5 @@
|
|||||||
<string name="service_lock_acquired"> (永不休眠)</string>
|
<string name="service_lock_acquired"> (永不休眠)</string>
|
||||||
<string name="service_acquire_lock">取得休眠锁</string>
|
<string name="service_acquire_lock">取得休眠锁</string>
|
||||||
<string name="service_release_lock">释放休眠锁</string>
|
<string name="service_release_lock">释放休眠锁</string>
|
||||||
|
<string name="exit">退出</string>
|
||||||
</resources>
|
</resources>
|
@ -15,6 +15,8 @@
|
|||||||
<color name="roseEnd">#ef0276</color>
|
<color name="roseEnd">#ef0276</color>
|
||||||
<color name="greenStart">#a7f56e</color>
|
<color name="greenStart">#a7f56e</color>
|
||||||
<color name="greenEnd">#11c612</color>
|
<color name="greenEnd">#11c612</color>
|
||||||
|
<color name="popup_background">#7f000000</color>
|
||||||
|
<color name="popup_split_background">#efefef</color>
|
||||||
|
|
||||||
<integer-array name="bubble_colors">
|
<integer-array name="bubble_colors">
|
||||||
<item>@color/roseStart</item>
|
<item>@color/roseStart</item>
|
||||||
|
@ -10,4 +10,6 @@
|
|||||||
<dimen name="custom_editor_line_height">48dp</dimen>
|
<dimen name="custom_editor_line_height">48dp</dimen>
|
||||||
<dimen name="custom_install_icon_width">36dp</dimen>
|
<dimen name="custom_install_icon_width">36dp</dimen>
|
||||||
<dimen name="custom_install_icon_height">36dp</dimen>
|
<dimen name="custom_install_icon_height">36dp</dimen>
|
||||||
|
<dimen name="min_popup_height">24dp</dimen>
|
||||||
|
<dimen name="popup_padding">4dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -76,6 +76,7 @@
|
|||||||
<string name="pref_customization_color_scheme">Color Scheme</string>
|
<string name="pref_customization_color_scheme">Color Scheme</string>
|
||||||
<string name="pref_customization_eks">Extra Keys</string>
|
<string name="pref_customization_eks">Extra Keys</string>
|
||||||
<string name="pref_package_source">Source</string>
|
<string name="pref_package_source">Source</string>
|
||||||
|
<string name="exit">Exit</string>
|
||||||
|
|
||||||
<string-array name="pref_general_shell_entries" translatable="false">
|
<string-array name="pref_general_shell_entries" translatable="false">
|
||||||
<item>sh</item>
|
<item>sh</item>
|
||||||
|
@ -254,7 +254,9 @@ public class TabSwitcher extends FrameLayout implements TabSwitcherLayout, Model
|
|||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
ViewUtil.removeOnGlobalLayoutListener(
|
ViewUtil.removeOnGlobalLayoutListener(
|
||||||
tabContainer.getViewTreeObserver(), this);
|
tabContainer.getViewTreeObserver(), this);
|
||||||
TabSwitcher.this.layout.onGlobalLayout();
|
if (TabSwitcher.this.layout != null) {
|
||||||
|
TabSwitcher.this.layout.onGlobalLayout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user