update some libraries & coroutines 1.5.0

* androidx.core 1.3.2 -> 1.5.0
* androidx.appcompat 1.2.0 -> 1.3.0
* gson 2.8.6 -> 2.8.7
* coroutines-android 1.4.3 -> 1.5.0
  * don't use GlobalScope, use lifecycleScope and vieModelScope instead. This fixes a few issues when fragments where destroied befor the coroutine finished.
* gradle wrapper 7.0 -> 7.9.2
This commit is contained in:
Jannik 2021-06-06 17:54:19 +02:00
parent 46e3d1f1b6
commit 5e48e724a7
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
15 changed files with 305 additions and 301 deletions

View File

@ -41,18 +41,20 @@ android {
dependencies { dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'androidx.security:security-crypto:1.1.0-alpha03' implementation 'androidx.security:security-crypto:1.1.0-alpha03'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.7'
implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3' implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.3' implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.3' implementation 'com.google.android.exoplayer:exoplayer-dash:2.13.3'

View File

@ -99,10 +99,12 @@ object AoDParser {
/** /**
* initially load all media and home screen data * initially load all media and home screen data
*/ */
fun initialLoading() = listOf( suspend fun initialLoading() {
loadHome(), coroutineScope {
listAnimes() launch { loadHome() }
) launch { listAnimes() }
}
}
/** /**
* get a media by it's ID (int) * get a media by it's ID (int)
@ -121,9 +123,9 @@ object AoDParser {
/** /**
* get subscription info from aod website, remove "Anime-Abo" Prefix and trim * get subscription info from aod website, remove "Anime-Abo" Prefix and trim
*/ */
fun getSubscriptionInfoAsync(): Deferred<String> { suspend fun getSubscriptionInfoAsync(): Deferred<String> {
return GlobalScope.async(Dispatchers.IO) { return coroutineScope {
// get the subscription page async(Dispatchers.IO) {
val res = Jsoup.connect(baseUrl + subscriptionPath) val res = Jsoup.connect(baseUrl + subscriptionPath)
.cookies(sessionCookies) .cookies(sessionCookies)
.get() .get()
@ -132,12 +134,13 @@ object AoDParser {
.removePrefix("Anime-Abo").trim() .removePrefix("Anime-Abo").trim()
} }
} }
}
fun getSubscriptionUrl(): String { fun getSubscriptionUrl(): String {
return baseUrl + subscriptionPath return baseUrl + subscriptionPath
} }
fun markAsWatched(mediaId: Int, episodeId: Int) = GlobalScope.launch { suspend fun markAsWatched(mediaId: Int, episodeId: Int) {
val episode = getMediaById(mediaId).getEpisodeById(episodeId) val episode = getMediaById(mediaId).getEpisodeById(episodeId)
episode.watched = true episode.watched = true
sendCallback(episode.watchedCallback) sendCallback(episode.watchedCallback)
@ -146,7 +149,8 @@ object AoDParser {
} }
// TODO don't use jsoup here // TODO don't use jsoup here
private fun sendCallback(callbackPath: String) = GlobalScope.launch(Dispatchers.IO) { private suspend fun sendCallback(callbackPath: String) = coroutineScope {
launch(Dispatchers.IO) {
val headers = mutableMapOf( val headers = mutableMapOf(
Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"), Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"),
@ -164,13 +168,15 @@ object AoDParser {
} catch (ex: IOException) { } catch (ex: IOException) {
Log.e(javaClass.name, "Callback for $callbackPath failed.", ex) Log.e(javaClass.name, "Callback for $callbackPath failed.", ex)
} }
}
} }
/** /**
* load all media from aod into itemMediaList and mediaList * load all media from aod into itemMediaList and mediaList
* TODO private suspend fun listAnimes() = withContext(Dispatchers.IO) should also work, maybe a bug in android studio?
*/ */
private fun listAnimes() = GlobalScope.launch(Dispatchers.IO) { private suspend fun listAnimes() = withContext(Dispatchers.IO) {
launch(Dispatchers.IO) {
val resAnimes = Jsoup.connect(baseUrl + libraryPath).get() val resAnimes = Jsoup.connect(baseUrl + libraryPath).get()
//println(resAnimes) //println(resAnimes)
@ -198,11 +204,13 @@ object AoDParser {
Log.i(javaClass.name, "Total library size is: ${mediaList.size}") Log.i(javaClass.name, "Total library size is: ${mediaList.size}")
} }
}
/** /**
* load new episodes, titles and highlights * load new episodes, titles and highlights
*/ */
private fun loadHome() = GlobalScope.launch(Dispatchers.IO) { private suspend fun loadHome() = withContext(Dispatchers.IO) {
launch(Dispatchers.IO) {
val resHome = Jsoup.connect(baseUrl).get() val resHome = Jsoup.connect(baseUrl).get()
// get highlights from AoD // get highlights from AoD
@ -278,6 +286,9 @@ object AoDParser {
highlightsList.add(ItemMedia(0,"", "")) highlightsList.add(ItemMedia(0,"", ""))
} }
} }
Log.i(javaClass.name, "loaded home")
}
} }
/** /**
@ -286,7 +297,8 @@ object AoDParser {
* load streams for the media path, movies have one episode * load streams for the media path, movies have one episode
* @param media is used as call ba reference * @param media is used as call ba reference
*/ */
private fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) { private suspend fun loadStreams(media: Media) = coroutineScope {
launch(Dispatchers.IO) {
if (sessionCookies.isEmpty()) login() if (sessionCookies.isEmpty()) login()
if (!loginSuccess) { if (!loginSuccess) {
@ -393,6 +405,7 @@ object AoDParser {
} }
Log.i(javaClass.name, "media loaded successfully") Log.i(javaClass.name, "media loaded successfully")
} }
}
/** /**
* don't use Gson().fromJson() as we don't have any control over the api and it may change * don't use Gson().fromJson() as we don't have any control over the api and it may change
@ -402,7 +415,7 @@ object AoDParser {
return CompletableDeferred(AoDObject(listOf(), language)) return CompletableDeferred(AoDObject(listOf(), language))
} }
return GlobalScope.async(Dispatchers.IO) { return CoroutineScope(Dispatchers.IO).async(Dispatchers.IO) {
val headers = mutableMapOf( val headers = mutableMapOf(
Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"), Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"),

View File

@ -32,20 +32,19 @@ import androidx.fragment.app.commit
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.callbacks.onDismiss import com.afollestad.materialdialogs.callbacks.onDismiss
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import kotlinx.coroutines.joinAll import kotlinx.coroutines.*
import kotlinx.coroutines.runBlocking
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.ActivityMainBinding import org.mosad.teapod.databinding.ActivityMainBinding
import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.ui.activity.player.PlayerActivity
import org.mosad.teapod.preferences.EncryptedPreferences import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.components.LoginDialog
import org.mosad.teapod.ui.activity.main.fragments.AccountFragment import org.mosad.teapod.ui.activity.main.fragments.AccountFragment
import org.mosad.teapod.ui.activity.main.fragments.HomeFragment import org.mosad.teapod.ui.activity.main.fragments.HomeFragment
import org.mosad.teapod.ui.activity.main.fragments.LibraryFragment import org.mosad.teapod.ui.activity.main.fragments.LibraryFragment
import org.mosad.teapod.ui.activity.main.fragments.SearchFragment import org.mosad.teapod.ui.activity.main.fragments.SearchFragment
import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity import org.mosad.teapod.ui.activity.onboarding.OnboardingActivity
import org.mosad.teapod.ui.activity.player.PlayerActivity
import org.mosad.teapod.ui.components.LoginDialog
import org.mosad.teapod.util.DataTypes import org.mosad.teapod.util.DataTypes
import org.mosad.teapod.util.StorageController import org.mosad.teapod.util.StorageController
import org.mosad.teapod.util.exitAndRemoveTask import org.mosad.teapod.util.exitAndRemoveTask
@ -138,7 +137,8 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
*/ */
private fun load() { private fun load() {
val time = measureTimeMillis { val time = measureTimeMillis {
val loadingJob = AoDParser.initialLoading() // start the initial loading val loadingJob = CoroutineScope(Dispatchers.IO + CoroutineName("InitialLoadingScope"))
.async { AoDParser.initialLoading() } // start the initial loading
// load all saved stuff here // load all saved stuff here
Preferences.load(this) Preferences.load(this)
@ -165,7 +165,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
} }
} }
runBlocking { loadingJob.joinAll() } // wait for initial loading to finish runBlocking { loadingJob.await() } // wait for initial loading to finish
} }
Log.i(javaClass.name, "loading and login in $time ms") Log.i(javaClass.name, "loading and login in $time ms")

