Browse Source

Ready for release

master
CookieGamesOfficial 4 months ago
parent
commit
39ba13f79d
  1. 3
      .idea/gradle.xml
  2. 24
      app/src/androidTest/java/com/cookiejarapps/smartcookieweb_ytdl/ExampleInstrumentedTest.kt
  3. 261
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/fragments/HomeFragment.kt
  4. 17
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/item/VideoInfoItem.kt
  5. 59
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/models/VideoInfoViewModel.kt
  6. 17
      app/src/test/java/com/cookiejarapps/smartcookieweb_ytdl/ExampleUnitTest.kt
  7. 2
      gradle/wrapper/gradle-wrapper.properties
  8. 2
      settings.gradle

3
.idea/gradle.xml

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="C:/Program Files (x86)/gradle" />
<option name="gradleJvm" value="1.8 (2)" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

24
app/src/androidTest/java/com/cookiejarapps/smartcookieweb_ytdl/ExampleInstrumentedTest.kt

@ -1,24 +0,0 @@
package com.cookiejarapps.smartcookieweb_ytdl
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.cookiejarapps.smartcookieweb_ytdl", appContext.packageName)
}
}

261
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/fragments/HomeFragment.kt

@ -0,0 +1,261 @@
package com.cookiejarapps.smartcookieweb_ytdl.fragments
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.TextView.OnEditorActionListener
import android.widget.Toast
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import androidx.work.*
import com.cookiejarapps.smartcookieweb_ytdl.MainActivity
import com.cookiejarapps.smartcookieweb_ytdl.R
import com.cookiejarapps.smartcookieweb_ytdl.adapters.VideoAdapter
import com.cookiejarapps.smartcookieweb_ytdl.adapters.VideoInfoListener
import com.cookiejarapps.smartcookieweb_ytdl.item.VideoInfoItem
import com.cookiejarapps.smartcookieweb_ytdl.models.LoadState
import com.cookiejarapps.smartcookieweb_ytdl.models.VideoInfoViewModel
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.audioCodecKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.downloadDirKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.formatIdKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.nameKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.sizeKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.urlKey
import com.cookiejarapps.smartcookieweb_ytdl.worker.DownloadWorker.Companion.videoCodecKey
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_home.view.*
class HomeFragment : Fragment(),
SAFDialogFragment.DialogListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
initialize(view)
}
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
private fun initialize(view: View) {
urlEditText.setOnEditorActionListener { v, actionId, event ->
val handled = false
if (actionId == EditorInfo.IME_ACTION_GO) {
val vidFormatsVm =
ViewModelProvider(activity as MainActivity).get(VideoInfoViewModel::class.java)
vidFormatsVm.fetchInfo(urlEditText.text.toString())
view.let { activity?.hideKeyboard(it) }
}
handled
}
val videoFormatsModel =
ViewModelProvider(activity as MainActivity).get(VideoInfoViewModel::class.java)
with(view.video_list) {
adapter =
VideoAdapter(VideoInfoListener listener@{
videoFormatsModel.selectedItem = it
if (!isStoragePermissionGranted()) {
return@listener
}
SAFDialogFragment().show(
childFragmentManager,
downloadLocationDialogTag
)
})
}
videoFormatsModel.vidFormats.observe(viewLifecycleOwner, { t ->
(video_list.adapter as VideoAdapter).updateAdapter(t)
})
videoFormatsModel.loadState.observe(viewLifecycleOwner, { t ->
when (t) {
LoadState.INITIAL -> {
loading_indicator.visibility = GONE
loading_text.visibility = GONE
video_list.visibility = GONE
urlEditText.visibility = VISIBLE
start_text.visibility = VISIBLE
error_text.visibility = GONE
}
LoadState.LOADING -> {
loading_indicator.visibility = VISIBLE
loading_text.visibility = VISIBLE
urlEditText.visibility = GONE
start_text.visibility = GONE
video_list.visibility = GONE
error_text.visibility = GONE
}
LoadState.LOADED -> {
loading_indicator.visibility = GONE
loading_text.visibility = GONE
start_text.visibility = GONE
urlEditText.visibility = GONE
video_list.visibility = VISIBLE
}
LoadState.ERRORED -> {
loading_indicator.visibility = GONE
loading_text.visibility = GONE
video_list.visibility = GONE
urlEditText.visibility = VISIBLE
start_text.visibility = VISIBLE
error_text.visibility = VISIBLE
}
}
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
OPEN_DIRECTORY_REQUEST_CODE -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.let {
activity?.contentResolver?.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
setDownloadLocation(it.toString())
val videoFormatsModel =
ViewModelProvider(activity as MainActivity).get(VideoInfoViewModel::class.java)
startDownload(videoFormatsModel.selectedItem, it.toString())
}
}
}
}
}
private fun setDownloadLocation(path: String) {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
prefs.getString(DOWNLOAD_LOCATION, null) ?: prefs.edit()
.putString(DOWNLOAD_LOCATION, path).apply()
}
private fun startDownload(vidFormatItem: VideoInfoItem.VideoFormatItem, downloadDir: String) {
val videoInfo = vidFormatItem.vidInfo
val videoFormat = vidFormatItem.vidFormat
val workTag = videoInfo.id
val workManager = WorkManager.getInstance(activity?.applicationContext!!)
val state = workManager.getWorkInfosByTag(workTag).get()?.getOrNull(0)?.state
val running = state === WorkInfo.State.RUNNING || state === WorkInfo.State.ENQUEUED
if (running) {
Toast.makeText(
context,
R.string.download_already_running,
Toast.LENGTH_LONG
).show()
return
}
val workData = workDataOf(
urlKey to videoInfo.webpageUrl,
nameKey to videoInfo.title,
formatIdKey to videoFormat.formatId,
audioCodecKey to videoFormat.acodec,
videoCodecKey to videoFormat.vcodec,
downloadDirKey to downloadDir,
sizeKey to videoFormat.filesize
)
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(workTag)
.setInputData(workData)
.build()
workManager.enqueueUniqueWork(
workTag,
ExistingWorkPolicy.KEEP,
workRequest
)
Toast.makeText(
context,
R.string.download_queued,
Toast.LENGTH_LONG
).show()
}
override fun onAccept(dialog: SAFDialogFragment) {
val videoFormatsModel =
ViewModelProvider(activity as MainActivity).get(VideoInfoViewModel::class.java)
val path = PreferenceManager.getDefaultSharedPreferences(context)
.getString(DOWNLOAD_LOCATION, null)
if (path == null) {
Toast.makeText(context, R.string.invalid_download_location, Toast.LENGTH_SHORT).show()
return
}
startDownload(videoFormatsModel.selectedItem, path)
}
override fun onPickFile(dialog: SAFDialogFragment) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
}
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE)
}
private fun isStoragePermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
true
} else {
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
false
}
} else {
true
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == 1 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
SAFDialogFragment().show(
childFragmentManager,
downloadLocationDialogTag
)
}
}
companion object {
const val downloadLocationDialogTag = "download_location_chooser_dialog"
private const val OPEN_DIRECTORY_REQUEST_CODE = 37321
private const val DOWNLOAD_LOCATION = "download"
}
}

