Fix mal login thanks @Arkon

Also added MAL activity to manifest + added log out support
This commit is contained in:
Carlos 2020-12-13 12:01:18 -05:00 committed by Jays2Kings
parent 5989c7119f
commit fbe4960c2e
9 changed files with 364 additions and 63 deletions

View File

@ -93,6 +93,9 @@
android:name=".widget.CustomLayoutPickerActivity"
android:label="@string/app_name"
android:theme="@style/FilePickerTheme" />
<activity
android:name=".ui.setting.track.MyAnimeListLoginActivity"
android:configChanges="uiMode|orientation|screenSize" />
<activity
android:name=".ui.setting.track.AnilistLoginActivity"
android:label="Anilist">

View File

@ -96,11 +96,11 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
return track
}
suspend fun login(csrfToken: String) = login("myanimelist", csrfToken)
override suspend fun login(username: String, password: String): Boolean {
logout()
return try {
val csrf = api.login(username, password)
saveCSRF(csrf)
saveCSRF(password)
saveCredentials(username, password)
true
} catch (e: Exception) {
@ -110,28 +110,10 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
}
}
suspend fun refreshLogin() {
val username = getUsername()
val password = getPassword()
logout()
try {
val csrf = api.login(username, password)
saveCSRF(csrf)
saveCredentials(username, password)
} catch (e: Exception) {
Timber.e(e)
logout()
throw e
}
}
// Attempt to login again if cookies have been cleared but credentials are still filled
suspend fun ensureLoggedIn() {
if (isAuthorized) return
if (!isLogged) throw Exception("MAL Login Credentials not found")
refreshLogin()
}
override fun logout() {

View File

@ -114,32 +114,6 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
}
}
suspend fun login(username: String, password: String): String {
return withContext(Dispatchers.IO) {
val csrf = getSessionInfo()
login(username, password, csrf)
csrf
}
}
private suspend fun getSessionInfo(): String {
val response = client.newCall(GET(loginUrl())).execute()
return Jsoup.parse(response.consumeBody()).select("meta[name=csrf_token]").attr("content")
}
private suspend fun login(username: String, password: String, csrf: String) {
withContext(Dispatchers.IO) {
val response =
client.newCall(POST(loginUrl(), body = loginPostBody(username, password, csrf)))
.execute()
response.use {
if (response.priorResponse?.code != 302) throw Exception("Authentication error")
}
}
}
private suspend fun getList(): List<TrackSearch> {
val results = getListXml(getListUrl()).select("manga")
@ -174,7 +148,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
companion object {
const val CSRF = "csrf_token"
private const val baseUrl = "https://myanimelist.net"
const val baseUrl = "https://myanimelist.net"
private const val baseMangaUrl = "$baseUrl/manga/"
private const val baseModifyListUrl = "$baseUrl/ownlist/manga"
private const val PREFIX_MY = "my:"
@ -182,7 +156,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
private fun mangaUrl(remoteId: Int) = baseMangaUrl + remoteId
private fun loginUrl() = baseUrl.toUri().buildUpon().appendPath("login.php").toString()
fun loginUrl() = baseUrl.toUri().buildUpon().appendPath("login.php").build()
private fun searchUrl(query: String): String {
val col = "c[]"

View File

@ -21,17 +21,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
myanimelist.ensureLoggedIn()
}
val request = chain.request()
var response = chain.proceed(updateRequest(request))
if (response.code == 400) {
runBlocking {
myanimelist.refreshLogin()
}
response.close()
response = chain.proceed(updateRequest(request))
}
return response
return chain.proceed(updateRequest(request))
}
private fun updateRequest(request: Request): Request {

View File

@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
import eu.kanade.tachiyomi.ui.setting.track.MyAnimeListLoginActivity
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.widget.preference.LoginPreference
import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog
@ -38,7 +39,13 @@ class SettingsTrackingController :
trackPreference(trackManager.myAnimeList) {
onClick {
showDialog(trackManager.myAnimeList)
if (trackManager.myAnimeList.isLogged) {
val dialog = TrackLogoutDialog(trackManager.myAnimeList)
dialog.targetController = this@SettingsTrackingController
dialog.showDialog(router)
} else {
startActivity(MyAnimeListLoginActivity.newIntent(context))
}
}
}
trackPreference(trackManager.aniList) {
@ -79,7 +86,7 @@ class SettingsTrackingController :
override fun onActivityResumed(activity: Activity) {
super.onActivityResumed(activity)
// Manually refresh anilist holder
updatePreference(trackManager.myAnimeList.id)
updatePreference(trackManager.aniList.id)
updatePreference(trackManager.shikimori.id)
updatePreference(trackManager.bangumi.id)

View File

@ -0,0 +1,77 @@
package eu.kanade.tachiyomi.ui.setting.track
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.webkit.WebView
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.webview.BaseWebViewActivity
import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import kotlinx.android.synthetic.main.webview_activity.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy
class MyAnimeListLoginActivity : BaseWebViewActivity() {
private val trackManager: TrackManager by injectLazy()
private val scope = CoroutineScope(Job() + Dispatchers.Main)
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
title = "MyAnimeList"
webview.webViewClient = object : WebViewClientCompat() {
override fun shouldOverrideUrlCompat(view: WebView, url: String): Boolean {
view.loadUrl(url)
return true
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// Get CSRF token from HTML after post-login redirect
if (url == MyAnimeListApi.baseUrl + "/") {
view?.evaluateJavascript(
"(function(){return document.querySelector('meta[name=csrf_token]').getAttribute('content')})();"
) {
scope.launch {
withContext(Dispatchers.IO) { trackManager.myAnimeList.login(it.replace("\"", "")) }
returnToSettings()
}
}
}
}
}
webview.loadUrl(MyAnimeListApi.loginUrl().toString())
}
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
private fun returnToSettings() {
finish()
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
}
companion object {
fun newIntent(context: Context): Intent {
val intent = Intent(context, MyAnimeListLoginActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
return intent
}
}
}

View File

@ -0,0 +1,255 @@
package eu.kanade.tachiyomi.ui.webview
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.graphics.ColorUtils
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.isInNightMode
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.setDefaultSettings
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.invisible
import eu.kanade.tachiyomi.util.view.marginBottom
import eu.kanade.tachiyomi.util.view.setStyle
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePadding
import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.webview_activity.*
open class BaseWebViewActivity : BaseActivity() {
private var bundle: Bundle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
delegate.localNightMode = when (preferences.theme()) {
1, 8 -> AppCompatDelegate.MODE_NIGHT_NO
2, 3, 4 -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
setContentView(R.layout.webview_activity)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar.setNavigationOnClickListener {
super.onBackPressed()
}
toolbar.navigationIcon?.setTint(getResourceColor(R.attr.actionBarTintColor))
val container: ViewGroup = findViewById(R.id.web_view_layout)
val content: LinearLayout = findViewById(R.id.web_linear_layout)
container.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
container.setOnApplyWindowInsetsListener { v, insets ->
val contextView = window?.decorView?.findViewById<View>(R.id.action_mode_bar)
contextView?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.systemWindowInsetLeft
rightMargin = insets.systemWindowInsetRight
}
// Consume any horizontal insets and pad all content in. There's not much we can do
// with horizontal insets
v.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
insets.replaceSystemWindowInsets(
0,
insets.systemWindowInsetTop,
0,
insets.systemWindowInsetBottom
)
}
swipe_refresh.setStyle()
swipe_refresh.setOnRefreshListener {
refreshPage()
}
window.statusBarColor = ColorUtils.setAlphaComponent(
getResourceColor(
R.attr
.colorSecondary
),
255
)
content.setOnApplyWindowInsetsListener { v, insets ->
// if pure white theme on a device that does not support dark status bar
/*if (getResourceColor(android.R.attr.statusBarColor) != Color.TRANSPARENT)
window.statusBarColor = Color.BLACK
else window.statusBarColor = getResourceColor(R.attr.colorPrimary)*/
window.navigationBarColor = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val colorPrimary = getResourceColor(R.attr.colorPrimaryVariant)
if (colorPrimary == Color.WHITE) Color.BLACK
else getResourceColor(android.R.attr.colorPrimary)
}
// if the android q+ device has gesture nav, transparent nav bar
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
(v.rootWindowInsets.systemWindowInsetBottom != v.rootWindowInsets.tappableElementInsets.bottom)
) {
getColor(android.R.color.transparent)
} else {
getResourceColor(android.R.attr.colorBackground)
}
v.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
0
)
if (Build.VERSION.SDK_INT >= 26 && !isInNightMode()) {
content.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
insets
}
swipe_refresh.isEnabled = false
if (bundle == null) {
webview.setDefaultSettings()
webview.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
progressBar.visible()
progressBar.progress = newProgress
if (newProgress == 100)
progressBar.invisible()
super.onProgressChanged(view, newProgress)
}
}
val marginB = webview.marginBottom
webview.setOnApplyWindowInsetsListener { v, insets ->
val bottomInset =
if (Build.VERSION.SDK_INT >= 29) insets.tappableElementInsets.bottom
else insets.systemWindowInsetBottom
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = marginB + bottomInset
}
insets
}
} else {
webview.restoreState(bundle)
}
}
private fun refreshPage() {
swipe_refresh.isRefreshing = true
webview.reload()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val lightMode = !isInNightMode()
window.statusBarColor = ColorUtils.setAlphaComponent(
getResourceColor(
R.attr
.colorSecondary
),
255
)
toolbar.setBackgroundColor(getResourceColor(R.attr.colorSecondary))
toolbar.popupTheme = if (lightMode) R.style.ThemeOverlay_MaterialComponents else R
.style.ThemeOverlay_MaterialComponents_Dark
val tintColor = getResourceColor(R.attr.actionBarTintColor)
toolbar.navigationIcon?.setTint(tintColor)
toolbar.overflowIcon?.mutate()
toolbar.setTitleTextColor(tintColor)
toolbar.overflowIcon?.setTint(tintColor)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
window.navigationBarColor = getResourceColor(R.attr.colorPrimaryVariant)
else if (window.navigationBarColor != getColor(android.R.color.transparent))
window.navigationBarColor = getResourceColor(android.R.attr.colorBackground)
web_linear_layout.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && lightMode) {
web_linear_layout.systemUiVisibility = web_linear_layout.systemUiVisibility.or(
View
.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
)
}
val typedValue = TypedValue()
theme.resolveAttribute(android.R.attr.windowLightStatusBar, typedValue, true)
if (typedValue.data == -1)
web_linear_layout.systemUiVisibility = web_linear_layout.systemUiVisibility
.or(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
else
web_linear_layout.systemUiVisibility = web_linear_layout.systemUiVisibility
.rem(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
invalidateOptionsMenu()
}
/**
* Called when the options menu of the toolbar is being created. It adds our custom menu.
*/
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.webview, menu)
return true
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
val backItem = toolbar.menu.findItem(R.id.action_web_back)
val forwardItem = toolbar.menu.findItem(R.id.action_web_forward)
backItem?.isEnabled = webview.canGoBack()
forwardItem?.isEnabled = webview.canGoForward()
val hasHistory = webview.canGoBack() || webview.canGoForward()
backItem?.isVisible = hasHistory
forwardItem?.isVisible = hasHistory
val tintColor = getResourceColor(R.attr.actionBarTintColor)
val translucentWhite = ColorUtils.setAlphaComponent(tintColor, 127)
backItem.icon?.setTint(if (webview.canGoBack()) tintColor else translucentWhite)
forwardItem?.icon?.setTint(if (webview.canGoForward()) tintColor else translucentWhite)
return super.onPrepareOptionsMenu(menu)
}
override fun onBackPressed() {
if (webview.canGoBack()) webview.goBack()
else super.onBackPressed()
}
/**
* Called when an item of the options menu was clicked. Used to handle clicks on our menu
* entries.
*/
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_web_back -> webview.goBack()
R.id.action_web_forward -> webview.goForward()
R.id.action_web_share -> shareWebpage()
R.id.action_web_browser -> openInBrowser()
}
return super.onOptionsItemSelected(item)
}
private fun shareWebpage() {
try {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, webview.url)
}
startActivity(Intent.createChooser(intent, getString(R.string.share)))
} catch (e: Exception) {
toast(e.message)
}
}
private fun openInBrowser() {
openInBrowser(webview.url)
}
}

View File

@ -35,7 +35,7 @@ import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.webview_activity.*
import uy.kohesive.injekt.injectLazy
class WebViewActivity : BaseActivity() {
open class WebViewActivity : BaseActivity() {
private val sourceManager by injectLazy<SourceManager>()
private var bundle: Bundle? = null

View File

@ -1,7 +1,20 @@
package eu.kanade.tachiyomi.util.system
import android.webkit.WebSettings
import android.webkit.WebView
fun WebView.setDefaultSettings() {
with(settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
setAppCacheEnabled(true)
useWideViewPort = true
loadWithOverviewMode = true
cacheMode = WebSettings.LOAD_DEFAULT
}
}
private val WEBVIEW_UA_VERSION_REGEX by lazy {
Regex(""".*Chrome/(\d+)\..*""")
}