added more details on format selection & implemented download card in share intent

pull/44/head
Denis Çerri 3 years ago
parent aa8a9270bd
commit 0047f8a833
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -5,7 +5,8 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
@ -35,12 +36,6 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
@ -49,20 +44,16 @@
</activity> </activity>
<activity android:name=".receiver.ShareActivity" <activity android:name=".receiver.ShareActivity"
android:theme="@style/Theme.BottomSheet"
android:configChanges="layoutDirection|locale" android:configChanges="layoutDirection|locale"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:exported="true"> android:exported="true">
<intent-filter>
<!-- <intent-filter>--> <action android:name="android.intent.action.SEND" />
<!-- <action android:name="android.intent.action.SEND" />--> <category android:name="android.intent.category.DEFAULT" />
<!-- <category android:name="android.intent.category.DEFAULT" />--> <data android:mimeType="text/plain" />
<!-- <data android:mimeType="text/plain" />--> </intent-filter>
<!-- </intent-filter>-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.SEND" />-->
<!-- <category android:name="android.intent.category.DEFAULT"/>-->
<!-- <data android:mimeType="application/txt" />-->
<!-- </intent-filter>-->
</activity> </activity>
<activity <activity
android:name=".ui.settings.SettingsActivity" android:name=".ui.settings.SettingsActivity"

@ -7,6 +7,12 @@ data class Format(
var format_id: String = "", var format_id: String = "",
@SerializedName(value = "ext", alternate = ["container"]) @SerializedName(value = "ext", alternate = ["container"])
var container: String = "", var container: String = "",
@SerializedName(value = "vcodec")
var vcodec: String = "",
@SerializedName(value = "acodec")
var acodec: String = "",
@SerializedName(value = "encoding")
var encoding: String = "",
@SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"]) @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"])
var filesize: Long = 0, var filesize: Long = 0,
@SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"]) @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"])

