apply recyclerview to logs to prevent crashes

pull/1112/head
deniscerri 5 months ago
parent 2370144089
commit 02aef16edc
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -0,0 +1,68 @@
package com.deniscerri.ytdl.ui.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
class LogAdapter : ListAdapter<String, LogAdapter.LogViewHolder>(DiffCallback()) {
var isWrapped: Boolean = true
var textSize: Float = 15f
var highlight: Boolean = false
class LogViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvLine: TextView = view.findViewById(R.id.content)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.log_textview, parent, false)
return LogViewHolder(view)
}
override fun onBindViewHolder(holder: LogViewHolder, position: Int) {
holder.tvLine.apply {
text = getItem(position)
if (textSize != this@LogAdapter.textSize) {
textSize = this@LogAdapter.textSize
}
if (isWrapped) {
// --- WRAP MODE ---
layoutParams = RecyclerView.LayoutParams(
// Forces the line to fit the screen width
resources.displayMetrics.widthPixels,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setHorizontallyScrolling(false)
isSingleLine = false
ellipsize = null
} else {
// --- NO-WRAP (BLEED) MODE ---
layoutParams = RecyclerView.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
// This is the magic combo for "Bleeding" text
setHorizontallyScrolling(true)
isSingleLine = true
ellipsize = null // Ensure text isn't cut off with dots
}
if (highlight) {
enableTextHighlight()
}
}
}
class DiffCallback : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(old: String, new: String) = false
override fun areContentsTheSame(old: String, new: String) = false
}
}

