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

View file

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

View file

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

View file

@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.work.DownloadWorker
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit
@ -261,29 +262,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
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) {
repository.deleteCancelled()
}
@ -292,15 +270,19 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.deleteErrored()
}
fun deleteQueued() = viewModelScope.launch(Dispatchers.IO) {
repository.deleteQueued()
fun cancelQueued() = viewModelScope.launch(Dispatchers.IO) {
repository.cancelQueued()
}
fun cloneFormat(item: Format) : Format {
private fun cloneFormat(item: Format) : Format {
val string = Gson().toJson(item, Format::class.java)
return Gson().fromJson(string, Format::class.java)
}
fun getActiveDownloads() : List<DownloadItem>{
return repository.getActiveDownloads()
}
suspend fun queueDownloads(items: List<DownloadItem>) {
val context = getApplication<App>().applicationContext
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)

View file

@ -138,10 +138,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
requireActivity()
)
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)
}else{
recyclerView?.layoutManager = LinearLayoutManager(context)
}
recyclerView?.adapter = homeAdapter
@ -478,8 +476,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
if (sharedPreferences!!.getBoolean("download_card", true)) {
showSingleDownloadSheet(item!!, type!!)
} else {
lifecycleScope.launch(Dispatchers.IO){
val downloadItem = downloadViewModel.createDownloadItemFromResult(item!!, type!!)
lifecycleScope.launch{
val downloadItem = withContext(Dispatchers.IO){
downloadViewModel.createDownloadItemFromResult(item!!, type!!)
}
downloadViewModel.queueDownloads(listOf(downloadItem))
}
}

View file

@ -134,9 +134,13 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
val formatListener = object : OnFormatClickListener {
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 ->
it.allFormats.clear()
it.allFormats.addAll(allFormats[index])
it.allFormats.addAll(formatCollection[index])
it.format = selectedFormats[index]
}
listAdapter.submitList(items.toList())

View file

@ -70,25 +70,33 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
if (items.size > 1){
val hasGenericFormats = when(items.first()!!.type){
Type.audio -> formats.flatten().size == resources.getStringArray(R.array.audio_formats).size
else -> formats.flatten().size == resources.getStringArray(R.array.video_formats).size
Type.audio -> formats.first().size == resources.getStringArray(R.array.audio_formats).size
else -> formats.first().size == resources.getStringArray(R.array.video_formats).size
}
if (!hasGenericFormats){
formatCollection.addAll(formats)
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 }
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 =
flattenFormats.filter { f -> f.format_id == it.format_id }
.sumOf { itt -> itt.filesize }
}
chosenFormats = commonFormats
}else{
chosenFormats = formats.flatten()
}
addFormatsToView(linearLayout)
}else{
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)
}
@ -118,6 +126,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
//playlist format filtering
}else{
var progress = "0/${items.size}"
formatCollection.clear()
refreshBtn.text = progress
withContext(Dispatchers.IO){
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
@ -167,7 +176,6 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
}
private fun addFormatsToView(linearLayout: LinearLayout){
linearLayout.removeAllViews()
Log.e("aa", chosenFormats.toString())
for (i in chosenFormats.lastIndex downTo 0){
val format = chosenFormats[i]
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.res.Configuration
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnClickListener
@ -65,12 +66,11 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
activeRecyclerView = view.findViewById(R.id.download_recyclerview)
activeRecyclerView.adapter = activeDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){
val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScape){
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) {
activeDownloads.submitList(it)

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.content.DialogInterface
import android.content.res.Configuration
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -69,12 +70,11 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
cancelledRecyclerView = view.findViewById(R.id.download_recyclerview)
cancelledRecyclerView.adapter = cancelledDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){
val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScape){
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) {
items = it

View file

@ -8,14 +8,21 @@ import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import androidx.work.WorkManager
import androidx.work.await
import com.deniscerri.ytdlnis.R
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.dialog.MaterialAlertDialogBuilder
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(){
@ -85,7 +92,7 @@ class DownloadQueueActivity : AppCompatActivity(){
R.id.clear_queue -> {
showDeleteDialog() {
cancelAllDownloads()
downloadViewModel.deleteQueued()
downloadViewModel.cancelQueued()
}
}
R.id.clear_cancelled -> {
@ -118,7 +125,20 @@ class DownloadQueueActivity : AppCompatActivity(){
}
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.res.Configuration
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -71,12 +72,11 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
erroredRecyclerView = view.findViewById(R.id.download_recyclerview)
erroredRecyclerView.adapter = erroredDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){
val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScape){
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) {
items = it

View file

@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.InputType
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.view.View.GONE
@ -112,11 +113,11 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
requireActivity()
)
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)
}else{
recyclerView?.layoutManager = LinearLayoutManager(context)
}
if (resources.getBoolean(R.bool.isTablet)) recyclerView?.layoutManager = GridLayoutManager(context, 3)
recyclerView?.adapter = historyAdapter
noResults?.visibility = GONE
@ -483,7 +484,9 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
else redownload.visibility = GONE
bottomSheet!!.show()
bottomSheet!!.behavior.peekHeight = 512
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
bottomSheet!!.behavior.peekHeight = displayMetrics.heightPixels
bottomSheet!!.window!!.setLayout(
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.os.Bundle
import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -79,12 +80,11 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
queuedRecyclerView = view.findViewById(R.id.download_recyclerview)
queuedRecyclerView.adapter = queuedDownloads
val landScapeOrTablet = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE || resources.getBoolean(R.bool.isTablet)
if (landScapeOrTablet){
val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && ! resources.getBoolean(R.bool.isTablet)
if (landScape){
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) {
items = it

View file

@ -18,6 +18,7 @@ import android.widget.EditText
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
@ -58,6 +59,7 @@ class TerminalActivity : AppCompatActivity() {
private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager
private lateinit var uiUtil: UiUtil
private lateinit var fileUtil: FileUtil
var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) {
@ -68,7 +70,8 @@ class TerminalActivity : AppCompatActivity() {
downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt")
if (! downloadFile.exists()) downloadFile.createNewFile()
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
uiUtil = UiUtil(FileUtil())
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview)
@ -105,7 +108,13 @@ class TerminalActivity : AppCompatActivity() {
}
}
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
}
@ -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?) {
val cmd = if (command!!.contains("yt-dlp")) command.replace("yt-dlp", "")
else command

View file

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

View file

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

View file

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

View file

@ -11,4 +11,9 @@
android:icon="@drawable/ic_shortcut"
app:showAsAction="ifRoom"
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>

View file

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