@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.database.repository
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.dao.ResultDao import com.deniscerri.ytdlnis.database.dao.ResultDao
import com.deniscerri.ytdlnis.database.models.CommandTemplate import com.deniscerri.ytdlnis.database.models.CommandTemplate
@ -13,6 +14,7 @@ import com.deniscerri.ytdlnis.util.InfoUtil
class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) { class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
private val tag: String = "ResultRepository" private val tag: String = "ResultRepository"
val allResults : LiveData<List<ResultItem>> = resultDao.getResults() val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
var itemCount = MutableLiveData(0)
suspend fun insert(it: ResultItem){ suspend fun insert(it: ResultItem){
resultDao.insert(it) resultDao.insert(it)
@ -26,23 +28,27 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
deleteAll() deleteAll()
val infoUtil = InfoUtil(context) val infoUtil = InfoUtil(context)
val items = infoUtil.getTrending(context) val items = infoUtil.getTrending(context)
itemCount.postValue(items.size)
for (i in items){ for (i in items){
resultDao.insert(i!!) resultDao.insert(i!!)
} }
} }
suspend fun search(inputQuery: String, resetResults: Boolean){ suspend fun search(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?>{
val infoUtil = InfoUtil(context) val infoUtil = InfoUtil(context)
try{ try{
if (resetResults) deleteAll() if (resetResults) deleteAll()
val res = infoUtil.search(inputQuery) val res = infoUtil.search(inputQuery)
itemCount.postValue(res.size)
res.forEach { res.forEach {
resultDao.insert(it!!) resultDao.insert(it!!)
} }
return res
}catch (ignored: Exception){} }catch (ignored: Exception){}
return arrayListOf()
} }
suspend fun getOne(inputQuery: String, resetResults: Boolean){ suspend fun getOne(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?>{
var el: Array<String?> = var el: Array<String?> =
inputQuery.split("/".toRegex()).dropLastWhile { it.isEmpty() } inputQuery.split("/".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray() .toTypedArray()
@ -60,13 +66,16 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
try { try {
val v = infoUtil.getVideo(query!!) val v = infoUtil.getVideo(query!!)
if (resetResults) deleteAll() if (resetResults) deleteAll()
itemCount.postValue(1)
resultDao.insert(v!!) resultDao.insert(v!!)
return arrayListOf(v)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, e.toString()) Log.e(tag, e.toString())
} }
return arrayListOf()
} }
suspend fun getPlaylist(inputQuery: String, resetResults: Boolean){ suspend fun getPlaylist(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?>{
val query = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() } val query = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[1] .toTypedArray()[1]
var nextPageToken = "" var nextPageToken = ""
@ -81,26 +90,32 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
if (tmpToken == nextPageToken) break if (tmpToken == nextPageToken) break
nextPageToken = tmpToken nextPageToken = tmpToken
} while (true) } while (true)
itemCount.postValue(items.size)
items.forEach { items.forEach {
resultDao.insert(it!!) resultDao.insert(it!!)
} }
return items
} }
suspend fun getDefault(inputQuery: String, resetResults: Boolean){ suspend fun getDefault(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?> {
val infoUtil = InfoUtil(context) val infoUtil = InfoUtil(context)
try { try {
if (resetResults) deleteAll() if (resetResults) deleteAll()
val items = infoUtil.getFromYTDL(inputQuery) val items = infoUtil.getFromYTDL(inputQuery)
itemCount.postValue(items.size)
items.forEach { items.forEach {
resultDao.insert(it!!) resultDao.insert(it!!)
} }
return items
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, e.toString()) Log.e(tag, e.toString())
} }
return arrayListOf()
} }
suspend fun deleteAll(){ suspend fun deleteAll(){
itemCount.postValue(0)
resultDao.deleteAll() resultDao.deleteAll()
} }

@ -57,6 +57,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
bestVideoFormat = Format( bestVideoFormat = Format(
videoFormat[videoFormat.lastIndex], videoFormat[videoFormat.lastIndex],
videoContainer!!, videoContainer!!,
"",
"",
"",
0, 0,
videoFormat[videoFormat.lastIndex] videoFormat[videoFormat.lastIndex]
) )
@ -65,6 +68,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
bestAudioFormat = Format( bestAudioFormat = Format(
"", "",
audioContainer!!, audioContainer!!,
"",
"",
"",
0, 0,
"" ""
) )

@ -17,6 +17,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
private val repository : ResultRepository private val repository : ResultRepository
val items : LiveData<List<ResultItem>> val items : LiveData<List<ResultItem>>
val loadingItems = MutableLiveData<Boolean>() val loadingItems = MutableLiveData<Boolean>()
var itemCount : LiveData<Int>
init { init {
val dao = DBManager.getInstance(application).resultDao val dao = DBManager.getInstance(application).resultDao
@ -24,6 +25,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext) repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext)
items = repository.allResults items = repository.allResults
loadingItems.postValue(false) loadingItems.postValue(false)
itemCount = repository.itemCount
} }
fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ fun checkTrending() = viewModelScope.launch(Dispatchers.IO){
@ -41,7 +43,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
} }
fun getTrending() = viewModelScope.launch(Dispatchers.IO){ fun getTrending() = viewModelScope.launch(Dispatchers.IO){
loadingItems.postValue(true) loadingItems.postValue(true)
repository.updateTrending(); repository.updateTrending()
loadingItems.postValue(false) loadingItems.postValue(false)
} }

@ -1,34 +1,50 @@
package com.deniscerri.ytdlnis.receiver package com.deniscerri.ytdlnis.receiver
import android.Manifest
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.provider.Settings
import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import androidx.fragment.app.FragmentManager import android.widget.TextView
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
import com.deniscerri.ytdlnis.ui.MoreFragment
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.Reader
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.*
import kotlin.system.exitProcess
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import kotlin.system.exitProcess
class ShareActivity : ComponentActivity() { class ShareActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var context: Context lateinit var context: Context
private lateinit var resultViewModel: ResultViewModel private lateinit var resultViewModel: ResultViewModel
private lateinit var downloadViewModel: DownloadViewModel private lateinit var downloadViewModel: DownloadViewModel
private lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -37,12 +53,28 @@ class ShareActivity : ComponentActivity() {
v.setPadding(0, 0, 0, 0) v.setPadding(0, 0, 0, 0)
insets insets
} }
window.setBackgroundDrawable(ColorDrawable(0))
window.setLayout( window.run {
setBackgroundDrawable(ColorDrawable(0))
setLayout(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT WindowManager.LayoutParams.MATCH_PARENT
) )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
} else {
setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
}
}
setContentView(R.layout.activity_share)
context = baseContext
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
sharedPreferences = getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
askPermissions()
val intent = intent
handleIntents(intent) handleIntents(intent)
} }
@ -55,46 +87,130 @@ class ShareActivity : ComponentActivity() {
val action = intent.action val action = intent.action
val type = intent.type val type = intent.type
if (Intent.ACTION_SEND == action && type != null) { if (Intent.ACTION_SEND == action && type != null) {
Log.e(TAG, action) val loadingBottomSheet = BottomSheetDialog(this)
moreFragment = MoreFragment() loadingBottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
if (type.equals("application/txt", ignoreCase = true)) { loadingBottomSheet.setContentView(R.layout.please_wait_bottom_sheet)
try { loadingBottomSheet.show()
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) val cancel = loadingBottomSheet.findViewById<TextView>(R.id.cancel)
val `is` = contentResolver.openInputStream(uri!!) cancel!!.setOnClickListener {
val textBuilder = StringBuilder() this.finish()
val reader: Reader = BufferedReader(
InputStreamReader(
`is`, Charset.forName(
StandardCharsets.UTF_8.name()
)
)
)
var c: Int
while (reader.read().also { c = it } != -1) {
textBuilder.append(c.toChar())
} }
val l = listOf(*textBuilder.toString().split("\n").toTypedArray())
val lines = LinkedList(l) val inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT)
try {
val result = resultViewModel.getItemByURL(inputQuery!!)
loadingBottomSheet.cancel()
showDownloadSheet(result)
}catch (e: Exception){ }catch (e: Exception){
e.printStackTrace() resultViewModel.deleteAll()
CoroutineScope(Dispatchers.IO).launch {
resultViewModel.parseQuery(inputQuery!!, true)
}
resultViewModel.items.observe(this) {
if (it.isNotEmpty()){
if(resultViewModel.itemCount.value!! == it.size){
loadingBottomSheet.cancel()
showDownloadSheet(it[0])
}
}
}
}
} }
}
private fun showDownloadSheet(it: ResultItem){
if (sharedPreferences.getBoolean("download_card", true)){
val bottomSheet = DownloadBottomSheetDialog(it, DownloadViewModel.Type.video)
bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
}else{ }else{
//TODO val downloadItem = downloadViewModel.createDownloadItemFromResult(it, DownloadViewModel.Type.video)
downloadViewModel.queueDownloads(listOf(downloadItem))
this.finish()
}
} }
private fun askPermissions() {
if (Build.VERSION.SDK_INT < VERSION_CODES.TIRAMISU && !checkFilePermission()) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
}else if (!checkNotificationPermission()){
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
1
)
} }
} }
private fun createDefaultFolders(){
val audio = File(getString(R.string.music_path))
val video = File(getString(R.string.video_path))
val command = File(getString(R.string.command_path))
audio.mkdirs()
video.mkdirs()
command.mkdirs()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
for (i in permissions.indices) {
if (permissions.contains(Manifest.permission.POST_NOTIFICATIONS)) break
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
createPermissionRequestDialog()
}else{
createDefaultFolders()
}
}
}
private fun exit() { private fun exit() {
finishAffinity() finishAffinity()
exitProcess(0) exitProcess(0)
} }
private fun checkFilePermission(): Boolean {
return (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED)
}
private fun checkNotificationPermission(): Boolean {
return (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED)
}
private fun createPermissionRequestDialog() {
val dialog = MaterialAlertDialogBuilder(this)
dialog.setTitle(getString(R.string.warning))
dialog.setMessage(getString(R.string.request_permission_desc))
dialog.setOnCancelListener { exit() }
dialog.setNegativeButton(getString(R.string.exit_app)) { _: DialogInterface?, _: Int -> exit() }
dialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
)
startActivity(intent)
exitProcess(0)
}
dialog.show()
}
override fun onConfigurationChanged(newConfig: Configuration) {
startActivity(Intent(this, MainActivity::class.java))
super.onConfigurationChanged(newConfig)
}
companion object { companion object {
private const val TAG = "ShareActivity" private const val TAG = "ShareActivity"
private lateinit var moreFragment: MoreFragment
private lateinit var fm: FragmentManager
} }
} }

@ -128,13 +128,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
resultViewModel.items.observe(viewLifecycleOwner) { resultViewModel.items.observe(viewLifecycleOwner) {
homeAdapter!!.submitList(it) homeAdapter!!.submitList(it)
resultsList = it resultsList = it
if(it.size > 1){ Log.e(TAG, resultViewModel.itemCount.toString())
if(resultViewModel.itemCount.value!! > 1){
if (it[0].playlistTitle.isNotEmpty() && it[0].playlistTitle != getString(R.string.trendingPlaylist)){ if (it[0].playlistTitle.isNotEmpty() && it[0].playlistTitle != getString(R.string.trendingPlaylist)){
downloadAllFabCoordinator!!.visibility = VISIBLE downloadAllFabCoordinator!!.visibility = VISIBLE
}else{ }else{
downloadAllFabCoordinator!!.visibility = GONE downloadAllFabCoordinator!!.visibility = GONE
} }
}else if (it.size == 1){ }else if (resultViewModel.itemCount.value!! == 1){
if (sharedPreferences!!.getBoolean("download_card", true)){ if (sharedPreferences!!.getBoolean("download_card", true)){
if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){ if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(it[0], DownloadViewModel.Type.video) showSingleDownloadSheet(it[0], DownloadViewModel.Type.video)

@ -14,17 +14,21 @@ import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView import android.widget.AutoCompleteTextView
import android.widget.TextView import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -34,9 +38,9 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var fileUtil : FileUtil private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout private lateinit var title : TextInputLayout
private lateinit var author : TextInputLayout private lateinit var author : TextInputLayout
@ -56,10 +60,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() {
_binding = FragmentHomeBinding.inflate(inflater, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false)
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity?
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.audio) downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.audio)
fileUtil = FileUtil() fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -109,8 +113,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() {
audioPathResultLauncher.launch(intent) audioPathResultLauncher.launch(intent)
} }
val format = view.findViewById<TextInputLayout>(R.id.format) var formats = resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) }
val formats = resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) }
val containers = requireContext().resources.getStringArray(R.array.audio_containers) val containers = requireContext().resources.getStringArray(R.array.audio_containers)
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer) val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
@ -118,49 +121,35 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() {
view.findViewById<AutoCompleteTextView>(R.id.container_textview) view.findViewById<AutoCompleteTextView>(R.id.container_textview)
val formatCard = view.findViewById<ConstraintLayout>(R.id.format_card_constraintLayout)
if (formats.isEmpty()) { if (formats.isEmpty()) {
format.visibility = View.GONE formatCard.visibility = View.GONE
} else { } else {
val formatTitles = formats.map { val chosenFormat = formats[formats.lastIndex]
if (it.format_note.contains("AUDIO_QUALITY_")) { uiUtil.populateFormatCard(formatCard, chosenFormat)
it.format_note.replace( val listener = object : OnFormatClickListener {
"AUDIO_QUALITY_", override fun onFormatClick(allFormats: List<Format>, item: Format) {
"" downloadItem.format = item
) + if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
if (containers.contains(item.container)){
downloadItem.format.container = item.container
containerAutoCompleteTextView.setText(item.container, false)
}else{ }else{
it.format_note + if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize( downloadItem.format.container = containers[0]
it.filesize containerAutoCompleteTextView.setText(containers[0], false)
)
} }
uiUtil.populateFormatCard(formatCard, item)
formats = allFormats
} }
val autoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
formatTitles
)
)
if (formatTitles.isNotEmpty()) {
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
} }
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener = formatCard.setOnClickListener{_ ->
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> formats.forEach {
downloadItem.format.format_note = formats[index].format_note Log.e("aa", it.toString())
downloadItem.format.format_id = formats[index].format_id
downloadItem.format.filesize = formats[index].filesize
if (containers.contains(formats[index].container)){
downloadItem.format.container = formats[index].container
containerAutoCompleteTextView.setText(formats[index].container, false)
}else{
downloadItem.format.container = containers[0]
containerAutoCompleteTextView.setText(containers[0], false)
} }
val bottomSheet = FormatSelectionBottomSheetDialog(formats, listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
} }
} }

@ -16,12 +16,14 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.get import androidx.core.view.get
import androidx.core.view.size import androidx.core.view.size
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
@ -57,12 +59,18 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
behavior = BottomSheetBehavior.from(view.parent as View) behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics() val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
behavior.peekHeight = displayMetrics.heightPixels - 400 behavior.peekHeight = displayMetrics.heightPixels - 700
} }
tabLayout = view.findViewById(R.id.download_tablayout) tabLayout = view.findViewById(R.id.download_tablayout)
viewPager2 = view.findViewById(R.id.download_viewpager) viewPager2 = view.findViewById(R.id.download_viewpager)
(viewPager2.getChildAt(0) as? RecyclerView)?.apply {
isNestedScrollingEnabled = false
overScrollMode = View.OVER_SCROLL_NEVER
}
val fragmentManager = parentFragmentManager val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter( fragmentAdapter = DownloadFragmentAdapter(
resultItem, resultItem,
@ -158,18 +166,18 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
} }
private fun getDownloadItem() : DownloadItem{ private fun getDownloadItem() : DownloadItem{
when(tabLayout.selectedTabPosition){ return when(tabLayout.selectedTabPosition){
0 -> { 0 -> {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
return f.downloadItem f.downloadItem
} }
1 -> { 1 -> {
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
return f.downloadItem f.downloadItem
} }
else -> { else -> {
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
return f.downloadItem f.downloadItem
} }
} }
} }
@ -192,6 +200,9 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit() parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit()
} }
} }
if (activity is ShareActivity){
(activity as ShareActivity).finish()
}
} }
} }

@ -35,7 +35,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var fileUtil : FileUtil private lateinit var fileUtil : FileUtil
@ -52,7 +51,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
_binding = FragmentHomeBinding.inflate(inflater, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity?
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, DownloadViewModel.Type.command) downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, DownloadViewModel.Type.command)
fileUtil = FileUtil() fileUtil = FileUtil()
@ -73,6 +71,9 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
downloadItem.format = Format( downloadItem.format = Format(
chosenCommand!!.title, chosenCommand!!.title,
"", "",
"",
"",
"",
0, 0,
chosenCommand.content chosenCommand.content
) )

@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -28,16 +29,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.util.UiUtil
class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() { class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private lateinit var fileUtil : FileUtil private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var title : TextInputLayout private lateinit var title : TextInputLayout
private lateinit var author : TextInputLayout private lateinit var author : TextInputLayout
@ -53,10 +54,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
_binding = FragmentHomeBinding.inflate(inflater, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false)
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity?
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.video) downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.video)
fileUtil = FileUtil() fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
return fragmentView return fragmentView
} }
@ -111,30 +112,12 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
if (formats.isEmpty()) { if (formats.isEmpty()) {
val videoFormats = resources.getStringArray(R.array.video_formats) val videoFormats = resources.getStringArray(R.array.video_formats)
val container = sharedPreferences.getString("video_format", "Default") val container = sharedPreferences.getString("video_format", "Default")
videoFormats.forEach { formats.add(Format(it, container!!, 0, it)) } videoFormats.forEach { formats.add(Format(it, container!!,"","", "",0, it)) }
} }
val formatTitles = formats.map {
it.format_note + if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
}
downloadItem.format = formats[formats.lastIndex] downloadItem.format = formats[formats.lastIndex]
val containers = requireContext().resources.getStringArray(R.array.video_containers) val containers = requireContext().resources.getStringArray(R.array.video_containers)
val format = view.findViewById<TextInputLayout>(R.id.format)
val autoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
formatTitles
)
)
if (formatTitles.isNotEmpty()) {
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
}
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer) val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView = val containerAutoCompleteTextView =
@ -151,27 +134,27 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() {
val selectedContainer: String = downloadItem.format.container val selectedContainer: String = downloadItem.format.container
containerAutoCompleteTextView!!.setText(selectedContainer, false) containerAutoCompleteTextView!!.setText(selectedContainer, false)
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener = val formatCard = view.findViewById<ConstraintLayout>(R.id.format_card_constraintLayout)
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> val chosenFormat = formats[formats.lastIndex]
downloadItem.format.format_note = formats[index].format_note uiUtil.populateFormatCard(formatCard, chosenFormat)
downloadItem.format.format_id = formats[index].format_id val listener = object : OnFormatClickListener {
downloadItem.format.filesize = formats[index].filesize override fun onFormatClick(allFormats: List<Format>, item: Format) {
downloadItem.format = item
if (containers.contains(formats[index].container)){ if (containers.contains(item.container)){
downloadItem.format.container = formats[index].container downloadItem.format.container = item.container
containerAutoCompleteTextView.setText(formats[index].container, false) containerAutoCompleteTextView.setText(item.container, false)
}else{
downloadItem.format.container = containers[0]
containerAutoCompleteTextView.setText(containers[0], false)
} }
uiUtil.populateFormatCard(formatCard, item)
} }
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.format.container = containers[index]
} }
formatCard.setOnClickListener{_ ->
val bottomSheet = FormatSelectionBottomSheetDialog(formats, listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
}
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles) val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
embedSubs!!.isChecked = downloadItem.videoPreferences.embedSubs embedSubs!!.isChecked = downloadItem.videoPreferences.embedSubs

@ -0,0 +1,104 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.TimePickerDialog
import android.content.DialogInterface
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.Log
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.widget.Adapter
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.get
import androidx.core.view.size
import androidx.lifecycle.ViewModelProvider
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointBackward
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.tabs.TabLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import java.util.*
class FormatSelectionBottomSheetDialog(private val formats: List<Format>, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() {
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
}
@SuppressLint("RestrictedApi")
override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
val view = LayoutInflater.from(context).inflate(R.layout.format_select_bottom_sheet, null)
dialog.setContentView(view)
dialog.setOnShowListener {
behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
behavior.peekHeight = displayMetrics.heightPixels / 2
}
val linearLayout = view.findViewById<LinearLayout>(R.id.format_list_linear_layout)
for (i in formats.lastIndex downTo 0){
val it = formats[i]
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
uiUtil.populateFormatCard(formatItem as ConstraintLayout, it)
formatItem.setOnClickListener{_ ->
listener.onFormatClick(formats, it)
dismiss()
}
linearLayout.addView(formatItem)
}
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
cleanUp()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
cleanUp()
}
private fun cleanUp(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
}
}
interface OnFormatClickListener{
fun onFormatClick(allFormats: List<Format>, item: Format)
}

@ -0,0 +1,30 @@
package com.deniscerri.ytdlnis.util
import android.view.View
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.Format
class UiUtil(private val fileUtil: FileUtil) {
fun populateFormatCard(formatCard : ConstraintLayout, chosenFormat: Format){
formatCard.findViewById<TextView>(R.id.container).text = chosenFormat.container.uppercase()
formatCard.findViewById<TextView>(R.id.format_note).text = chosenFormat.format_note.uppercase()
formatCard.findViewById<TextView>(R.id.format_id).text = "id: ${chosenFormat.format_id}"
val codec =
if (chosenFormat.encoding != "") {
chosenFormat.encoding.uppercase()
}else if (chosenFormat.vcodec != "none" && chosenFormat.vcodec != ""){
chosenFormat.vcodec.uppercase()
} else {
chosenFormat.acodec.uppercase()
}
if (codec == "" || codec == "none"){
formatCard.findViewById<TextView>(R.id.codec).visibility = View.GONE
}else{
formatCard.findViewById<TextView>(R.id.codec).text = codec
}
formatCard.findViewById<TextView>(R.id.file_size).text = fileUtil.convertFileSize(chosenFormat.filesize)
}
}

@ -114,11 +114,13 @@ class DownloadWorker(
DownloadViewModel.Type.audio -> { DownloadViewModel.Type.audio -> {
request.addOption("-x") request.addOption("-x")
var audioQualityId : String = downloadItem.format.format_id var audioQualityId : String = downloadItem.format.format_id
if (audioQualityId == "0" || audioQualityId.isEmpty()) audioQualityId = "ba" if (audioQualityId.isEmpty() || audioQualityId == "0") audioQualityId = ""
if (audioQualityId.isNotEmpty()){
request.addOption("-f", audioQualityId) request.addOption("-f", audioQualityId)
}
val ext = downloadItem.format.container val ext = downloadItem.format.container
if(ext != context.getString(R.string.defaultValue)){ if(ext != context.getString(R.string.defaultValue) && ext != "webm"){
request.addOption("--audio-format", ext) request.addOption("--audio-format", ext)
}else{ }else{
request.addOption("--audio-format", sharedPreferences.getString("audio_format", "mp3")!!) request.addOption("--audio-format", sharedPreferences.getString("audio_format", "mp3")!!)

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".receiver.ShareActivity"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

@ -6,25 +6,19 @@
android:orientation="vertical" android:orientation="vertical"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ScrollView <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp"
android:orientation="horizontal"
android:paddingTop="20dp"> android:paddingTop="20dp">
<TextView <TextView
@ -50,8 +44,8 @@
android:id="@+id/bottomsheet_schedule_button" android:id="@+id/bottomsheet_schedule_button"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal" style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginEnd="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_clock" app:icon="@drawable/ic_clock"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_download_button" app:layout_constraintEnd_toStartOf="@+id/bottomsheet_download_button"
@ -70,7 +64,6 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
@ -100,7 +93,6 @@
</com.google.android.material.tabs.TabLayout> </com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/download_viewpager" android:id="@+id/download_viewpager"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -110,7 +102,6 @@
</LinearLayout> </LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/format_card_constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/container"
android:layout_width="60dp"
android:background="?attr/colorPrimaryInverse"
android:textColor="@color/white"
android:clickable="false"
android:layout_height="55dp"
android:gravity="center"
android:minWidth="30dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout2"
android:layout_width="wrap_content"
android:layout_height="55dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/container"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/format_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="10dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/codec"
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="?attr/colorSecondary"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="10dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/file_size"
style="@style/Widget.Material3.FloatingActionButton.Large.Tertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="?attr/colorSecondary"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="10dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/format_id"
android:layout_width="wrap_content"
android:layout_height="55dp"
android:clickable="false"
android:gravity="bottom|end"
android:minWidth="30dp"
android:textColor="@color/white"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/format"
android:textSize="25sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_format"
android:paddingBottom="20dp"
android:textSize="12sp" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:scrollbars="none"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/format_list_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -66,7 +71,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
app:errorEnabled="true" android:paddingBottom="10dp"
android:hint="@string/title" android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox"> style="@style/Widget.Material3.TextInputLayout.FilledBox">
@ -81,6 +86,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
@ -88,7 +94,6 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="40" android:layout_weight="40"
app:errorEnabled="true"
android:hint="@string/author" android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox"> style="@style/Widget.Material3.TextInputLayout.FilledBox">
@ -122,27 +127,16 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.textfield.TextInputLayout <TextView
android:id="@+id/format" android:layout_width="wrap_content"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/audio_format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="none" android:text="@string/audio_format" />
app:simpleItems="@array/video_formats" <include layout="@layout/format_item" />
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath" android:id="@+id/outputPath"
android:paddingTop="10dp"
style="@style/Widget.Material3.TextInputLayout.FilledBox" style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -201,3 +195,5 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:scrollbars="none"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp" android:padding="10dp"
android:orientation="vertical"> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<!-- <androidx.constraintlayout.widget.ConstraintLayout--> <!-- <androidx.constraintlayout.widget.ConstraintLayout-->
@ -62,40 +65,38 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput" android:id="@+id/title_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
app:errorEnabled="true"
android:hint="@string/title" android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox"> android:paddingBottom="10dp">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content" android:layout_height="wrap_content"
/> android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal"
android:paddingBottom="10dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput" android:id="@+id/author_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="40" android:layout_weight="40"
app:errorEnabled="true" android:hint="@string/author">
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content" android:layout_height="wrap_content"
/> android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -104,70 +105,58 @@
android:id="@+id/downloadContainer" android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu" style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/container"> android:layout_weight="45"
android:hint="@string/container"
android:paddingStart="10dp">
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/container_textview" android:id="@+id/container_textview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="none" android:inputType="none"
app:simpleItems="@array/audio_containers" app:simpleItems="@array/audio_containers" />
/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.textfield.TextInputLayout <TextView
android:id="@+id/format" android:layout_width="wrap_content"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/video_format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="none" android:text="@string/video_format" />
app:simpleItems="@array/video_formats"
/>
</com.google.android.material.textfield.TextInputLayout> <include layout="@layout/format_item" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath" android:id="@+id/outputPath"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/save_dir" android:hint="@string/save_dir"
style="@style/Widget.Material3.TextInputLayout.FilledBox"> android:paddingTop="10dp">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:inputType="text" android:layout_height="wrap_content"
android:layout_height="wrap_content"/> android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<LinearLayout <LinearLayout
android:id="@+id/adjust_video" android:id="@+id/adjust_video"
android:layout_width="match_parent" android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
> android:padding="10dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:paddingBottom="5dp"
android:textSize="15sp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/adjust_video" /> android:paddingBottom="5dp"
android:text="@string/adjust_video"
android:textSize="15sp" />
<HorizontalScrollView <HorizontalScrollView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -176,8 +165,8 @@
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup" android:id="@+id/chipGroup"
android:layout_width="wrap_content" android:layout_width="wrap_content"
app:singleLine="false" android:layout_height="wrap_content"
android:layout_height="wrap_content"> app:singleLine="false">
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/embed_subtitles" android:id="@+id/embed_subtitles"
@ -215,4 +204,4 @@
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView>

@ -0,0 +1,27 @@
<?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"
android:layout_width="match_parent"
android:padding="20dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/please_wait"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:textColor="?attr/colorAccent"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -170,4 +170,7 @@
<string name="language">Language</string> <string name="language">Language</string>
<string name="command_templates">Command Templates</string> <string name="command_templates">Command Templates</string>
<string name="terminal">Terminal</string> <string name="terminal">Terminal</string>
<string name="format">Format</string>
<string name="select_format">Select Format</string>
<string name="please_wait">Please Wait</string>
</resources> </resources>

@ -9,6 +9,14 @@
<item name="dialogCornerRadius">28dp</item> <item name="dialogCornerRadius">28dp</item>
</style> </style>
<style name="Theme.BottomSheet" parent="Theme.Material3.DayNight.NoActionBar">
<item name="windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="AppTheme" parent="AppDefaultTheme" /> <style name="AppTheme" parent="AppDefaultTheme" />

Loading…
Cancel
Save