From 3f45d769d2cd6410718940316ebd78170e62cad1 Mon Sep 17 00:00:00 2001 From: Jannik Date: Wed, 13 Jan 2021 20:57:00 +0100 Subject: [PATCH] rework initial loading, don't crash on login timeout on app start closes #25 --- .../java/org/mosad/teapod/MainActivity.kt | 61 ++++-- .../java/org/mosad/teapod/parser/AoDParser.kt | 177 ++++++++---------- app/src/main/res/values-de-rDE/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 129 insertions(+), 113 deletions(-) diff --git a/app/src/main/java/org/mosad/teapod/MainActivity.kt b/app/src/main/java/org/mosad/teapod/MainActivity.kt index 86e9f2e..880f5d1 100644 --- a/app/src/main/java/org/mosad/teapod/MainActivity.kt +++ b/app/src/main/java/org/mosad/teapod/MainActivity.kt @@ -29,16 +29,25 @@ import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.commit +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.callbacks.onDismiss import com.google.android.material.bottomnavigation.BottomNavigationView +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.runBlocking import org.mosad.teapod.databinding.ActivityMainBinding import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.player.PlayerActivity import org.mosad.teapod.preferences.EncryptedPreferences import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.ui.components.LoginDialog -import org.mosad.teapod.ui.fragments.* +import org.mosad.teapod.ui.fragments.AccountFragment +import org.mosad.teapod.ui.fragments.HomeFragment +import org.mosad.teapod.ui.fragments.LibraryFragment +import org.mosad.teapod.ui.fragments.SearchFragment import org.mosad.teapod.util.DataTypes import org.mosad.teapod.util.StorageController +import java.net.SocketTimeoutException +import kotlin.system.exitProcess import kotlin.system.measureTimeMillis class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener { @@ -116,26 +125,43 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } } + /** + * initial loading and login are run in parallel, as initial loading doesn't require + * any login cookies + */ private fun load() { - // running login and list in parallel does not bring any speed improvements val time = measureTimeMillis { - Preferences.load(this) + val loadingJob = AoDParser.initialLoading() // start the initial loading - // make sure credentials are set, run's async + // load all saved stuff here + Preferences.load(this) EncryptedPreferences.readCredentials(this) - if (EncryptedPreferences.password.isEmpty()) { - showLoginDialog(true) - } else { - // try to login in, as most sites can only bee loaded once loged in - if (!AoDParser.login()) showLoginDialog(false) + StorageController.load(this) + + try { + // make sure credentials are set, run's async + if (EncryptedPreferences.password.isEmpty()) { + showLoginDialog(true) + } else { + // try to login in, as most sites can only bee loaded once loged in + if (!AoDParser.login()) showLoginDialog(false) + } + } catch (ex: SocketTimeoutException) { + Log.w(javaClass.name, "Timeout during login!") + + // show waring dialog before finishing + MaterialDialog(this).show { + title(R.string.dialog_timeout_head) + message(R.string.dialog_timeout_desc) + onDismiss { exitAndRemoveTask() } + } } - StorageController.load(this) - AoDParser.initialLoading() - - wasInitialized = true + runBlocking { loadingJob.joinAll() } // wait for initial loading to finish } - Log.i(javaClass.name, "login and list in $time ms") + Log.i(javaClass.name, "loading and login in $time ms") + + wasInitialized = true } private fun showLoginDialog(firstTry: Boolean) { @@ -186,5 +212,12 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS startActivity(restartIntent) } + /** + * exit and remove the app from tasks + */ + fun exitAndRemoveTask() { + this.finishAndRemoveTask() + exitProcess(0) + } } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt index 46d73b0..6905a19 100644 --- a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt +++ b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt @@ -78,7 +78,7 @@ object AoDParser { val resLogin = Jsoup.connect(baseUrl + loginPath) .method(Connection.Method.POST) - .timeout(60000) // login can take some time + .timeout(60000) // login can take some time default is 60000 (60 sec) .data(data) .postDataCharset("UTF-8") .cookies(authCookies) @@ -96,20 +96,11 @@ object AoDParser { /** * initially load all media and home screen data - * -> blocking */ - fun initialLoading() = runBlocking { - val loadHomeJob = GlobalScope.async { - loadHome() - } - - val listJob = GlobalScope.async { + fun initialLoading() = listOf( + loadHome(), listAnimes() - } - - loadHomeJob.await() - listJob.await() - } + ) /** * get a media by it's ID (int) @@ -134,7 +125,7 @@ object AoDParser { } // TODO don't use jsoup here - fun sendCallback(callbackPath: String) = GlobalScope.launch(Dispatchers.IO) { + private fun sendCallback(callbackPath: String) = GlobalScope.launch(Dispatchers.IO) { val headers = mutableMapOf( Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"), @@ -158,112 +149,99 @@ object AoDParser { /** * load all media from aod into itemMediaList and mediaList */ - private fun listAnimes() = runBlocking { - if (sessionCookies.isEmpty()) login() + private fun listAnimes() = GlobalScope.launch(Dispatchers.IO) { + val resAnimes = Jsoup.connect(baseUrl + libraryPath).get() + //println(resAnimes) - withContext(Dispatchers.Default) { - val resAnimes = Jsoup.connect(baseUrl + libraryPath) - .cookies(sessionCookies) - .get() - - //println(resAnimes) - - itemMediaList.clear() - mediaList.clear() - resAnimes.select("div.animebox").forEach { - val type = if (it.select("p.animebox-link").select("a").text().toLowerCase(Locale.ROOT) == "zur serie") { - MediaType.TVSHOW - } else { - MediaType.MOVIE - } - val mediaTitle = it.select("h3.animebox-title").text() - val mediaLink = it.select("p.animebox-link").select("a").attr("href") - val mediaImage = it.select("p.animebox-image").select("img").attr("src") - val mediaShortText = it.select("p.animebox-shorttext").text() - val mediaId = mediaLink.substringAfterLast("/").toInt() - - itemMediaList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) - mediaList.add(Media(mediaId, mediaLink, type).apply { - info.title = mediaTitle - info.posterUrl = mediaImage - info.shortDesc = mediaShortText - }) + itemMediaList.clear() + mediaList.clear() + resAnimes.select("div.animebox").forEach { + val type = if (it.select("p.animebox-link").select("a").text().toLowerCase(Locale.ROOT) == "zur serie") { + MediaType.TVSHOW + } else { + MediaType.MOVIE } + val mediaTitle = it.select("h3.animebox-title").text() + val mediaLink = it.select("p.animebox-link").select("a").attr("href") + val mediaImage = it.select("p.animebox-image").select("img").attr("src") + val mediaShortText = it.select("p.animebox-shorttext").text() + val mediaId = mediaLink.substringAfterLast("/").toInt() - Log.i(javaClass.name, "Total library size is: ${mediaList.size}") + itemMediaList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) + mediaList.add(Media(mediaId, mediaLink, type).apply { + info.title = mediaTitle + info.posterUrl = mediaImage + info.shortDesc = mediaShortText + }) } + + Log.i(javaClass.name, "Total library size is: ${mediaList.size}") } /** * load new episodes, titles and highlights */ - private fun loadHome() = runBlocking { - if (sessionCookies.isEmpty()) login() + private fun loadHome() = GlobalScope.launch(Dispatchers.IO) { + val resHome = Jsoup.connect(baseUrl).get() - withContext(Dispatchers.Default) { - val resHome = Jsoup.connect(baseUrl) - .cookies(sessionCookies) - .get() + // get highlights from AoD + highlightsList.clear() + resHome.select("#aod-highlights").select("div.news-item").forEach { + val mediaId = it.select("div.news-item-text").select("a.serienlink") + .attr("href").substringAfterLast("/").toIntOrNull() + val mediaTitle = it.select("div.news-title").select("h2").text() + val mediaImage = it.select("img").attr("src") - // get highlights from AoD - highlightsList.clear() - resHome.select("#aod-highlights").select("div.news-item").forEach { - val mediaId = it.select("div.news-item-text").select("a.serienlink") - .attr("href").substringAfterLast("/").toIntOrNull() - val mediaTitle = it.select("div.news-title").select("h2").text() - val mediaImage = it.select("img").attr("src") - - if (mediaId != null) { - highlightsList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) - } + if (mediaId != null) { + highlightsList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) } + } - // get all new episodes from AoD - newEpisodesList.clear() - resHome.select("h2:contains(Neue Episoden)").next().select("li").forEach { - val mediaId = it.select("a.thumbs").attr("href") - .substringAfterLast("/").toIntOrNull() - val mediaImage = it.select("a.thumbs > img").attr("src") - val mediaTitle = "${it.select("a").text()} - ${it.select("span.neweps").text()}" + // get all new episodes from AoD + newEpisodesList.clear() + resHome.select("h2:contains(Neue Episoden)").next().select("li").forEach { + val mediaId = it.select("a.thumbs").attr("href") + .substringAfterLast("/").toIntOrNull() + val mediaImage = it.select("a.thumbs > img").attr("src") + val mediaTitle = "${it.select("a").text()} - ${it.select("span.neweps").text()}" - if (mediaId != null) { - newEpisodesList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) - } + if (mediaId != null) { + newEpisodesList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) } + } - // get new simulcasts from AoD - newSimulcastsList.clear() - resHome.select("h2:contains(Neue Simulcasts)").next().select("li").forEach { - val mediaId = it.select("a.thumbs").attr("href") - .substringAfterLast("/").toIntOrNull() - val mediaImage = it.select("a.thumbs > img").attr("src") - val mediaTitle = it.select("a").text() + // get new simulcasts from AoD + newSimulcastsList.clear() + resHome.select("h2:contains(Neue Simulcasts)").next().select("li").forEach { + val mediaId = it.select("a.thumbs").attr("href") + .substringAfterLast("/").toIntOrNull() + val mediaImage = it.select("a.thumbs > img").attr("src") + val mediaTitle = it.select("a").text() - if (mediaId != null) { - newSimulcastsList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) - } + if (mediaId != null) { + newSimulcastsList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) } + } - // get new titles from AoD - newTitlesList.clear() - resHome.select("h2:contains(Neue Anime-Titel)").next().select("li").forEach { - val mediaId = it.select("a.thumbs").attr("href") - .substringAfterLast("/").toIntOrNull() - val mediaImage = it.select("a.thumbs > img").attr("src") - val mediaTitle = it.select("a").text() + // get new titles from AoD + newTitlesList.clear() + resHome.select("h2:contains(Neue Anime-Titel)").next().select("li").forEach { + val mediaId = it.select("a.thumbs").attr("href") + .substringAfterLast("/").toIntOrNull() + val mediaImage = it.select("a.thumbs > img").attr("src") + val mediaTitle = it.select("a").text() - if (mediaId != null) { - newTitlesList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) - } + if (mediaId != null) { + newTitlesList.add(ItemMedia(mediaId, mediaTitle, mediaImage)) } + } - // if highlights is empty, add a random new title - if (highlightsList.isEmpty()) { - if (newTitlesList.isNotEmpty()) { - highlightsList.add(newTitlesList[Random.nextInt(0, newTitlesList.size)]) - } else { - highlightsList.add(ItemMedia(0,"", "")) - } + // if highlights is empty, add a random new title + if (highlightsList.isEmpty()) { + if (newTitlesList.isNotEmpty()) { + highlightsList.add(newTitlesList[Random.nextInt(0, newTitlesList.size)]) + } else { + highlightsList.add(ItemMedia(0,"", "")) } } } @@ -272,7 +250,7 @@ object AoDParser { * load streams for the media path, movies have one episode * @param media is used as call ba reference */ - private suspend fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) { + private fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) { if (sessionCookies.isEmpty()) login() if (!loginSuccess) { @@ -363,6 +341,7 @@ object AoDParser { } } } + Log.i(javaClass.name, "media loaded successfully") } /** diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 034486b..bc52c0a 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -59,6 +59,8 @@ speichern @android:string/cancel + Anmelden fehlgeschlagen + Der Server scheint langsam zu antworten. Bitte versuche es später noch einmal. Login diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e0b71d..5ddf5ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -75,6 +75,8 @@ save @android:string/cancel + Login failed + Looks like the server is taking to long to respond. Please try again later. Login