I want to insert the VideoUrls object into the database through callback while enqueuing a network request through the volley library in Android just after I obtain the title from the response. Here is my code:
YoutubeDownloaderViewModel:
@HiltViewModel
class YoutubeDownloaderViewModel @Inject constructor(
private val videoUrlsDBRepository: VideoUrlsDBRepository,
private val videoUrlsNetworkRepository: VideoUrlsNetworkRepository
) : ViewModel() {
// LiveData for observing URLs from the database
val urls: LiveData<List<VideoUrls>> = videoUrlsDBRepository.getUrls().asLiveData()
private var downloadUrl: String = ""
private var deleteUrl: String = ""
private val outputUrls: MutableList<VideoItem> = mutableListOf()
var downloadId: Long = 0
private val _downloadCompleted = MutableLiveData<Boolean>()
val downloadCompleted: LiveData<Boolean>
get() = _downloadCompleted
private val _thumbnail = MutableLiveData<String>()
val thumbnail: LiveData<String>
get() = _thumbnail
private val _title = MutableLiveData<String>()
val title: LiveData<String>
get() = _title
init {
// Initializing LiveData values
_downloadCompleted.value = false
_thumbnail.value = ""
_title.value = ""
}
@SuppressLint("SimpleDateFormat")
fun insertUrl(url: String) {
// Deleting previous URL if any and inserting the new URL into the database
val temp = deleteUrl
deleteUrl = url
deleteUrl()
deleteUrl = temp
val sdf = SimpleDateFormat("dd/MM/yyyy")
val date = sdf.format(Date()).toString()
val time = Calendar.getInstance().time.toString()
var title=""
videoUrlsNetworkRepository.insertUrlRequestIntoQueue(
getYoutubeID(url),
updateTitle = {
title = it
Log.d("title update inside","title=${title} time=${System.currentTimeMillis()}")
viewModelScope.launch {
videoUrlsDBRepository.insertUrl(
VideoUrls(
videoUrl = url,
title = title,
date = date,
time = time
)
)
}
})
}
fun deleteUrl() {
// Deleting URL from the database
viewModelScope.launch {
videoUrlsDBRepository.deleteUrl(deleteUrl)
}
resetAll()
}
fun assignUrls(url: String) {
// Assigning download URL and delete URL and fetching thumbnail and title
downloadUrl = url
deleteUrl = url
thumbUrlAndTitle()
}
fun outPutVideos(pos: Int) {
// Processing video download request
outputUrls.addAll(videoUrlsNetworkRepository.outputVideosRequestIntoQueue(pos,getYoutubeID(downloadUrl), updateDownloadId = {
downloadId=it
}, updateDownloadLiveData = {
if(downloadId==it) _downloadCompleted.value=true
}))
}
fun resetAll() {
// Resetting all values to default
_downloadCompleted.value = false
outputUrls.clear()
_thumbnail.value = ""
_title.value = ""
}
fun isYoutubeUrl(youTubeURl: String): Boolean {
// Checking if URL is a valid YouTube URL
val pattern = Pattern.compile("^(http(s)?:\\/\\/)?((w){3}.)?youtu(be|.be)?(\\.com)?\\/.+")
val matcher = pattern.matcher(youTubeURl)
return matcher.matches()
}
private fun thumbUrlAndTitle() {
// Fetching thumbnail and title from API
videoUrlsNetworkRepository.insertThumbUrlAndTitleRequestIntoQueue(getYoutubeID(downloadUrl), updateThumbAndTitle = {
s1,s2->
_thumbnail.value=s1
_title.value=s2
})
}
private fun getYoutubeID(youtubeUrl: String): String? {
// Extracting YouTube video ID from the URL
if (TextUtils.isEmpty(youtubeUrl)) {
return ""
}
var video_id: String? = ""
val expression =
"^.*((youtu.be" + "\\/)" + "|(v\\/)|(\\/u\\/w\\/)|(embed\\/)|(watch\\?))\\??v?=?([^#\\&\\?]*).*" // var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
val input: CharSequence = youtubeUrl
val pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE)
val matcher = pattern.matcher(input)
if (matcher.matches()) {
val groupIndex1 = matcher.group(7)
if (groupIndex1 != null && groupIndex1.length == 11) video_id = groupIndex1
}
if (TextUtils.isEmpty(video_id)) {
if (youtubeUrl.contains("youtu.be/")) {
val spl = youtubeUrl.split("youtu.be/".toRegex()).toTypedArray()[1]
video_id = if (spl.contains("\\?")) {
spl.split("\\?".toRegex()).toTypedArray()[0]
} else {
spl
}
}
}
return video_id
}
}
VideoUrlsNetworkRepository:
class VideoUrlsNetworkRepository @Inject constructor(private val queue: RequestQueue,@ApplicationContext private val context: Context) {
fun insertUrlRequestIntoQueue(youtubeId:String?,updateTitle:(String)->Unit={}) {
val currUrl = "https://ytstream-download-youtube-videos.p.rapidapi.com/dl?id=${youtubeId}&rapidapi-key=1e59c5254cmsh65f87366aa4844fp138d18jsn4df36025d134"
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, currUrl, null, { response ->
updateTitle(response.getString("title"))
},
{ error ->
Log.d("insertion error", error.toString())
}
)
queue.add(jsonObjectRequest)
}
fun insertThumbUrlAndTitleRequestIntoQueue(youtubeId:String?,updateThumbAndTitle:(String,String)->Unit={
s1,s2->
}){
val currUrl =
"https://ytstream-download-youtube-videos.p.rapidapi.com/dl?id=${youtubeId}&rapidapi-key=1e59c5254cmsh65f87366aa4844fp138d18jsn4df36025d134"
val thumbAndTitle= mutableListOf<String>()
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, currUrl, null,
{ response ->
updateThumbAndTitle(
response.getString("thumb"),
response.getString("title")
)
},
{ error ->
Log.d("problem", error.toString())
}
)
queue.add(jsonObjectRequest)
}
fun outputVideosRequestIntoQueue(
pos:Int,
youtubeId: String?,
updateDownloadId:(Long)->Unit={},
updateDownloadLiveData:(Long?)->Unit={}
):List<VideoItem> {
val outputUrls:MutableList<VideoItem> = mutableListOf()
val currUrl =
"https://ytstream-download-youtube-videos.p.rapidapi.com/dl?id=${youtubeId}&rapidapi-key=1e59c5254cmsh65f87366aa4844fp138d18jsn4df36025d134"
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET, currUrl, null,
{ response ->
val obj = response.getJSONObject("link")
outputUrls.add(VideoItem(obj.getJSONArray("18").getString(0)))
outputUrls.add(VideoItem(obj.getJSONArray("22").getString(0)))
val DIRECTORY = "/YoutubeVideos/"
val DIRECTORY_FOLDER =
File("${Environment.getExternalStorageDirectory()}/Download/${DIRECTORY}")
if (!DIRECTORY_FOLDER.exists()) {
DIRECTORY_FOLDER.mkdirs()
}
val request = DownloadManager.Request(Uri.parse(outputUrls[pos].url))
.setTitle("YoutubeVideo_" + System.currentTimeMillis().toString() + ".mp4")
.setDescription("Downloading Video")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
.setAllowedOverMetered(true)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
DIRECTORY + "YoutubeVideo_" + System.currentTimeMillis().toString() + ".mp4"
)
val dm: DownloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
updateDownloadId(dm.enqueue(request))
MediaScannerConnection.scanFile(
context,
arrayOf(
File(
Environment.DIRECTORY_DOWNLOADS + "/" + DIRECTORY + "YoutubeVideo_" + System.currentTimeMillis()
.toString() + ".mp4"
).absolutePath
),
null
)
{ _, _ ->
}
val br = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
val id = p1?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
updateDownloadLiveData(id)
}
}
context.registerReceiver(br, (IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)))
},
{ error ->
Log.d("problem", error.toString())
}
)
queue.add(jsonObjectRequest)
return outputUrls
}
}
VideoUrlsDBRepository:
class VideoUrlsDBRepository @Inject constructor(private val videoUrlDao: VideoUrlDao) {
fun getUrls()=videoUrlDao.getUrls()
suspend fun insertUrl(videoUrl: VideoUrls){
Log.d("title update inside fun","title=${videoUrl.title} time=${System.currentTimeMillis()}")
videoUrlDao.insertUrl(videoUrl)
}
suspend fun deleteUrl(url:String)=videoUrlDao.deleteUrl(url)
}
Viewmodelscope.launch is not getting called inside lambda in YoutubeDownloaderViewModel and the title also is not getting updated.