17
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/item/VideoInfoItem.kt

@ -0,0 +1,17 @@
package com.cookiejarapps.smartcookieweb_ytdl.item
import com.yausername.youtubedl_android.mapper.VideoFormat
import com.yausername.youtubedl_android.mapper.VideoInfo
sealed class VideoInfoItem {
abstract val id: String
data class VideoFormatItem(val vidInfo: VideoInfo, val formatId: String) : VideoInfoItem() {
override val id = vidInfo.id + "_" + formatId
val vidFormat: VideoFormat = vidInfo.formats.find { f -> f.formatId == formatId }!!
}
data class VideoHeaderItem(val vidInfo: VideoInfo) : VideoInfoItem() {
override val id: String = vidInfo.id
}
}

59
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/models/VideoInfoViewModel.kt

@ -0,0 +1,59 @@
package com.cookiejarapps.smartcookieweb_ytdl.models
import android.util.Log
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.cookiejarapps.smartcookieweb_ytdl.item.VideoInfoItem
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.mapper.VideoInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class VideoInfoViewModel : ViewModel() {
val vidFormats: MutableLiveData<VideoInfo> = MutableLiveData()
val loadState: MutableLiveData<LoadState> = MutableLiveData(LoadState.INITIAL)
private val thumbnail: MutableLiveData<String> = MutableLiveData()
lateinit var selectedItem: VideoInfoItem.VideoFormatItem
private fun submit(vidInfoItems: VideoInfo?) {
vidFormats.postValue(vidInfoItems)
}
private fun updateLoading(loadState: LoadState) {
this.loadState.postValue(loadState)
}
private fun updateThumbnail(thumbnail: String?) {
this.thumbnail.postValue(thumbnail)
}
fun fetchInfo(url: String) {
viewModelScope.launch {
updateLoading(LoadState.LOADING)
submit(null)
updateThumbnail(null)
lateinit var vidInfo: VideoInfo
try {
withContext(Dispatchers.IO) {
vidInfo = YoutubeDL.getInstance().getInfo(url)
}
} catch (e: Exception) {
updateLoading(LoadState.ERRORED)
return@launch
}
updateLoading(LoadState.LOADED)
updateThumbnail(vidInfo.thumbnail)
submit(vidInfo)
}
}
}
enum class LoadState {
INITIAL, LOADING, LOADED, ERRORED
}

17
app/src/test/java/com/cookiejarapps/smartcookieweb_ytdl/ExampleUnitTest.kt

@ -1,17 +0,0 @@
package com.cookiejarapps.smartcookieweb_ytdl
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

2
gradle/wrapper/gradle-wrapper.properties

@ -1,4 +1,4 @@
#Sun Feb 14 10:26:34 GMT 2021
#Sun Feb 14 17:30:10 GMT 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

2
settings.gradle

@ -1,2 +1,2 @@
rootProject.name='dvd'
include ':app'
rootProject.name = "SmartCookieWeb-ytdl"
Loading…
Cancel
Save