View File

@ -12,9 +12,9 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice import com.afollestad.materialdialogs.list.listItemsSingleChoice
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.mosad.teapod.BuildConfig import org.mosad.teapod.BuildConfig
import org.mosad.teapod.R import org.mosad.teapod.R
@ -64,7 +64,7 @@ class AccountFragment : Fragment() {
// load subscription (async) info before anything else // load subscription (async) info before anything else
binding.textAccountSubscription.text = getString(R.string.account_subscription, getString(R.string.loading)) binding.textAccountSubscription.text = getString(R.string.account_subscription, getString(R.string.loading))
GlobalScope.launch { lifecycleScope.launch {
binding.textAccountSubscription.text = getString( binding.textAccountSubscription.text = getString(
R.string.account_subscription, R.string.account_subscription,
AoDParser.getSubscriptionInfoAsync().await() AoDParser.getSubscriptionInfoAsync().await()

View File

@ -6,14 +6,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.databinding.FragmentHomeBinding import org.mosad.teapod.databinding.FragmentHomeBinding
import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.util.ItemMedia import org.mosad.teapod.util.ItemMedia
import org.mosad.teapod.util.StorageController import org.mosad.teapod.util.StorageController
import org.mosad.teapod.util.adapter.MediaItemAdapter import org.mosad.teapod.util.adapter.MediaItemAdapter
@ -40,7 +39,7 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
GlobalScope.launch(Dispatchers.Main) { lifecycleScope.launch {
context?.let { context?.let {
initHighlight() initHighlight()
initRecyclerViews() initRecyclerViews()
@ -101,7 +100,7 @@ class HomeFragment : Fragment() {
private fun initActions() { private fun initActions() {
binding.buttonPlayHighlight.setOnClickListener { binding.buttonPlayHighlight.setOnClickListener {
// TODO get next episode // TODO get next episode
GlobalScope.launch { lifecycleScope.launch {
val media = AoDParser.getMediaById(highlightMedia.id) val media = AoDParser.getMediaById(highlightMedia.id)
Log.d(javaClass.name, "Starting Player with mediaId: ${media.id}") Log.d(javaClass.name, "Starting Player with mediaId: ${media.id}")

View File

@ -5,10 +5,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import kotlinx.coroutines.Dispatchers import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.mosad.teapod.databinding.FragmentLibraryBinding import org.mosad.teapod.databinding.FragmentLibraryBinding
import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.adapter.MediaItemAdapter import org.mosad.teapod.util.adapter.MediaItemAdapter
@ -29,9 +27,8 @@ class LibraryFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// init async // init async
GlobalScope.launch { lifecycleScope.launch {
// create and set the adapter, needs context // create and set the adapter, needs context
withContext(Dispatchers.Main) {
context?.let { context?.let {
adapter = MediaItemAdapter(AoDParser.itemMediaList) adapter = MediaItemAdapter(AoDParser.itemMediaList)
adapter.onItemClick = { mediaId, _ -> adapter.onItemClick = { mediaId, _ ->
@ -41,7 +38,6 @@ class LibraryFragment : Fragment() {
binding.recyclerMediaLibrary.adapter = adapter binding.recyclerMediaLibrary.adapter = adapter
binding.recyclerMediaLibrary.addItemDecoration(MediaItemDecoration(9)) binding.recyclerMediaLibrary.addItemDecoration(MediaItemDecoration(9))
} }
}
} }
} }

View File

@ -10,20 +10,21 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import jp.wasabeef.glide.transformations.BlurTransformation import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.coroutines.* import kotlinx.coroutines.launch
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentMediaBinding import org.mosad.teapod.databinding.FragmentMediaBinding
import org.mosad.teapod.ui.activity.main.MainActivity import org.mosad.teapod.ui.activity.main.MainActivity
import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel import org.mosad.teapod.ui.activity.main.viewmodel.MediaFragmentViewModel
import org.mosad.teapod.util.*
import org.mosad.teapod.util.DataTypes.MediaType import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.Episode
import org.mosad.teapod.util.StorageController
/** /**
* The media detail fragment. * The media detail fragment.
@ -61,15 +62,14 @@ class MediaFragment(private val mediaId: Int) : Fragment() {
} }
}.attach() }.attach()
GlobalScope.launch(Dispatchers.Main) {
lifecycleScope.launch {
model.load(mediaId) // load the streams and tmdb for the selected media model.load(mediaId) // load the streams and tmdb for the selected media
if (this@MediaFragment.isAdded) {
updateGUI() updateGUI()
initActions() initActions()
} }
} }
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()

View File

@ -6,7 +6,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.SearchView import android.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import kotlinx.coroutines.* import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import org.mosad.teapod.databinding.FragmentSearchBinding import org.mosad.teapod.databinding.FragmentSearchBinding
import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.parser.AoDParser
import org.mosad.teapod.util.decoration.MediaItemDecoration import org.mosad.teapod.util.decoration.MediaItemDecoration
@ -26,9 +27,8 @@ class SearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
GlobalScope.launch { lifecycleScope.launch {
// create and set the adapter, needs context // create and set the adapter, needs context
withContext(Dispatchers.Main) {
context?.let { context?.let {
adapter = MediaItemAdapter(AoDParser.itemMediaList) adapter = MediaItemAdapter(AoDParser.itemMediaList)
adapter!!.onItemClick = { mediaId, _ -> adapter!!.onItemClick = { mediaId, _ ->
@ -40,7 +40,6 @@ class SearchFragment : Fragment() {
binding.recyclerMediaSearch.addItemDecoration(MediaItemDecoration(9)) binding.recyclerMediaSearch.addItemDecoration(MediaItemDecoration(9))
} }
} }
}
initActions() initActions()
} }

View File

@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.databinding.FragmentOnLoginBinding import org.mosad.teapod.databinding.FragmentOnLoginBinding
@ -35,7 +36,7 @@ class OnLoginFragment: Fragment() {
EncryptedPreferences.saveCredentials(email, password, requireContext()) // save the credentials EncryptedPreferences.saveCredentials(email, password, requireContext()) // save the credentials
binding.buttonLogin.isClickable = false binding.buttonLogin.isClickable = false
loginJob = GlobalScope.launch { loginJob = lifecycleScope.launch {
if (AoDParser.login()) { if (AoDParser.login()) {
// if login was successful, switch to main // if login was successful, switch to main
if (activity is OnboardingActivity) { if (activity is OnboardingActivity) {

View File

@ -19,6 +19,7 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.ui.StyledPlayerControlView import com.google.android.exoplayer2.ui.StyledPlayerControlView
@ -26,7 +27,6 @@ import com.google.android.exoplayer2.util.Util
import kotlinx.android.synthetic.main.activity_player.* import kotlinx.android.synthetic.main.activity_player.*
import kotlinx.android.synthetic.main.player_controls.* import kotlinx.android.synthetic.main.player_controls.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.mosad.teapod.R import org.mosad.teapod.R
@ -255,7 +255,7 @@ class PlayerActivity : AppCompatActivity() {
} }
timerUpdates = Timer().scheduleAtFixedRate(0, 500) { timerUpdates = Timer().scheduleAtFixedRate(0, 500) {
GlobalScope.launch { lifecycleScope.launch {
var btnNextEpIsVisible: Boolean var btnNextEpIsVisible: Boolean
var controlsVisible: Boolean var controlsVisible: Boolean

View File

@ -4,6 +4,7 @@ import android.app.Application
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.SimpleExoPlayer
@ -11,6 +12,7 @@ import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util import com.google.android.exoplayer2.util.Util
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.parser.AoDParser
@ -107,9 +109,11 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
// if episodes has not been watched, mark as watched // if episodes has not been watched, mark as watched
if (!episode.watched) { if (!episode.watched) {
viewModelScope.launch {
AoDParser.markAsWatched(media.id, episode.id) AoDParser.markAsWatched(media.id, episode.id)
} }
} }
}
fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) { fun playMedia(source: MediaSource, replace: Boolean = false, seekPosition: Long = 0) {
if (replace || player.contentDuration == C.TIME_UNSET) { if (replace || player.contentDuration == C.TIME_UNSET) {

View File

@ -3,16 +3,12 @@ package org.mosad.teapod.util
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import android.widget.Toast
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonParser import com.google.gson.JsonParser
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.mosad.teapod.R
import java.io.File import java.io.File
import java.io.FileReader import java.io.FileReader
import java.io.FileWriter import java.io.FileWriter
import java.lang.Exception
import java.net.URI
/** /**
* This controller contains the logic for permanently saved data. * This controller contains the logic for permanently saved data.
@ -45,7 +41,7 @@ object StorageController {
fun saveMyList(context: Context): Job { fun saveMyList(context: Context): Job {
val file = File(context.filesDir, fileNameMyList) val file = File(context.filesDir, fileNameMyList)
return GlobalScope.launch(Dispatchers.IO) { return CoroutineScope(Dispatchers.IO).launch {
file.writeText(Gson().toJson(myList.distinct())) file.writeText(Gson().toJson(myList.distinct()))
} }
} }

View File

@ -24,8 +24,8 @@ class TMDBApiController {
val searchTerm = title.replace("(Sub)", "").trim() val searchTerm = title.replace("(Sub)", "").trim()
return when (type) { return when (type) {
MediaType.MOVIE -> searchMovie(searchTerm).await() MediaType.MOVIE -> searchMovie(searchTerm)
MediaType.TVSHOW -> searchTVShow(searchTerm).await() MediaType.TVSHOW -> searchTVShow(searchTerm)
else -> { else -> {
Log.e(javaClass.name, "Wrong Type: $type") Log.e(javaClass.name, "Wrong Type: $type")
TMDBResponse() TMDBResponse()
@ -34,14 +34,13 @@ class TMDBApiController {
} }
fun searchTVShow(title: String): Deferred<TMDBResponse> { @Suppress("BlockingMethodInNonBlockingContext")
private suspend fun searchTVShow(title: String): TMDBResponse = withContext(Dispatchers.IO) {
val url = URL("$searchTVUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}") val url = URL("$searchTVUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}")
return GlobalScope.async {
val response = JsonParser.parseString(url.readText()).asJsonObject val response = JsonParser.parseString(url.readText()).asJsonObject
//println(response) //println(response)
if (response.get("total_results").asInt > 0) { return@withContext if (response.get("total_results").asInt > 0) {
response.get("results").asJsonArray.first().asJsonObject.let { response.get("results").asJsonArray.first().asJsonObject.let {
val id = getStringNotNull(it, "id").toInt() val id = getStringNotNull(it, "id").toInt()
val overview = getStringNotNull(it, "overview") val overview = getStringNotNull(it, "overview")
@ -54,16 +53,14 @@ class TMDBApiController {
TMDBResponse() TMDBResponse()
} }
} }
}
fun searchMovie(title: String): Deferred<TMDBResponse> { @Suppress("BlockingMethodInNonBlockingContext")
private suspend fun searchMovie(title: String): TMDBResponse = withContext(Dispatchers.IO) {
val url = URL("$searchMovieUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}") val url = URL("$searchMovieUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}")
return GlobalScope.async {
val response = JsonParser.parseString(url.readText()).asJsonObject val response = JsonParser.parseString(url.readText()).asJsonObject
//println(response) //println(response)
if (response.get("total_results").asInt > 0) { return@withContext if (response.get("total_results").asInt > 0) {
response.get("results").asJsonArray.first().asJsonObject.let { response.get("results").asJsonArray.first().asJsonObject.let {
val id = getStringNotNull(it,"id").toInt() val id = getStringNotNull(it,"id").toInt()
val overview = getStringNotNull(it,"overview") val overview = getStringNotNull(it,"overview")
@ -77,19 +74,16 @@ class TMDBApiController {
TMDBResponse() TMDBResponse()
} }
} }
}
/** /**
* currently only used for runtime, need a rework * currently only used for runtime, need a rework
*/ */
fun getMovieRuntime(id: Int): Int = runBlocking { @Suppress("BlockingMethodInNonBlockingContext")
suspend fun getMovieRuntime(id: Int): Int = withContext(Dispatchers.IO) {
val url = URL("$getMovieUrl/$id?api_key=$apiKey&language=$language") val url = URL("$getMovieUrl/$id?api_key=$apiKey&language=$language")
GlobalScope.async {
val response = JsonParser.parseString(url.readText()).asJsonObject val response = JsonParser.parseString(url.readText()).asJsonObject
return@withContext getStringNotNull(response,"runtime").toInt()
return@async getStringNotNull(response,"runtime").toInt()
}.await()
} }
/** /**

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = "1.5.0" ext.kotlin_version = "1.5.10"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists