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:
parent
46e3d1f1b6
commit
5e48e724a7
|
@ -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'
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue