|
|
|
|
@ -12,7 +12,6 @@ import android.os.Bundle
|
|
|
|
|
import android.util.DisplayMetrics
|
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
|
import android.view.MenuItem
|
|
|
|
|
import android.view.MotionEvent
|
|
|
|
|
import android.view.View
|
|
|
|
|
import android.view.ViewGroup
|
|
|
|
|
import android.view.inputmethod.InputMethodManager
|
|
|
|
|
@ -31,15 +30,12 @@ import androidx.lifecycle.Observer
|
|
|
|
|
import androidx.lifecycle.ViewModelProvider
|
|
|
|
|
import androidx.lifecycle.lifecycleScope
|
|
|
|
|
import androidx.preference.PreferenceManager
|
|
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
|
|
|
import androidx.work.WorkInfo
|
|
|
|
|
import androidx.work.WorkManager
|
|
|
|
|
import com.deniscerri.ytdl.R
|
|
|
|
|
import com.deniscerri.ytdl.database.models.TerminalItem
|
|
|
|
|
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
|
|
|
|
import com.deniscerri.ytdl.database.viewmodel.TerminalViewModel
|
|
|
|
|
import com.deniscerri.ytdl.ui.adapter.LogAdapter
|
|
|
|
|
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
|
|
|
|
|
import com.deniscerri.ytdl.util.Extensions.setCustomTextSize
|
|
|
|
|
import com.deniscerri.ytdl.util.FileUtil
|
|
|
|
|
@ -61,11 +57,10 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
private lateinit var topAppBar: MaterialToolbar
|
|
|
|
|
private lateinit var notificationUtil: NotificationUtil
|
|
|
|
|
private lateinit var terminalViewModel: TerminalViewModel
|
|
|
|
|
private lateinit var logRecyclerView: RecyclerView
|
|
|
|
|
private lateinit var horizontalScrollingView: HorizontalScrollView
|
|
|
|
|
private lateinit var logAdapter: LogAdapter
|
|
|
|
|
private lateinit var output: TextView
|
|
|
|
|
private lateinit var input: EditText
|
|
|
|
|
private lateinit var fab: ExtendedFloatingActionButton
|
|
|
|
|
private lateinit var scrollView: ScrollView
|
|
|
|
|
private lateinit var bottomAppBar: BottomAppBar
|
|
|
|
|
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
|
|
|
|
private lateinit var sharedPreferences: SharedPreferences
|
|
|
|
|
@ -73,9 +68,6 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
private lateinit var imm : InputMethodManager
|
|
|
|
|
private lateinit var metrics: DisplayMetrics
|
|
|
|
|
|
|
|
|
|
private var contentText: String = ""
|
|
|
|
|
private var contentLineCount: Int = 0
|
|
|
|
|
|
|
|
|
|
override fun onCreateView(
|
|
|
|
|
inflater: LayoutInflater,
|
|
|
|
|
container: ViewGroup?,
|
|
|
|
|
@ -90,7 +82,7 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
override fun onSaveInstanceState(outState: Bundle) {
|
|
|
|
|
super.onSaveInstanceState(outState)
|
|
|
|
|
outState.putString("input", input.text.toString())
|
|
|
|
|
outState.putString("output", contentText)
|
|
|
|
|
outState.putString("output", output.text.toString())
|
|
|
|
|
outState.putBoolean("run", fab.text == requireActivity().getString(R.string.run_command))
|
|
|
|
|
outState.putLong("downloadID", downloadID)
|
|
|
|
|
}
|
|
|
|
|
@ -104,24 +96,10 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
|
var bundle = savedInstanceState
|
|
|
|
|
imm = requireActivity().getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
|
|
|
|
|
|
|
|
|
logRecyclerView = view.findViewById(R.id.log_recycler_view)
|
|
|
|
|
logAdapter = LogAdapter()
|
|
|
|
|
logRecyclerView.layoutManager = LinearLayoutManager(requireContext())
|
|
|
|
|
logRecyclerView.adapter = logAdapter
|
|
|
|
|
logRecyclerView.itemAnimator = null
|
|
|
|
|
|
|
|
|
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
|
|
|
|
|
|
|
|
logAdapter.isWrapped = sharedPreferences.getBoolean("wrap_text_terminal", false)
|
|
|
|
|
logAdapter.textSize = sharedPreferences.getFloat("terminal_zoom", 2f) + 13f
|
|
|
|
|
logAdapter.highlight = sharedPreferences.getBoolean("use_code_color_highlighter", true)
|
|
|
|
|
|
|
|
|
|
horizontalScrollingView = view.findViewById(R.id.horizontal_container)
|
|
|
|
|
|
|
|
|
|
scrollView = view.findViewById(R.id.custom_command_scrollview)
|
|
|
|
|
topAppBar = requireActivity().findViewById(R.id.custom_command_toolbar)
|
|
|
|
|
topAppBar.setNavigationOnClickListener { requireActivity().finish() }
|
|
|
|
|
topAppBar.setOnClickListener { logRecyclerView.scrollTo(0,0) }
|
|
|
|
|
topAppBar.setOnClickListener { scrollView.scrollTo(0,0) }
|
|
|
|
|
|
|
|
|
|
input = view.findViewById(R.id.command_edittext)
|
|
|
|
|
fab = view.findViewById(R.id.command_fab)
|
|
|
|
|
@ -142,6 +120,7 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
|
|
|
|
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
|
|
|
metrics = DisplayMetrics()
|
|
|
|
|
requireActivity().windowManager.defaultDisplay.getMetrics(metrics)
|
|
|
|
|
|
|
|
|
|
@ -224,18 +203,21 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output = view.findViewById(R.id.custom_command_output)
|
|
|
|
|
output.setTextIsSelectable(true)
|
|
|
|
|
output.layoutParams!!.width = LayoutParams.WRAP_CONTENT
|
|
|
|
|
input.requestFocus()
|
|
|
|
|
fab.setOnClickListener {
|
|
|
|
|
if (fab.text == requireActivity().getString(R.string.run_command)){
|
|
|
|
|
input.visibility = View.GONE
|
|
|
|
|
val commandLine = "~ $ ${input.text}"
|
|
|
|
|
|
|
|
|
|
val txt = "${output.text}\n~ $ ${input.text}\n"
|
|
|
|
|
output.text = txt
|
|
|
|
|
showCancelFab()
|
|
|
|
|
imm.hideSoftInputFromWindow(input.windowToken, 0)
|
|
|
|
|
lifecycleScope.launch {
|
|
|
|
|
val command = input.text.toString().replaceFirst("yt-dlp", "")
|
|
|
|
|
downloadID = withContext(Dispatchers.IO){
|
|
|
|
|
terminalViewModel.insert(TerminalItem(command = command, log = commandLine))
|
|
|
|
|
terminalViewModel.insert(TerminalItem(command = command, log = output.text.toString()))
|
|
|
|
|
}
|
|
|
|
|
terminalViewModel.startTerminalDownloadWorker(TerminalItem(downloadID, command))
|
|
|
|
|
input.visibility = View.GONE
|
|
|
|
|
@ -259,55 +241,13 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
input.append(bundle?.getString("input") ?: "")
|
|
|
|
|
input.requestFocus()
|
|
|
|
|
input.setSelection(input.text.length)
|
|
|
|
|
|
|
|
|
|
val list = (bundle?.getString("output")?.split("\r","\n") ?: contentText.split("\r","\n"))
|
|
|
|
|
contentLineCount = list.size
|
|
|
|
|
contentText = list.joinToString("\n")
|
|
|
|
|
logAdapter.submitList(list)
|
|
|
|
|
output.text = bundle?.getString("output") ?: output.text
|
|
|
|
|
output.isVisible = output.text.toString().isNotEmpty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bundle?.getBoolean("run") == true){
|
|
|
|
|
showCancelFab()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
|
|
|
|
|
private var startX = 0f
|
|
|
|
|
private var startY = 0f
|
|
|
|
|
|
|
|
|
|
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
|
|
|
|
when (e.action) {
|
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
|
startX = e.x
|
|
|
|
|
startY = e.y
|
|
|
|
|
// Prevent parent from stealing the initial touch
|
|
|
|
|
rv.parent.requestDisallowInterceptTouchEvent(true)
|
|
|
|
|
}
|
|
|
|
|
MotionEvent.ACTION_MOVE -> {
|
|
|
|
|
val dx = Math.abs(e.x - startX)
|
|
|
|
|
val dy = Math.abs(e.y - startY)
|
|
|
|
|
|
|
|
|
|
// If moving more vertically than horizontally, lock vertical scroll
|
|
|
|
|
if (dy > dx) {
|
|
|
|
|
rv.parent.requestDisallowInterceptTouchEvent(true)
|
|
|
|
|
} else if (dx > dy && !logAdapter.isWrapped) {
|
|
|
|
|
// If moving horizontally and wrap is OFF, let the parent take it
|
|
|
|
|
rv.parent.requestDisallowInterceptTouchEvent(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onTouchEvent(
|
|
|
|
|
rv: RecyclerView,
|
|
|
|
|
e: MotionEvent
|
|
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
runWorkerListener()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -321,15 +261,31 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
topAppBar.setOnMenuItemClickListener { m: MenuItem ->
|
|
|
|
|
when(m.itemId){
|
|
|
|
|
R.id.wrap -> {
|
|
|
|
|
val newState = !logAdapter.isWrapped
|
|
|
|
|
logAdapter.isWrapped = newState
|
|
|
|
|
sharedPreferences.edit(commit = true) { putBoolean("wrap_text_terminal", newState) }
|
|
|
|
|
logAdapter.notifyDataSetChanged()
|
|
|
|
|
var scrollView = requireView().findViewById<HorizontalScrollView>(R.id.horizontalscroll_output)
|
|
|
|
|
if(scrollView != null){
|
|
|
|
|
val parent = (scrollView.parent as ViewGroup)
|
|
|
|
|
scrollView.removeAllViews()
|
|
|
|
|
parent.removeView(scrollView)
|
|
|
|
|
parent.addView(output, 0)
|
|
|
|
|
sharedPreferences.edit().putBoolean("wrap_text_terminal", true).apply()
|
|
|
|
|
}else{
|
|
|
|
|
val parent = output.parent as ViewGroup
|
|
|
|
|
parent.removeView(output)
|
|
|
|
|
scrollView = HorizontalScrollView(requireContext())
|
|
|
|
|
scrollView.layoutParams = LinearLayout.LayoutParams(
|
|
|
|
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
|
|
|
ViewGroup.LayoutParams.MATCH_PARENT
|
|
|
|
|
)
|
|
|
|
|
scrollView.addView(output)
|
|
|
|
|
scrollView.id = R.id.horizontalscroll_output
|
|
|
|
|
parent.addView(scrollView, 0)
|
|
|
|
|
sharedPreferences.edit().putBoolean("wrap_text_terminal", false).apply()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
R.id.export_clipboard -> {
|
|
|
|
|
lifecycleScope.launch(Dispatchers.IO){
|
|
|
|
|
val clipboard: ClipboardManager = requireActivity().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
|
|
|
|
clipboard.setText(contentText)
|
|
|
|
|
clipboard.setText(output.text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -344,20 +300,22 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
this.valueFrom = 0f
|
|
|
|
|
this.valueTo = 10f
|
|
|
|
|
this.value = sharedPreferences.getFloat("terminal_zoom", 2f)
|
|
|
|
|
|
|
|
|
|
logAdapter.textSize = this.value + 13f
|
|
|
|
|
logAdapter.notifyDataSetChanged()
|
|
|
|
|
output.setCustomTextSize(this.value + 13f)
|
|
|
|
|
input.setCustomTextSize(this.value + 13f)
|
|
|
|
|
|
|
|
|
|
this.addOnChangeListener { slider, value, fromUser ->
|
|
|
|
|
logAdapter.textSize = value + 13f
|
|
|
|
|
logAdapter.notifyDataSetChanged()
|
|
|
|
|
output.setCustomTextSize(value + 13f)
|
|
|
|
|
input.setCustomTextSize(value + 13f)
|
|
|
|
|
sharedPreferences.edit(true){
|
|
|
|
|
putFloat("terminal_zoom", value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sharedPreferences.getBoolean("wrap_text_terminal", false).apply {
|
|
|
|
|
if (this){
|
|
|
|
|
bottomAppBar.menu.performIdentifierAction(R.id.wrap, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private fun hideCancelFab() {
|
|
|
|
|
kotlin.runCatching {
|
|
|
|
|
@ -394,13 +352,11 @@ class TerminalFragment : Fragment() {
|
|
|
|
|
requireActivity().runOnUiThread{
|
|
|
|
|
if (it != null){
|
|
|
|
|
if (!it.log.isNullOrBlank()) {
|
|
|
|
|
contentText = it.log!!
|
|
|
|
|
val lines = contentText.split("\r","\n")
|
|
|
|
|
contentLineCount = lines.size
|
|
|
|
|
logAdapter.submitList(lines) {
|
|
|
|
|
logRecyclerView.scrollToPosition(lines.size - 1)
|
|
|
|
|
}
|
|
|
|
|
output.isVisible = true
|
|
|
|
|
output.text = it.log
|
|
|
|
|
}
|
|
|
|
|
output.scrollTo(0, output.height)
|
|
|
|
|
scrollView.fullScroll(View.FOCUS_DOWN)
|
|
|
|
|
input.visibility = View.GONE
|
|
|
|
|
showCancelFab()
|
|
|
|
|
}
|
|
|
|
|
|