implemented tablet ui & folder picker in terminal

This commit is contained in:
deniscerri 2023-04-20 00:09:20 +02:00
parent 81a47c3407
commit 326e701812
No known key found for this signature in database
GPG key ID: 95C43D517D830350
24 changed files with 474 additions and 122 deletions

View file

@ -25,16 +25,14 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.test.InstrumentationRegistry
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.HomeFragment import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.downloads.DownloadQueueActivity import com.deniscerri.ytdlnis.ui.downloads.DownloadQueueActivity
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.UpdateUtil import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.BufferedReader import java.io.BufferedReader
@ -51,7 +49,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
private lateinit var resultViewModel: ResultViewModel private lateinit var resultViewModel: ResultViewModel
private lateinit var cookieViewModel: CookieViewModel private lateinit var cookieViewModel: CookieViewModel
private lateinit var bottomNav: BottomNavigationView private lateinit var navigationView: NavigationBarView
private lateinit var navHostFragment : NavHostFragment private lateinit var navHostFragment : NavHostFragment
@ -73,20 +71,20 @@ class MainActivity : AppCompatActivity() {
navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
val navController = navHostFragment.findNavController() val navController = navHostFragment.findNavController()
bottomNav = findViewById(R.id.bottomNavigationView) navigationView = findViewById(R.id.bottomNavigationView)
window.decorView.setOnApplyWindowInsetsListener { view: View, windowInsets: WindowInsets? -> window.decorView.setOnApplyWindowInsetsListener { view: View, windowInsets: WindowInsets? ->
val windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat( val windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(
windowInsets!!, view windowInsets!!, view
) )
val isImeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime()) val isImeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime())
bottomNav.visibility = navigationView.visibility =
if (isImeVisible) View.GONE else View.VISIBLE if (isImeVisible) View.GONE else View.VISIBLE
view.onApplyWindowInsets(windowInsets) view.onApplyWindowInsets(windowInsets)
} }
bottomNav.setupWithNavController(navController) navigationView.setupWithNavController(navController)
bottomNav.setOnItemReselectedListener { navigationView.setOnItemReselectedListener {
when (it.itemId) { when (it.itemId) {
R.id.homeFragment -> { R.id.homeFragment -> {
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as HomeFragment).scrollToTop() (navHostFragment.childFragmentManager.primaryNavigationFragment!! as HomeFragment).scrollToTop()
@ -119,19 +117,19 @@ class MainActivity : AppCompatActivity() {
} }
fun hideNav() { fun hideNav() {
bottomNav.visibility = View.GONE navigationView.visibility = View.GONE
} }
fun showNav() { fun showNav() {
bottomNav.visibility = View.VISIBLE navigationView.visibility = View.VISIBLE
} }
fun disableBottomNavigation(){ fun disableBottomNavigation(){
bottomNav.menu.forEach { it.isEnabled = false } navigationView.menu.forEach { it.isEnabled = false }
} }
fun enableBottomNavigation(){ fun enableBottomNavigation(){
bottomNav.menu.forEach { it.isEnabled = true } navigationView.menu.forEach { it.isEnabled = true }
} }
override fun onResume() { override fun onResume() {

View file

@ -13,6 +13,9 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status='Active'") @Query("SELECT * FROM downloads WHERE status='Active'")
fun getActiveDownloads() : LiveData<List<DownloadItem>> fun getActiveDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Active'")
fun getActiveDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id") @Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
fun getQueuedDownloads() : LiveData<List<DownloadItem>> fun getQueuedDownloads() : LiveData<List<DownloadItem>>
@ -46,8 +49,8 @@ interface DownloadDao {
@Query("DELETE FROM downloads WHERE status='Error'") @Query("DELETE FROM downloads WHERE status='Error'")
suspend fun deleteErrored() suspend fun deleteErrored()
@Query("DELETE FROM downloads WHERE status='Queued'") @Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued'")
suspend fun deleteQueued() suspend fun cancelQueued()
@Query("DELETE FROM downloads WHERE status='Processing' AND id=:id") @Query("DELETE FROM downloads WHERE status='Processing' AND id=:id")
suspend fun deleteSingleProcessing(id: Long) suspend fun deleteSingleProcessing(id: Long)

View file

@ -44,6 +44,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
return downloadDao.getDownloadById(id) return downloadDao.getDownloadById(id)
} }
fun getActiveDownloads() : List<DownloadItem> {
return downloadDao.getActiveDownloadsList()
}
suspend fun deleteCancelled(){ suspend fun deleteCancelled(){
downloadDao.deleteCancelled() downloadDao.deleteCancelled()
} }
@ -52,8 +56,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.deleteErrored() downloadDao.deleteErrored()
} }
suspend fun deleteQueued(){ suspend fun cancelQueued(){
downloadDao.deleteQueued() downloadDao.cancelQueued()
} }
fun checkIfPresentForProcessing(item: ResultItem): DownloadItem{ fun checkIfPresentForProcessing(item: ResultItem): DownloadItem{

View file

@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.work.DownloadWorker
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -261,29 +262,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
return list return list
} }
fun putDownloadsForProcessing(items: List<ResultItem?>, downloadItems: List<DownloadItem>) : LiveData<List<Long>> {
val result = MutableLiveData<List<Long>>()
viewModelScope.launch(Dispatchers.IO){
val list : MutableList<Long> = mutableListOf()
items.forEachIndexed { i, it ->
val tmpDownloadItem = downloadItems[i]
try {
val item = repository.checkIfPresentForProcessing(it!!)
tmpDownloadItem.id = item.id
tmpDownloadItem.status = DownloadRepository.Status.Processing.toString()
repository.update(tmpDownloadItem)
list.add(tmpDownloadItem.id)
}catch (e: Exception){
val id = repository.insert(tmpDownloadItem)
list.add(id)
}
}
result.postValue(list)
}
return result
}
fun deleteCancelled() = viewModelScope.launch(Dispatchers.IO) { fun deleteCancelled() = viewModelScope.launch(Dispatchers.IO) {
repository.deleteCancelled() repository.deleteCancelled()
} }
@ -292,15 +270,19 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.deleteErrored() repository.deleteErrored()
} }
fun deleteQueued() = viewModelScope.launch(Dispatchers.IO) { fun cancelQueued() = viewModelScope.launch(Dispatchers.IO) {
repository.deleteQueued() repository.cancelQueued()
} }
fun cloneFormat(item: Format) : Format { private fun cloneFormat(item: Format) : Format {
val string = Gson().toJson(item, Format::class.java) val string = Gson().toJson(item, Format::class.java)
return Gson().fromJson(string, Format::class.java) return Gson().fromJson(string, Format::class.java)
} }
fun getActiveDownloads() : List<DownloadItem>{
return repository.getActiveDownloads()
}
suspend fun queueDownloads(items: List<DownloadItem>) { suspend fun queueDownloads(items: List<DownloadItem>) {
val context = getApplication<App>().applicationContext val context = getApplication<App>().applicationContext
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true) val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)

View file

@ -138,10 +138,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
requireActivity() requireActivity()
) )
recyclerView = view.findViewById(R.id.recyclerViewHome) recyclerView = view.findViewById(R.id.recyclerViewHome)
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)){ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)){
recyclerView?.layoutManager = GridLayoutManager(context, 2) recyclerView?.layoutManager = GridLayoutManager(context, 2)
}else{
recyclerView?.layoutManager = LinearLayoutManager(context)
} }
recyclerView?.adapter = homeAdapter recyclerView?.adapter = homeAdapter
@ -478,8 +476,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
if (sharedPreferences!!.getBoolean("download_card", true)) { if (sharedPreferences!!.getBoolean("download_card", true)) {
showSingleDownloadSheet(item!!, type!!) showSingleDownloadSheet(item!!, type!!)
} else { } else {
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch{
val downloadItem = downloadViewModel.createDownloadItemFromResult(item!!, type!!) val downloadItem = withContext(Dispatchers.IO){
downloadViewModel.createDownloadItemFromResult(item!!, type!!)
}
downloadViewModel.queueDownloads(listOf(downloadItem)) downloadViewModel.queueDownloads(listOf(downloadItem))
} }
} }

View file

@ -134,9 +134,13 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
val formatListener = object : OnFormatClickListener { val formatListener = object : OnFormatClickListener {
override fun onFormatClick(allFormats: List<List<Format>>, selectedFormats: List<Format>) { override fun onFormatClick(allFormats: List<List<Format>>, selectedFormats: List<Format>) {
val formatCollection = mutableListOf<List<Format>>()
allFormats.forEach {f ->
formatCollection.add(f.mapTo(mutableListOf()) {it.copy()})
}
items.forEachIndexed { index, it -> items.forEachIndexed { index, it ->
it.allFormats.clear() it.allFormats.clear()
it.allFormats.addAll(allFormats[index]) it.allFormats.addAll(formatCollection[index])
it.format = selectedFormats[index] it.format = selectedFormats[index]
} }
listAdapter.submitList(items.toList()) listAdapter.submitList(items.toList())

View file

@ -70,25 +70,33 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
if (items.size > 1){ if (items.size > 1){
val hasGenericFormats = when(items.first()!!.type){ val hasGenericFormats = when(items.first()!!.type){
Type.audio -> formats.flatten().size == resources.getStringArray(R.array.audio_formats).size Type.audio -> formats.first().size == resources.getStringArray(R.array.audio_formats).size
else -> formats.flatten().size == resources.getStringArray(R.array.video_formats).size else -> formats.first().size == resources.getStringArray(R.array.video_formats).size
} }
if (!hasGenericFormats){ if (!hasGenericFormats){
formatCollection.addAll(formats) formatCollection.addAll(formats)
val flattenFormats = formats.flatten() val flattenFormats = formats.flatten()
val commonFormats = flattenFormats.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flattenFormats.first { f -> f.format_id == it.key } }.map { it.value } val commonFormats = flattenFormats.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flattenFormats.first { f -> f.format_id == it.key } }.map { it.value }
commonFormats.forEach { chosenFormats = commonFormats.mapTo(mutableListOf()) {it.copy()}
chosenFormats = when(items.first()?.type){
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> chosenFormats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
chosenFormats.forEach {
it.filesize = it.filesize =
flattenFormats.filter { f -> f.format_id == it.format_id } flattenFormats.filter { f -> f.format_id == it.format_id }
.sumOf { itt -> itt.filesize } .sumOf { itt -> itt.filesize }
} }
chosenFormats = commonFormats
}else{ }else{
chosenFormats = formats.flatten() chosenFormats = formats.flatten()
} }
addFormatsToView(linearLayout) addFormatsToView(linearLayout)
}else{ }else{
chosenFormats = formats.flatten() chosenFormats = formats.flatten()
chosenFormats = when(items.first()?.type){
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> chosenFormats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
addFormatsToView(linearLayout) addFormatsToView(linearLayout)
} }
@ -118,6 +126,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
//playlist format filtering //playlist format filtering
}else{ }else{
var progress = "0/${items.size}" var progress = "0/${items.size}"
formatCollection.clear()
refreshBtn.text = progress refreshBtn.text = progress
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
infoUtil.getFormatsMultiple(items.map { it!!.url }) { infoUtil.getFormatsMultiple(items.map { it!!.url }) {
@ -167,7 +176,6 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
} }
private fun addFormatsToView(linearLayout: LinearLayout){ private fun addFormatsToView(linearLayout: LinearLayout){
linearLayout.removeAllViews() linearLayout.removeAllViews()
Log.e("aa", chosenFormats.toString())
for (i in chosenFormats.lastIndex downTo 0){ for (i in chosenFormats.lastIndex downTo 0){
val format = chosenFormats[i] val format = chosenFormats[i]
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null) val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.OnClickListener import android.view.View.OnClickListener
@ -65,12 +66,11 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
activeRecyclerView = view.findViewById(R.id.download_recyclerview) activeRecyclerView = view.findViewById(R.id.download_recyclerview)
activeRecyclerView.adapter = activeDownloads activeRecyclerView.adapter = activeDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet) val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){ if (landScape){
activeRecyclerView.layoutManager = GridLayoutManager(context, 2) activeRecyclerView.layoutManager = GridLayoutManager(context, 2)
}else{
activeRecyclerView.layoutManager = LinearLayoutManager(context)
} }
if (resources.getBoolean(R.bool.isTablet)) activeRecyclerView.layoutManager = GridLayoutManager(context, 3)
downloadViewModel.activeDownloads.observe(viewLifecycleOwner) { downloadViewModel.activeDownloads.observe(viewLifecycleOwner) {
activeDownloads.submitList(it) activeDownloads.submitList(it)

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -69,12 +70,11 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
cancelledRecyclerView = view.findViewById(R.id.download_recyclerview) cancelledRecyclerView = view.findViewById(R.id.download_recyclerview)
cancelledRecyclerView.adapter = cancelledDownloads cancelledRecyclerView.adapter = cancelledDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet) val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){ if (landScape){
cancelledRecyclerView.layoutManager = GridLayoutManager(context, 2) cancelledRecyclerView.layoutManager = GridLayoutManager(context, 2)
}else{
cancelledRecyclerView.layoutManager = LinearLayoutManager(context)
} }
if (resources.getBoolean(R.bool.isTablet)) cancelledRecyclerView.layoutManager = GridLayoutManager(context, 3)
downloadViewModel.cancelledDownloads.observe(viewLifecycleOwner) { downloadViewModel.cancelledDownloads.observe(viewLifecycleOwner) {
items = it items = it

View file

@ -8,14 +8,21 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.await
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.yausername.youtubedl_android.YoutubeDL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class DownloadQueueActivity : AppCompatActivity(){ class DownloadQueueActivity : AppCompatActivity(){
@ -85,7 +92,7 @@ class DownloadQueueActivity : AppCompatActivity(){
R.id.clear_queue -> { R.id.clear_queue -> {
showDeleteDialog() { showDeleteDialog() {
cancelAllDownloads() cancelAllDownloads()
downloadViewModel.deleteQueued() downloadViewModel.cancelQueued()
} }
} }
R.id.clear_cancelled -> { R.id.clear_cancelled -> {
@ -118,7 +125,20 @@ class DownloadQueueActivity : AppCompatActivity(){
} }
private fun cancelAllDownloads() { private fun cancelAllDownloads() {
workManager.cancelAllWork() lifecycleScope.launch {
val notificationUtil = NotificationUtil(this@DownloadQueueActivity)
val activeDownloads = withContext(Dispatchers.IO){
downloadViewModel.getActiveDownloads()
}
val workManager = WorkManager.getInstance(this@DownloadQueueActivity)
activeDownloads.forEach {
val id = it.id.toInt()
YoutubeDL.getInstance().destroyProcessById(id.toString())
workManager.cancelUniqueWork(id.toString())
notificationUtil.cancelDownloadNotification(id)
}
workManager.cancelAllWorkByTag("download")
}
} }

View file

@ -5,6 +5,7 @@ import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -71,12 +72,11 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
erroredRecyclerView = view.findViewById(R.id.download_recyclerview) erroredRecyclerView = view.findViewById(R.id.download_recyclerview)
erroredRecyclerView.adapter = erroredDownloads erroredRecyclerView.adapter = erroredDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet) val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){ if (landScape){
erroredRecyclerView.layoutManager = GridLayoutManager(context, 2) erroredRecyclerView.layoutManager = GridLayoutManager(context, 2)
}else{
erroredRecyclerView.layoutManager = LinearLayoutManager(context)
} }
if (resources.getBoolean(R.bool.isTablet)) erroredRecyclerView.layoutManager = GridLayoutManager(context, 3)
downloadViewModel.erroredDownloads.observe(viewLifecycleOwner) { downloadViewModel.erroredDownloads.observe(viewLifecycleOwner) {
items = it items = it

View file

@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.InputType import android.text.InputType
import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.view.* import android.view.*
import android.view.View.GONE import android.view.View.GONE
@ -112,11 +113,11 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
requireActivity() requireActivity()
) )
recyclerView = view.findViewById(R.id.recyclerviewhistorys) recyclerView = view.findViewById(R.id.recyclerviewhistorys)
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)){ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)){
recyclerView?.layoutManager = GridLayoutManager(context, 2) recyclerView?.layoutManager = GridLayoutManager(context, 2)
}else{
recyclerView?.layoutManager = LinearLayoutManager(context)
} }
if (resources.getBoolean(R.bool.isTablet)) recyclerView?.layoutManager = GridLayoutManager(context, 3)
recyclerView?.adapter = historyAdapter recyclerView?.adapter = historyAdapter
noResults?.visibility = GONE noResults?.visibility = GONE
@ -483,7 +484,9 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
else redownload.visibility = GONE else redownload.visibility = GONE
bottomSheet!!.show() bottomSheet!!.show()
bottomSheet!!.behavior.peekHeight = 512 val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
bottomSheet!!.behavior.peekHeight = displayMetrics.heightPixels
bottomSheet!!.window!!.setLayout( bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT ViewGroup.LayoutParams.MATCH_PARENT

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -79,12 +80,11 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
queuedRecyclerView = view.findViewById(R.id.download_recyclerview) queuedRecyclerView = view.findViewById(R.id.download_recyclerview)
queuedRecyclerView.adapter = queuedDownloads queuedRecyclerView.adapter = queuedDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet) val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){ if (landScape){
queuedRecyclerView.layoutManager = GridLayoutManager(context, 2) queuedRecyclerView.layoutManager = GridLayoutManager(context, 2)
}else{
queuedRecyclerView.layoutManager = LinearLayoutManager(context)
} }
if (resources.getBoolean(R.bool.isTablet)) queuedRecyclerView.layoutManager = GridLayoutManager(context, 3)
downloadViewModel.queuedDownloads.observe(viewLifecycleOwner) { downloadViewModel.queuedDownloads.observe(viewLifecycleOwner) {
items = it items = it

View file

@ -18,6 +18,7 @@ import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.TextView import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -58,6 +59,7 @@ class TerminalActivity : AppCompatActivity() {
private lateinit var observer: FileObserver private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager private lateinit var imm : InputMethodManager
private lateinit var uiUtil: UiUtil private lateinit var uiUtil: UiUtil
private lateinit var fileUtil: FileUtil
var context: Context? = null var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
@ -68,7 +70,8 @@ class TerminalActivity : AppCompatActivity() {
downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt") downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt")
if (! downloadFile.exists()) downloadFile.createNewFile() if (! downloadFile.exists()) downloadFile.createNewFile()
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
uiUtil = UiUtil(FileUtil()) fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
context = baseContext context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview) scrollView = findViewById(R.id.custom_command_scrollview)
@ -105,7 +108,13 @@ class TerminalActivity : AppCompatActivity() {
} }
} }
R.id.shortcuts -> showShortcuts() R.id.shortcuts -> showShortcuts()
else -> {} R.id.folder -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
commandPathResultLauncher.launch(intent)
}
} }
true true
} }
@ -253,6 +262,21 @@ class TerminalActivity : AppCompatActivity() {
} }
} }
private var commandPathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let {
contentResolver?.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
}
input!!.text.insert(input!!.selectionStart, fileUtil.formatPath(result.data?.data.toString()))
}
}
private fun startDownload(command: String?) { private fun startDownload(command: String?) {
val cmd = if (command!!.contains("yt-dlp")) command.replace("yt-dlp", "") val cmd = if (command!!.contains("yt-dlp")) command.replace("yt-dlp", "")
else command else command

View file

@ -614,17 +614,19 @@ class InfoUtil(private val context: Context) {
var playlistTitle: String? = "" var playlistTitle: String? = ""
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title") if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
if(playlistTitle.equals(query)) playlistTitle = "" if(playlistTitle.equals(query)) playlistTitle = ""
val formatsInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JsonArray) jsonObject.getJSONArray("formats") else null val formatsInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JSONArray) jsonObject.getJSONArray("formats") else null
val formats : ArrayList<Format> = ArrayList() val formats : ArrayList<Format> = ArrayList()
if (formatsInJSON != null) { if (formatsInJSON != null) {
for (f in 0 until formatsInJSON.length()){ for (f in 0 until formatsInJSON.length()){
val format = formatsInJSON.getJSONObject(f) val format = formatsInJSON.getJSONObject(f)
val formatProper = Gson().fromJson(format.toString(), Format::class.java) val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.filesize > 0){ if (formatProper.filesize > 0){
if ( !formatProper.format_note.contains("audio only", true)) { if (format.has("format_note")){
formatProper.format_note = format.getString("format_note") if ( !formatProper.format_note.contains("audio only", true)) {
}else{ formatProper.format_note = format.getString("format_note")
formatProper.format_note = "${format.getString("format_note")} audio" }else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
} }
formatProper.container = format.getString("ext") formatProper.container = format.getString("ext")
formats.add(formatProper) formats.add(formatProper)

View file

@ -64,7 +64,7 @@ class TerminalDownloadWorker(
request.addOption("--cookies", cookiesFile.absolutePath) request.addOption("--cookies", cookiesFile.absolutePath)
} }
request.addOption("-P", tempFileDir.absolutePath) if (!command.contains("-P ")) request.addOption("-P", tempFileDir.absolutePath)
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false) val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
val logFolder = File(context.filesDir.absolutePath + "/logs") val logFolder = File(context.filesDir.absolutePath + "/logs")

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
</vector>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/incognito_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Incognito"
android:textStyle="bold"
android:visibility="gone"
android:padding="5dp"
android:background="?colorPrimaryContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/frame_layout"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/bottomNavigationView"
app:layout_constraintTop_toBottomOf="@+id/incognito_header"
/>
<com.google.android.material.navigationrail.NavigationRailView
android:id="@+id/bottomNavigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:labelVisibilityMode="unlabeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/homecoordinator"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.search.SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/search_hint"
app:layout_anchor="@id/search_bar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ScrollView
android:id="@+id/search_history_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/queries_constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/queries"
android:layout_margin="10dp"
app:chipSpacingVertical="-10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/init_search_query"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:singleLine="false" />
<com.google.android.material.button.MaterialButton
android:id="@+id/init_search_query"
style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:iconSize="20dp"
app:cornerRadius="10dp"
app:icon="@drawable/ic_search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="-10dp"
android:visibility="gone"
android:id="@+id/link_you_copied"
layout="@layout/search_suggestion_item"
/>
<LinearLayout
android:id="@+id/search_history_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
</ScrollView>
<ScrollView
android:id="@+id/search_suggestions_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/search_suggestions_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</LinearLayout>
</com.google.android.material.search.SearchView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/home_appbarlayout"
app:liftOnScroll="true"
android:background="@null"
android:elevation="0dp"
app:elevation="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
app:layout_scrollFlags="scroll"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/home_toolbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/Toolbar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/app_name" />
<com.google.android.material.search.SearchBar
android:id="@+id/search_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
app:layout_scrollFlags="scroll|enterAlways"
android:hint="@string/search_hint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@+id/home_toolbar"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/main_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:id="@+id/recyclerViewHome"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:spanCount="3"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
/>
<com.facebook.shimmer.ShimmerFrameLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shimmer_results_framelayout"
android:orientation="vertical">
<LinearLayout
android:id="@+id/shimmer_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="vertical">
<include layout="@layout/result_card_shimmer" />
<include layout="@layout/result_card_shimmer" />
<include layout="@layout/result_card_shimmer" />
<include layout="@layout/result_card_shimmer" />
<include layout="@layout/result_card_shimmer" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/home_fabs"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/download_all_coordinator"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/download_all_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_gravity="bottom|end"
android:text="@string/download_all"
app:icon="@drawable/ic_down"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/download_selected_coordinator"
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/download_selected_fab"
app:elevation="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:icon="@drawable/ic_down"
android:text="@string/download"
app:srcCompat="@drawable/ic_music"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -50,17 +50,18 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/download_type" android:id="@+id/download_type"
style="?attr/materialIconButtonFilledTonalStyle" style="?attr/materialIconButtonFilledTonalStyle"
app:backgroundTint="?attr/colorSurface" android:layout_width="24dp"
android:layout_width="32dp" android:layout_height="24dp"
android:layout_height="32dp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:insetLeft="0dp" android:insetLeft="0dp"
android:insetTop="0dp" android:insetTop="0dp"
android:insetRight="0dp" android:insetRight="0dp"
android:insetBottom="0dp" android:insetBottom="0dp"
app:backgroundTint="?attr/colorSurface"
app:icon="@drawable/ic_music" app:icon="@drawable/ic_music"
app:iconGravity="textStart"
app:iconPadding="0dp" app:iconPadding="0dp"
app:iconSize="13dp" app:iconSize="11dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Full" /> app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.Full" />
@ -68,17 +69,19 @@
<HorizontalScrollView <HorizontalScrollView
android:id="@+id/linearlayout2_horizontalscrollview" android:id="@+id/linearlayout2_horizontalscrollview"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_marginStart="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:scrollbars="none" android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@+id/title_author"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/download_type"> app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/download_type"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/linearLayout2" android:id="@+id/linearLayout2"
android:layout_width="wrap_content" android:layout_width="24dp"
android:layout_height="wrap_content" android:layout_height="24dp"
android:layout_gravity="center_vertical|end" android:layout_gravity="center_vertical|end"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingHorizontal="10dp" android:paddingHorizontal="10dp"
@ -89,40 +92,43 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clickable="false" android:clickable="false"
app:chipStrokeWidth="0.5dp"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.RoundedStart"
android:textSize="11sp" android:textSize="11sp"
app:chipBackgroundColor="?attr/colorSurface"
app:chipMinTouchTargetSize="0dp" app:chipMinTouchTargetSize="0dp"
app:chipStrokeWidth="0.5dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.RoundedStart" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/codec" android:id="@+id/codec"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.Middle"
android:clickable="false" android:clickable="false"
app:chipStrokeWidth="0.5dp"
android:textSize="11sp" android:textSize="11sp"
app:chipBackgroundColor="?attr/colorSurface"
app:chipMinTouchTargetSize="0dp" app:chipMinTouchTargetSize="0dp"
app:chipStrokeWidth="0.5dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.Middle" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/file_size" android:id="@+id/file_size"
app:chipStrokeWidth="0.5dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.RoundedEnd"
android:clickable="false" android:clickable="false"
android:textSize="11sp" android:textSize="11sp"
app:chipBackgroundColor="?attr/colorSurface"
app:chipMinTouchTargetSize="0dp" app:chipMinTouchTargetSize="0dp"
app:chipStrokeWidth="0.5dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Chip.RoundedEnd" />
</com.google.android.material.chip.ChipGroup> </com.google.android.material.chip.ChipGroup>
@ -131,13 +137,14 @@
<LinearLayout <LinearLayout
android:id="@+id/title_author" android:id="@+id/title_author"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="wrap_content"
android:dividerPadding="5dp" android:dividerPadding="5dp"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/output" app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@+id/active_download_cancel" app:layout_constraintEnd_toStartOf="@+id/active_download_cancel"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/download_type"> app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview">
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
@ -177,10 +184,26 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_cancel"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="10dp"
app:icon="@drawable/ic_cancel"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" />
<TextView <TextView
android:id="@+id/output" android:id="@+id/output"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:clickable="true" android:clickable="true"
android:ellipsize="end" android:ellipsize="end"
android:focusable="true" android:focusable="true"
@ -197,20 +220,6 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_cancel"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginTop="5dp"
app:cornerRadius="10dp"
app:backgroundTint="?attr/colorSurface"
app:iconTint="?android:textColorPrimary"
app:icon="@drawable/ic_cancel"
app:iconSize="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/title_author" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -2,11 +2,13 @@
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fillViewport="true" android:fillViewport="true"
android:scrollbars="none"> android:scrollbars="none">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/download_recyclerview" android:id="@+id/download_recyclerview"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

View file

@ -11,4 +11,9 @@
android:icon="@drawable/ic_shortcut" android:icon="@drawable/ic_shortcut"
app:showAsAction="ifRoom" app:showAsAction="ifRoom"
android:id="@+id/shortcuts"/> android:id="@+id/shortcuts"/>
<item
android:id="@+id/folder"
android:title="@string/command_directory"
android:icon="@drawable/baseline_folder_24"
app:showAsAction="collapseActionView|ifRoom"/>
</menu> </menu>

View file

@ -55,18 +55,18 @@
<style name="ShapeAppearanceOverlay.Chip.RoundedStart" parent="ShapeAppearance.MaterialComponents.SmallComponent"> <style name="ShapeAppearanceOverlay.Chip.RoundedStart" parent="ShapeAppearance.MaterialComponents.SmallComponent">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSizeTopLeft">15dp</item> <item name="cornerSizeTopLeft">12dp</item>
<item name="cornerSizeTopRight">0dp</item> <item name="cornerSizeTopRight">0dp</item>
<item name="cornerSizeBottomLeft">15dp</item> <item name="cornerSizeBottomLeft">12dp</item>
<item name="cornerSizeBottomRight">0dp</item> <item name="cornerSizeBottomRight">0dp</item>
</style> </style>
<style name="ShapeAppearanceOverlay.Chip.RoundedEnd" parent="ShapeAppearance.MaterialComponents.SmallComponent"> <style name="ShapeAppearanceOverlay.Chip.RoundedEnd" parent="ShapeAppearance.MaterialComponents.SmallComponent">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSizeTopLeft">0dp</item> <item name="cornerSizeTopLeft">0dp</item>
<item name="cornerSizeTopRight">15dp</item> <item name="cornerSizeTopRight">12dp</item>
<item name="cornerSizeBottomLeft">0dp</item> <item name="cornerSizeBottomLeft">0dp</item>
<item name="cornerSizeBottomRight">15dp</item> <item name="cornerSizeBottomRight">12dp</item>
</style> </style>
<style name="ShapeAppearanceOverlay.Chip.Middle" parent="ShapeAppearance.MaterialComponents.SmallComponent"> <style name="ShapeAppearanceOverlay.Chip.Middle" parent="ShapeAppearance.MaterialComponents.SmallComponent">