rework initial loading, don't crash on login timeout on app start
closes #25
This commit is contained in:
parent
7dc120ccfe
commit
3f45d769d2
|
@ -29,16 +29,25 @@ import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.commit
|
import androidx.fragment.app.commit
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
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.runBlocking
|
||||||
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.player.PlayerActivity
|
import org.mosad.teapod.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.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.DataTypes
|
||||||
import org.mosad.teapod.util.StorageController
|
import org.mosad.teapod.util.StorageController
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
|
import kotlin.system.exitProcess
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
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() {
|
private fun load() {
|
||||||
// running login and list in parallel does not bring any speed improvements
|
|
||||||
val time = measureTimeMillis {
|
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)
|
EncryptedPreferences.readCredentials(this)
|
||||||
if (EncryptedPreferences.password.isEmpty()) {
|
StorageController.load(this)
|
||||||
showLoginDialog(true)
|
|
||||||
} else {
|
try {
|
||||||
// try to login in, as most sites can only bee loaded once loged in
|
// make sure credentials are set, run's async
|
||||||
if (!AoDParser.login()) showLoginDialog(false)
|
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)
|
runBlocking { loadingJob.joinAll() } // wait for initial loading to finish
|
||||||
AoDParser.initialLoading()
|
|
||||||
|
|
||||||
wasInitialized = true
|
|
||||||
}
|
}
|
||||||
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) {
|
private fun showLoginDialog(firstTry: Boolean) {
|
||||||
|
@ -186,5 +212,12 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
|
||||||
startActivity(restartIntent)
|
startActivity(restartIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* exit and remove the app from tasks
|
||||||
|
*/
|
||||||
|
fun exitAndRemoveTask() {
|
||||||
|
this.finishAndRemoveTask()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -78,7 +78,7 @@ object AoDParser {
|
||||||
|
|
||||||
val resLogin = Jsoup.connect(baseUrl + loginPath)
|
val resLogin = Jsoup.connect(baseUrl + loginPath)
|
||||||
.method(Connection.Method.POST)
|
.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)
|
.data(data)
|
||||||
.postDataCharset("UTF-8")
|
.postDataCharset("UTF-8")
|
||||||
.cookies(authCookies)
|
.cookies(authCookies)
|
||||||
|
@ -96,20 +96,11 @@ object AoDParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initially load all media and home screen data
|
* initially load all media and home screen data
|
||||||
* -> blocking
|
|
||||||
*/
|
*/
|
||||||
fun initialLoading() = runBlocking {
|
fun initialLoading() = listOf(
|
||||||
val loadHomeJob = GlobalScope.async {
|
loadHome(),
|
||||||
loadHome()
|
|
||||||
}
|
|
||||||
|
|
||||||
val listJob = GlobalScope.async {
|
|
||||||
listAnimes()
|
listAnimes()
|
||||||
}
|
)
|
||||||
|
|
||||||
loadHomeJob.await()
|
|
||||||
listJob.await()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get a media by it's ID (int)
|
* get a media by it's ID (int)
|
||||||
|
@ -134,7 +125,7 @@ object AoDParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO don't use jsoup here
|
// 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(
|
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"),
|
||||||
|
@ -158,112 +149,99 @@ object AoDParser {
|
||||||
/**
|
/**
|
||||||
* load all media from aod into itemMediaList and mediaList
|
* load all media from aod into itemMediaList and mediaList
|
||||||
*/
|
*/
|
||||||
private fun listAnimes() = runBlocking {
|
private fun listAnimes() = GlobalScope.launch(Dispatchers.IO) {
|
||||||
if (sessionCookies.isEmpty()) login()
|
val resAnimes = Jsoup.connect(baseUrl + libraryPath).get()
|
||||||
|
//println(resAnimes)
|
||||||
|
|
||||||
withContext(Dispatchers.Default) {
|
itemMediaList.clear()
|
||||||
val resAnimes = Jsoup.connect(baseUrl + libraryPath)
|
mediaList.clear()
|
||||||
.cookies(sessionCookies)
|
resAnimes.select("div.animebox").forEach {
|
||||||
.get()
|
val type = if (it.select("p.animebox-link").select("a").text().toLowerCase(Locale.ROOT) == "zur serie") {
|
||||||
|
MediaType.TVSHOW
|
||||||
//println(resAnimes)
|
} else {
|
||||||
|
MediaType.MOVIE
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
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
|
* load new episodes, titles and highlights
|
||||||
*/
|
*/
|
||||||
private fun loadHome() = runBlocking {
|
private fun loadHome() = GlobalScope.launch(Dispatchers.IO) {
|
||||||
if (sessionCookies.isEmpty()) login()
|
val resHome = Jsoup.connect(baseUrl).get()
|
||||||
|
|
||||||
withContext(Dispatchers.Default) {
|
// get highlights from AoD
|
||||||
val resHome = Jsoup.connect(baseUrl)
|
highlightsList.clear()
|
||||||
.cookies(sessionCookies)
|
resHome.select("#aod-highlights").select("div.news-item").forEach {
|
||||||
.get()
|
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
|
if (mediaId != null) {
|
||||||
highlightsList.clear()
|
highlightsList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get all new episodes from AoD
|
// get all new episodes from AoD
|
||||||
newEpisodesList.clear()
|
newEpisodesList.clear()
|
||||||
resHome.select("h2:contains(Neue Episoden)").next().select("li").forEach {
|
resHome.select("h2:contains(Neue Episoden)").next().select("li").forEach {
|
||||||
val mediaId = it.select("a.thumbs").attr("href")
|
val mediaId = it.select("a.thumbs").attr("href")
|
||||||
.substringAfterLast("/").toIntOrNull()
|
.substringAfterLast("/").toIntOrNull()
|
||||||
val mediaImage = it.select("a.thumbs > img").attr("src")
|
val mediaImage = it.select("a.thumbs > img").attr("src")
|
||||||
val mediaTitle = "${it.select("a").text()} - ${it.select("span.neweps").text()}"
|
val mediaTitle = "${it.select("a").text()} - ${it.select("span.neweps").text()}"
|
||||||
|
|
||||||
if (mediaId != null) {
|
if (mediaId != null) {
|
||||||
newEpisodesList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
newEpisodesList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get new simulcasts from AoD
|
// get new simulcasts from AoD
|
||||||
newSimulcastsList.clear()
|
newSimulcastsList.clear()
|
||||||
resHome.select("h2:contains(Neue Simulcasts)").next().select("li").forEach {
|
resHome.select("h2:contains(Neue Simulcasts)").next().select("li").forEach {
|
||||||
val mediaId = it.select("a.thumbs").attr("href")
|
val mediaId = it.select("a.thumbs").attr("href")
|
||||||
.substringAfterLast("/").toIntOrNull()
|
.substringAfterLast("/").toIntOrNull()
|
||||||
val mediaImage = it.select("a.thumbs > img").attr("src")
|
val mediaImage = it.select("a.thumbs > img").attr("src")
|
||||||
val mediaTitle = it.select("a").text()
|
val mediaTitle = it.select("a").text()
|
||||||
|
|
||||||
if (mediaId != null) {
|
if (mediaId != null) {
|
||||||
newSimulcastsList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
newSimulcastsList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get new titles from AoD
|
// get new titles from AoD
|
||||||
newTitlesList.clear()
|
newTitlesList.clear()
|
||||||
resHome.select("h2:contains(Neue Anime-Titel)").next().select("li").forEach {
|
resHome.select("h2:contains(Neue Anime-Titel)").next().select("li").forEach {
|
||||||
val mediaId = it.select("a.thumbs").attr("href")
|
val mediaId = it.select("a.thumbs").attr("href")
|
||||||
.substringAfterLast("/").toIntOrNull()
|
.substringAfterLast("/").toIntOrNull()
|
||||||
val mediaImage = it.select("a.thumbs > img").attr("src")
|
val mediaImage = it.select("a.thumbs > img").attr("src")
|
||||||
val mediaTitle = it.select("a").text()
|
val mediaTitle = it.select("a").text()
|
||||||
|
|
||||||
if (mediaId != null) {
|
if (mediaId != null) {
|
||||||
newTitlesList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
newTitlesList.add(ItemMedia(mediaId, mediaTitle, mediaImage))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if highlights is empty, add a random new title
|
// if highlights is empty, add a random new title
|
||||||
if (highlightsList.isEmpty()) {
|
if (highlightsList.isEmpty()) {
|
||||||
if (newTitlesList.isNotEmpty()) {
|
if (newTitlesList.isNotEmpty()) {
|
||||||
highlightsList.add(newTitlesList[Random.nextInt(0, newTitlesList.size)])
|
highlightsList.add(newTitlesList[Random.nextInt(0, newTitlesList.size)])
|
||||||
} else {
|
} else {
|
||||||
highlightsList.add(ItemMedia(0,"", ""))
|
highlightsList.add(ItemMedia(0,"", ""))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,7 +250,7 @@ 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 suspend fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) {
|
private fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) {
|
||||||
if (sessionCookies.isEmpty()) login()
|
if (sessionCookies.isEmpty()) login()
|
||||||
|
|
||||||
if (!loginSuccess) {
|
if (!loginSuccess) {
|
||||||
|
@ -363,6 +341,7 @@ object AoDParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.i(javaClass.name, "media loaded successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -59,6 +59,8 @@
|
||||||
<!-- dialogs -->
|
<!-- dialogs -->
|
||||||
<string name="save">speichern</string>
|
<string name="save">speichern</string>
|
||||||
<string name="cancel">@android:string/cancel</string>
|
<string name="cancel">@android:string/cancel</string>
|
||||||
|
<string name="dialog_timeout_head">Anmelden fehlgeschlagen</string>
|
||||||
|
<string name="dialog_timeout_desc">Der Server scheint langsam zu antworten. Bitte versuche es später noch einmal.</string>
|
||||||
|
|
||||||
<!-- etc -->
|
<!-- etc -->
|
||||||
<string name="login">Login</string>
|
<string name="login">Login</string>
|
||||||
|
|
|
@ -75,6 +75,8 @@
|
||||||
<!-- dialogs -->
|
<!-- dialogs -->
|
||||||
<string name="save">save</string>
|
<string name="save">save</string>
|
||||||
<string name="cancel">@android:string/cancel</string>
|
<string name="cancel">@android:string/cancel</string>
|
||||||
|
<string name="dialog_timeout_head">Login failed</string>
|
||||||
|
<string name="dialog_timeout_desc">Looks like the server is taking to long to respond. Please try again later.</string>
|
||||||
|
|
||||||
<!-- etc -->
|
<!-- etc -->
|
||||||
<string name="login">Login</string>
|
<string name="login">Login</string>
|
||||||
|
|
Loading…
Reference in New Issue