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