diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5cb71889..86104984 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,8 @@ - + @@ -35,12 +36,6 @@ - - - - - - @@ -49,20 +44,16 @@ - - - - - - - - - - - - + + + + + > = resultDao.getResults() + var itemCount = MutableLiveData(0) suspend fun insert(it: ResultItem){ resultDao.insert(it) @@ -26,23 +28,27 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp deleteAll() val infoUtil = InfoUtil(context) val items = infoUtil.getTrending(context) + itemCount.postValue(items.size) for (i in items){ resultDao.insert(i!!) } } - suspend fun search(inputQuery: String, resetResults: Boolean){ + suspend fun search(inputQuery: String, resetResults: Boolean) : ArrayList{ val infoUtil = InfoUtil(context) try{ if (resetResults) deleteAll() val res = infoUtil.search(inputQuery) + itemCount.postValue(res.size) res.forEach { resultDao.insert(it!!) } + return res }catch (ignored: Exception){} + return arrayListOf() } - suspend fun getOne(inputQuery: String, resetResults: Boolean){ + suspend fun getOne(inputQuery: String, resetResults: Boolean) : ArrayList{ var el: Array = inputQuery.split("/".toRegex()).dropLastWhile { it.isEmpty() } .toTypedArray() @@ -60,13 +66,16 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp try { val v = infoUtil.getVideo(query!!) if (resetResults) deleteAll() + itemCount.postValue(1) resultDao.insert(v!!) + return arrayListOf(v) } catch (e: Exception) { Log.e(tag, e.toString()) } + return arrayListOf() } - suspend fun getPlaylist(inputQuery: String, resetResults: Boolean){ + suspend fun getPlaylist(inputQuery: String, resetResults: Boolean) : ArrayList{ val query = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() } .toTypedArray()[1] var nextPageToken = "" @@ -81,26 +90,32 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp if (tmpToken == nextPageToken) break nextPageToken = tmpToken } while (true) + itemCount.postValue(items.size) items.forEach { resultDao.insert(it!!) } + return items } - suspend fun getDefault(inputQuery: String, resetResults: Boolean){ + suspend fun getDefault(inputQuery: String, resetResults: Boolean) : ArrayList { val infoUtil = InfoUtil(context) try { if (resetResults) deleteAll() val items = infoUtil.getFromYTDL(inputQuery) + itemCount.postValue(items.size) items.forEach { resultDao.insert(it!!) } + return items } catch (e: Exception) { Log.e(tag, e.toString()) } + return arrayListOf() } suspend fun deleteAll(){ + itemCount.postValue(0) resultDao.deleteAll() } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt index 1ff93e5a..3faa4bf0 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt @@ -57,6 +57,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application bestVideoFormat = Format( videoFormat[videoFormat.lastIndex], videoContainer!!, + "", + "", + "", 0, videoFormat[videoFormat.lastIndex] ) @@ -65,6 +68,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application bestAudioFormat = Format( "", audioContainer!!, + "", + "", + "", 0, "" ) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt index 7102f031..7c60e222 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt @@ -17,6 +17,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) private val repository : ResultRepository val items : LiveData> val loadingItems = MutableLiveData() + var itemCount : LiveData init { val dao = DBManager.getInstance(application).resultDao @@ -24,6 +25,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) repository = ResultRepository(dao, commandDao, getApplication().applicationContext) items = repository.allResults loadingItems.postValue(false) + itemCount = repository.itemCount } fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ @@ -41,7 +43,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) } fun getTrending() = viewModelScope.launch(Dispatchers.IO){ loadingItems.postValue(true) - repository.updateTrending(); + repository.updateTrending() loadingItems.postValue(false) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt index 89164c9c..eba2e4ca 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt @@ -1,34 +1,50 @@ package com.deniscerri.ytdlnis.receiver +import android.Manifest +import android.app.Activity import android.content.Context +import android.content.DialogInterface 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.net.Uri +import android.os.Build +import android.os.Build.VERSION_CODES import android.os.Bundle -import android.util.Log +import android.provider.Settings +import android.view.Window import android.view.WindowManager -import androidx.fragment.app.FragmentManager -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 android.widget.TextView import androidx.activity.ComponentActivity +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat import androidx.core.view.ViewCompat 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 private lateinit var resultViewModel: ResultViewModel private lateinit var downloadViewModel: DownloadViewModel + private lateinit var sharedPreferences: SharedPreferences + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -37,12 +53,28 @@ class ShareActivity : ComponentActivity() { v.setPadding(0, 0, 0, 0) insets } - window.setBackgroundDrawable(ColorDrawable(0)) - window.setLayout( - WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.MATCH_PARENT - ) + window.run { + setBackgroundDrawable(ColorDrawable(0)) + setLayout( + 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) } @@ -55,46 +87,130 @@ class ShareActivity : ComponentActivity() { val action = intent.action val type = intent.type if (Intent.ACTION_SEND == action && type != null) { - Log.e(TAG, action) - moreFragment = MoreFragment() - if (type.equals("application/txt", ignoreCase = true)) { - try { - val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) - val `is` = contentResolver.openInputStream(uri!!) - val textBuilder = StringBuilder() - 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 loadingBottomSheet = BottomSheetDialog(this) + loadingBottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) + loadingBottomSheet.setContentView(R.layout.please_wait_bottom_sheet) + loadingBottomSheet.show() + val cancel = loadingBottomSheet.findViewById(R.id.cancel) + cancel!!.setOnClickListener { + this.finish() + } + + val inputQuery = intent.getStringExtra(Intent.EXTRA_TEXT) + try { + val result = resultViewModel.getItemByURL(inputQuery!!) + loadingBottomSheet.cancel() + showDownloadSheet(result) + }catch (e: Exception){ + 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]) + } } - val l = listOf(*textBuilder.toString().split("\n").toTypedArray()) - val lines = LinkedList(l) - } catch (e: Exception) { - e.printStackTrace() } - } else { - //TODO } } } + private fun showDownloadSheet(it: ResultItem){ + if (sharedPreferences.getBoolean("download_card", true)){ + val bottomSheet = DownloadBottomSheetDialog(it, DownloadViewModel.Type.video) + bottomSheet.show(supportFragmentManager, "downloadSingleSheet") + }else{ + 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, + 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() { finishAffinity() 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 { private const val TAG = "ShareActivity" - private lateinit var moreFragment: MoreFragment - private lateinit var fm: FragmentManager + } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 97cf8fed..7fdce1f9 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -128,13 +128,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi resultViewModel.items.observe(viewLifecycleOwner) { homeAdapter!!.submitList(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)){ downloadAllFabCoordinator!!.visibility = VISIBLE }else{ downloadAllFabCoordinator!!.visibility = GONE } - }else if (it.size == 1){ + }else if (resultViewModel.itemCount.value!! == 1){ if (sharedPreferences!!.getBoolean("download_card", true)){ if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){ showSingleDownloadSheet(it[0], DownloadViewModel.Type.video) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index 999a7921..bac99288 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -14,17 +14,21 @@ import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.deniscerri.ytdlnis.MainActivity 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.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.util.FileUtil +import com.deniscerri.ytdlnis.util.UiUtil import com.google.android.material.chip.Chip import com.google.android.material.textfield.TextInputLayout import kotlinx.coroutines.launch @@ -34,9 +38,9 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() { private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null - private var mainActivity: MainActivity? = null private lateinit var downloadViewModel : DownloadViewModel private lateinit var fileUtil : FileUtil + private lateinit var uiUtil : UiUtil private lateinit var title : TextInputLayout private lateinit var author : TextInputLayout @@ -56,10 +60,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() { _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false) activity = getActivity() - mainActivity = activity as MainActivity? downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.audio) fileUtil = FileUtil() + uiUtil = UiUtil(fileUtil) return fragmentView } @@ -109,8 +113,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() { audioPathResultLauncher.launch(intent) } - val format = view.findViewById(R.id.format) - val formats = resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) } + var formats = resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) } val containers = requireContext().resources.getStringArray(R.array.audio_containers) val container = view.findViewById(R.id.downloadContainer) @@ -118,49 +121,35 @@ class DownloadAudioFragment(private var resultItem: ResultItem) : Fragment() { view.findViewById(R.id.container_textview) - + val formatCard = view.findViewById(R.id.format_card_constraintLayout) if (formats.isEmpty()) { - format.visibility = View.GONE + formatCard.visibility = View.GONE } else { - val formatTitles = formats.map { - if (it.format_note.contains("AUDIO_QUALITY_")) { - it.format_note.replace( - "AUDIO_QUALITY_", - "" - ) + if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize) - } else { - it.format_note + if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize( - it.filesize - ) - } - } - - val autoCompleteTextView = - view.findViewById(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 = - AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> - downloadItem.format.format_note = formats[index].format_note - 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) + val chosenFormat = formats[formats.lastIndex] + uiUtil.populateFormatCard(formatCard, chosenFormat) + val listener = object : OnFormatClickListener { + override fun onFormatClick(allFormats: List, item: Format) { + downloadItem.format = item + + if (containers.contains(item.container)){ + downloadItem.format.container = item.container + containerAutoCompleteTextView.setText(item.container, false) }else{ downloadItem.format.container = containers[0] containerAutoCompleteTextView.setText(containers[0], false) } + uiUtil.populateFormatCard(formatCard, item) + formats = allFormats + } + } + formatCard.setOnClickListener{_ -> + formats.forEach { + Log.e("aa", it.toString()) } + val bottomSheet = FormatSelectionBottomSheetDialog(formats, listener) + bottomSheet.show(parentFragmentManager, "formatSheet") + } + } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt index 33946b71..f92d9abd 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt @@ -16,12 +16,14 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.get import androidx.core.view.size import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.database.models.DownloadItem 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.receiver.ShareActivity import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment 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) val displayMetrics = DisplayMetrics() requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) - behavior.peekHeight = displayMetrics.heightPixels - 400 + behavior.peekHeight = displayMetrics.heightPixels - 700 } + tabLayout = view.findViewById(R.id.download_tablayout) viewPager2 = view.findViewById(R.id.download_viewpager) + (viewPager2.getChildAt(0) as? RecyclerView)?.apply { + isNestedScrollingEnabled = false + overScrollMode = View.OVER_SCROLL_NEVER + } + val fragmentManager = parentFragmentManager fragmentAdapter = DownloadFragmentAdapter( resultItem, @@ -158,18 +166,18 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val } private fun getDownloadItem() : DownloadItem{ - when(tabLayout.selectedTabPosition){ + return when(tabLayout.selectedTabPosition){ 0 -> { val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment - return f.downloadItem + f.downloadItem } 1 -> { val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment - return f.downloadItem + f.downloadItem } else -> { 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() } } + if (activity is ShareActivity){ + (activity as ShareActivity).finish() + } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt index c19f4a00..de1fa368 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt @@ -35,7 +35,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() { private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null - private var mainActivity: MainActivity? = null private lateinit var downloadViewModel : DownloadViewModel private lateinit var fileUtil : FileUtil @@ -52,7 +51,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() { _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false) activity = getActivity() - mainActivity = activity as MainActivity? downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, DownloadViewModel.Type.command) fileUtil = FileUtil() @@ -73,6 +71,9 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() { downloadItem.format = Format( chosenCommand!!.title, "", + "", + "", + "", 0, chosenCommand.content ) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index f6a2c767..269c2ab4 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewGroup import android.widget.* import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -28,16 +29,16 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type +import com.deniscerri.ytdlnis.util.UiUtil class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() { private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null - private var mainActivity: MainActivity? = null - private lateinit var resultViewModel : ResultViewModel private lateinit var downloadViewModel : DownloadViewModel private lateinit var fileUtil : FileUtil + private lateinit var uiUtil : UiUtil private lateinit var title : TextInputLayout private lateinit var author : TextInputLayout @@ -53,10 +54,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() { _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false) activity = getActivity() - mainActivity = activity as MainActivity? downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, Type.video) fileUtil = FileUtil() + uiUtil = UiUtil(fileUtil) return fragmentView } @@ -111,30 +112,12 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() { if (formats.isEmpty()) { val videoFormats = resources.getStringArray(R.array.video_formats) 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] val containers = requireContext().resources.getStringArray(R.array.video_containers) - val format = view.findViewById(R.id.format) - val autoCompleteTextView = - view.findViewById(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(R.id.downloadContainer) val containerAutoCompleteTextView = @@ -151,27 +134,27 @@ class DownloadVideoFragment(private val resultItem: ResultItem) : Fragment() { val selectedContainer: String = downloadItem.format.container containerAutoCompleteTextView!!.setText(selectedContainer, false) - (format!!.editText as AutoCompleteTextView?)!!.onItemClickListener = - AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> - downloadItem.format.format_note = formats[index].format_note - 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) + val formatCard = view.findViewById(R.id.format_card_constraintLayout) + val chosenFormat = formats[formats.lastIndex] + uiUtil.populateFormatCard(formatCard, chosenFormat) + val listener = object : OnFormatClickListener { + override fun onFormatClick(allFormats: List, item: Format) { + downloadItem.format = item + + if (containers.contains(item.container)){ + downloadItem.format.container = item.container + containerAutoCompleteTextView.setText(item.container, false) + }else{ + downloadItem.format.container = containers[0] + containerAutoCompleteTextView.setText(containers[0], false) } - - } - - (container!!.editText as AutoCompleteTextView?)!!.onItemClickListener = - AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long -> - downloadItem.format.container = containers[index] + uiUtil.populateFormatCard(formatCard, item) } - - - - + } + formatCard.setOnClickListener{_ -> + val bottomSheet = FormatSelectionBottomSheetDialog(formats, listener) + bottomSheet.show(parentFragmentManager, "formatSheet") + } val embedSubs = view.findViewById(R.id.embed_subtitles) embedSubs!!.isChecked = downloadItem.videoPreferences.embedSubs diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt new file mode 100644 index 00000000..73f75acc --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt @@ -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, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() { + private lateinit var behavior: BottomSheetBehavior + 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(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, item: Format) +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt new file mode 100644 index 00000000..5b44e91d --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt @@ -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(R.id.container).text = chosenFormat.container.uppercase() + formatCard.findViewById(R.id.format_note).text = chosenFormat.format_note.uppercase() + formatCard.findViewById(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(R.id.codec).visibility = View.GONE + }else{ + formatCard.findViewById(R.id.codec).text = codec + } + formatCard.findViewById(R.id.file_size).text = fileUtil.convertFileSize(chosenFormat.filesize) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt index 50c52797..69870525 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -114,11 +114,13 @@ class DownloadWorker( DownloadViewModel.Type.audio -> { request.addOption("-x") var audioQualityId : String = downloadItem.format.format_id - if (audioQualityId == "0" || audioQualityId.isEmpty()) audioQualityId = "ba" - request.addOption("-f", audioQualityId) + if (audioQualityId.isEmpty() || audioQualityId == "0") audioQualityId = "" + if (audioQualityId.isNotEmpty()){ + request.addOption("-f", audioQualityId) + } 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) }else{ request.addOption("--audio-format", sharedPreferences.getString("audio_format", "mp3")!!) diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml new file mode 100644 index 00000000..e2d336fa --- /dev/null +++ b/app/src/main/res/layout/activity_share.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/download_bottom_sheet.xml b/app/src/main/res/layout/download_bottom_sheet.xml index 062ac769..0cd19daf 100644 --- a/app/src/main/res/layout/download_bottom_sheet.xml +++ b/app/src/main/res/layout/download_bottom_sheet.xml @@ -6,111 +6,102 @@ android:orientation="vertical" android:layout_height="match_parent"> - - + android:layout_marginHorizontal="20dp" + android:orientation="horizontal" + android:paddingTop="20dp"> - - - - - - - -