@ -8,14 +8,18 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.HorizontalScrollView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.edit
import androidx.core.view.ScrollingView
import androidx.core.view.children
import androidx.core.view.get
import androidx.core.view.isVisible
@ -23,9 +27,14 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdl.MainActivity
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.database.viewmodel.LogViewModel
import com.deniscerri.ytdl.ui.adapter.LogAdapter
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
import com.deniscerri.ytdl.util.Extensions.setCustomTextSize
@ -47,14 +56,17 @@ import org.greenrobot.eventbus.ThreadMode
class DownloadLogFragment : Fragment() {
private lateinit var content: TextView
private lateinit var contentScrollView : ScrollView
private lateinit var logRecyclerView: RecyclerView
private lateinit var horizontalScrollingView: HorizontalScrollView
private lateinit var logAdapter: LogAdapter
private lateinit var topAppBar: MaterialToolbar
private lateinit var copyLog : ExtendedFloatingActionButton
private lateinit var mainActivity: MainActivity
private lateinit var logViewModel: LogViewModel
private lateinit var sharedPreferences: SharedPreferences
private var logID: Long? = null
private var contentText: String = ""
private var contentLineCount: Int = 0
private var autoScroll : Boolean = true
private var scrollDownBtn : MenuItem? = null
@ -80,23 +92,28 @@ class DownloadLogFragment : Fragment() {
mainActivity.onBackPressedDispatcher.onBackPressed()
}
content = view.findViewById(R.id.content)
content.setTextIsSelectable(true)
content.layoutParams!!.width = ActionBar.LayoutParams.WRAP_CONTENT
contentScrollView = view.findViewById(R.id.content_scrollview)
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
logRecyclerView = view.findViewById(R.id.log_recycler_view)
logAdapter = LogAdapter()
logRecyclerView.layoutManager = LinearLayoutManager(requireContext())
logRecyclerView.adapter = logAdapter
logAdapter.isWrapped = sharedPreferences.getBoolean("wrap_text_log", false)
logAdapter.textSize = sharedPreferences.getFloat("log_zoom", 2f) + 13f
logAdapter.highlight = sharedPreferences.getBoolean("use_code_color_highlighter", true)
horizontalScrollingView = view.findViewById(R.id.horizontal_container)
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
topAppBar.setOnClickListener {
contentScrollView.scrollTo(0,0)
logRecyclerView.scrollTo(0,0)
bottomAppBar?.menu?.get(1)?.isVisible = true
}
copyLog = view.findViewById(R.id.copy_log)
copyLog.setOnClickListener {
val clipboard: ClipboardManager =
mainActivity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setText(content.text)
clipboard.setText(contentText)
Snackbar.make(bottomAppBar, getString(R.string.copied_to_clipboard), Snackbar.LENGTH_LONG)
.setAnchorView(bottomAppBar)
.show()
@ -124,56 +141,23 @@ class DownloadLogFragment : Fragment() {
}
}
lifecycleScope.launch(Dispatchers.IO){
content.isFocusable = true
if (sharedPreferences.getBoolean("use_code_color_highlighter", true)) {
content.enableTextHighlight()
}
}
contentScrollView.enableFastScroll()
//logRecyclerView.enableFastScroll()
scrollDownBtn = bottomAppBar?.menu?.children?.first { it.itemId == R.id.scroll_down }
val slider = view.findViewById<Slider>(R.id.textsize_seekbar)
bottomAppBar?.setOnMenuItemClickListener { m: MenuItem ->
when(m.itemId){
R.id.wrap -> {
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(content, 0)
// contentScrollView.setPadding(0,0,0,
// (requireContext().resources.displayMetrics.density * 150).toInt()
// )
sharedPreferences.edit().putBoolean("wrap_text_log", true).apply()
updateAutoScrollState()
}else{
val parent = content.parent as ViewGroup
parent.removeView(content)
scrollView = HorizontalScrollView(requireContext())
// scrollView.setPadding(0,0,0,
// (requireContext().resources.displayMetrics.density * 150).toInt()
// )
contentScrollView.setPadding(0,0,0,0)
scrollView.layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
scrollView.addView(content)
scrollView.id = R.id.horizontalscroll_output
parent.addView(scrollView, 0)
updateAutoScrollState()
sharedPreferences.edit().putBoolean("wrap_text_log", false).apply()
}
val newState = !logAdapter.isWrapped
logAdapter.isWrapped = newState
sharedPreferences.edit(commit = true) { putBoolean("wrap_text_log", newState) }
logAdapter.notifyDataSetChanged()
}
R.id.scroll_down -> {
m.isVisible = false
autoScroll = true
contentScrollView.fullScroll(View.FOCUS_DOWN)
logRecyclerView.smoothScrollToPosition(contentLineCount)
}
R.id.text_size -> {
@ -204,42 +188,80 @@ class DownloadLogFragment : Fragment() {
this.valueFrom = 0f
this.valueTo = 10f
this.value = sharedPreferences.getFloat("log_zoom", 2f)
content.setCustomTextSize(this.value + 13f)
logAdapter.textSize = this.value + 13f
logAdapter.notifyDataSetChanged()
this.addOnChangeListener { slider, value, fromUser ->
content.setCustomTextSize(value + 13f)
logAdapter.textSize = value + 13f
logAdapter.notifyDataSetChanged()
sharedPreferences.edit(true){
putFloat("log_zoom", value)
}
}
}
contentScrollView.setOnScrollChangeListener { view, sx, sy, osx, osy ->
logRecyclerView.setOnScrollChangeListener { view, sx, sy, osx, osy ->
updateAutoScrollState()
}
contentScrollView.setOnTouchListener { view, motionEvent ->
logRecyclerView.setOnTouchListener { view, motionEvent ->
autoScroll = false
false
}
sharedPreferences.getBoolean("wrap_text_log", false).apply {
if (this){
bottomAppBar.menu.performIdentifierAction(R.id.wrap, 0)
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) {
}
})
logViewModel.getLogFlowByID(logID!!).observe(viewLifecycleOwner){logItem ->
kotlin.runCatching {
requireActivity().runOnUiThread{
if (logItem != null){
if (logItem.content.isNotBlank()) {
content.setText(logItem.content, TextView.BufferType.SPANNABLE)
bottomAppBar?.menu?.get(1)?.isVisible = contentScrollView.canScrollVertically(1)
contentText = logItem.content
val lines = contentText.split("\n")
contentLineCount = lines.size
logAdapter.submitList(lines)
bottomAppBar?.menu?.get(1)?.isVisible = logRecyclerView.canScrollVertically(1)
}
if (autoScroll){
//content.scrollTo(0, content.height)
content.post {
contentScrollView.fullScroll(View.FOCUS_DOWN)
if (autoScroll && contentLineCount > 0){
logRecyclerView.post {
logRecyclerView.smoothScrollToPosition(contentLineCount)
}
}
}
@ -249,7 +271,7 @@ class DownloadLogFragment : Fragment() {
}
private fun updateAutoScrollState() {
val canVerticallyScroll = contentScrollView.canScrollVertically(1)
val canVerticallyScroll = logRecyclerView.canScrollVertically(1)
scrollDownBtn?.isVisible = canVerticallyScroll
}
@ -272,8 +294,4 @@ class DownloadLogFragment : Fragment() {
progressBar.setProgressCompat(event.progress, true)
}
}
companion object {
private const val TAG = "DownloadLogActivity"
}
}

@ -12,6 +12,7 @@ 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
@ -30,12 +31,15 @@ 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
@ -57,10 +61,11 @@ class TerminalFragment : Fragment() {
private lateinit var topAppBar: MaterialToolbar
private lateinit var notificationUtil: NotificationUtil
private lateinit var terminalViewModel: TerminalViewModel
private lateinit var output: TextView
private lateinit var logRecyclerView: RecyclerView
private lateinit var horizontalScrollingView: HorizontalScrollView
private lateinit var logAdapter: LogAdapter
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
@ -68,6 +73,9 @@ 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?,
@ -82,7 +90,7 @@ class TerminalFragment : Fragment() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("input", input.text.toString())
outState.putString("output", output.text.toString())
outState.putString("output", contentText)
outState.putBoolean("run", fab.text == requireActivity().getString(R.string.run_command))
outState.putLong("downloadID", downloadID)
}
@ -96,10 +104,24 @@ class TerminalFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
var bundle = savedInstanceState
imm = requireActivity().getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
scrollView = view.findViewById(R.id.custom_command_scrollview)
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)
topAppBar = requireActivity().findViewById(R.id.custom_command_toolbar)
topAppBar.setNavigationOnClickListener { requireActivity().finish() }
topAppBar.setOnClickListener { scrollView.scrollTo(0,0) }
topAppBar.setOnClickListener { logRecyclerView.scrollTo(0,0) }
input = view.findViewById(R.id.command_edittext)
fab = view.findViewById(R.id.command_fab)
@ -120,7 +142,6 @@ class TerminalFragment : Fragment() {
}
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
metrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(metrics)
@ -203,21 +224,18 @@ 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 txt = "${output.text}\n~ $ ${input.text}\n"
output.text = txt
val commandLine = "~ $ ${input.text}"
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 = output.text.toString()))
terminalViewModel.insert(TerminalItem(command = command, log = commandLine))
}
terminalViewModel.startTerminalDownloadWorker(TerminalItem(downloadID, command))
input.visibility = View.GONE
@ -241,13 +259,55 @@ class TerminalFragment : Fragment() {
input.append(bundle?.getString("input") ?: "")
input.requestFocus()
input.setSelection(input.text.length)
output.text = bundle?.getString("output") ?: output.text
output.isVisible = output.text.toString().isNotEmpty()
val list = (bundle?.getString("output")?.split("\r","\n") ?: contentText.split("\r","\n"))
contentLineCount = list.size
contentText = list.joinToString("\n")
logAdapter.submitList(list)
}
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()
}
@ -261,31 +321,15 @@ class TerminalFragment : Fragment() {
topAppBar.setOnMenuItemClickListener { m: MenuItem ->
when(m.itemId){
R.id.wrap -> {
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()
}
val newState = !logAdapter.isWrapped
logAdapter.isWrapped = newState
sharedPreferences.edit(commit = true) { putBoolean("wrap_text_terminal", newState) }
logAdapter.notifyDataSetChanged()
}
R.id.export_clipboard -> {
lifecycleScope.launch(Dispatchers.IO){
val clipboard: ClipboardManager = requireActivity().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setText(output.text)
clipboard.setText(contentText)
}
}
@ -300,22 +344,20 @@ class TerminalFragment : Fragment() {
this.valueFrom = 0f
this.valueTo = 10f
this.value = sharedPreferences.getFloat("terminal_zoom", 2f)
output.setCustomTextSize(this.value + 13f)
logAdapter.textSize = this.value + 13f
logAdapter.notifyDataSetChanged()
input.setCustomTextSize(this.value + 13f)
this.addOnChangeListener { slider, value, fromUser ->
output.setCustomTextSize(value + 13f)
logAdapter.textSize = value + 13f
logAdapter.notifyDataSetChanged()
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 {
@ -352,11 +394,13 @@ class TerminalFragment : Fragment() {
requireActivity().runOnUiThread{
if (it != null){
if (!it.log.isNullOrBlank()) {
output.isVisible = true
output.text = it.log
contentText = it.log!!
val lines = contentText.split("\r","\n")
contentLineCount = lines.size
logAdapter.submitList(lines) {
logRecyclerView.scrollToPosition(lines.size - 1)
}
}
output.scrollTo(0, output.height)
scrollView.fullScroll(View.FOCUS_DOWN)
input.visibility = View.GONE
showCancelFab()
}

@ -35,52 +35,25 @@
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/frameLayout"
<HorizontalScrollView
android:id="@+id/horizontal_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:fillViewport="true"
android:scrollbars="horizontal"
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appbarlayout">
<me.zhanghai.android.fastscroll.FastScrollScrollView
android:id="@+id/content_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:id="@+id/horizontalscroll_output"
android:layout_height="wrap_content">
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:gravity="bottom"
android:padding="10dp"
android:scrollbars="none"
android:textIsSelectable="true"
android:textSize="15sp" />
</HorizontalScrollView>
</LinearLayout>
</me.zhanghai.android.fastscroll.FastScrollScrollView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/log_recycler_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="100dp" />
</FrameLayout>
</HorizontalScrollView>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"

@ -6,15 +6,13 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:id="@+id/custom_command_scrollview"
android:layout_width="0dp"
<androidx.core.widget.NestedScrollView
android:id="@+id/terminal_scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:fillViewport="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout">
<LinearLayout
android:layout_width="match_parent"
@ -23,39 +21,39 @@
android:orientation="vertical">
<HorizontalScrollView
android:id="@+id/horizontalscroll_output"
android:id="@+id/horizontal_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/custom_command_output"
android:visibility="gone"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="horizontal"
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appbarlayout">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/log_recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:gravity="bottom"
android:scrollbars="horizontal|vertical"
android:scrollHorizontally="true"
android:textIsSelectable="true"
android:textSize="15sp" />
android:layout_height="match_parent"
android:clipToPadding="false"/>
</HorizontalScrollView>
<EditText
android:id="@+id/command_edittext"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fontFamily="monospace"
android:gravity="start"
android:paddingHorizontal="10dp"
android:inputType="textMultiLine|textNoSuggestions"
android:maxLines="10000"
android:text="yt-dlp "
android:paddingBottom="100dp"
android:textSize="15sp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</ScrollView>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"

@ -0,0 +1,10 @@
<TextView android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:gravity="bottom"
android:paddingHorizontal="10dp"
android:scrollbars="none"
android:textIsSelectable="true"
android:textSize="15sp"
xmlns:android="http://schemas.android.com/apk/res/android" />
Loading…
Cancel
Save