Changed login method of Bangumi tracker
Created a login dialog to enter access token instead of browser redirect.
This commit is contained in:
parent
f9754f4f58
commit
0d105c04cf
5 changed files with 146 additions and 7 deletions
|
@ -95,6 +95,13 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||
onDismissRequest = { dialog = null },
|
||||
)
|
||||
}
|
||||
is BangumiLoginDialog ->{
|
||||
TrackingLoginDialogForBangumi(
|
||||
service = service,
|
||||
uNameStringRes = uNameStringRes,
|
||||
onDismissRequest = { dialog = null }
|
||||
)
|
||||
}
|
||||
is LogoutDialog -> {
|
||||
TrackingLogoutDialog(
|
||||
service = service,
|
||||
|
@ -160,7 +167,7 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||
Preference.PreferenceItem.TrackingPreference(
|
||||
title = trackManager.bangumi.name,
|
||||
service = trackManager.bangumi,
|
||||
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
|
||||
login = { dialog = BangumiLoginDialog(trackManager.bangumi, R.string.username); },
|
||||
logout = { dialog = LogoutDialog(trackManager.bangumi) },
|
||||
),
|
||||
Preference.PreferenceItem.InfoPreference(stringResource(R.string.tracking_info)),
|
||||
|
@ -281,6 +288,96 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog for Bangumi Tracking, user need to directly enter access token.
|
||||
*/
|
||||
@Composable
|
||||
private fun TrackingLoginDialogForBangumi(
|
||||
service: TrackService,
|
||||
@StringRes uNameStringRes: Int,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var accessToken by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var expiresIn by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var processing by remember { mutableStateOf(false) }
|
||||
var inputError by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = "Enter access token for Bangumi",
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
IconButton(onClick = onDismissRequest) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Close,
|
||||
contentDescription = stringResource(R.string.action_close),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = accessToken,
|
||||
onValueChange = { accessToken = it },
|
||||
label = { Text(text = "access token") },
|
||||
visualTransformation = VisualTransformation.None,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
singleLine = true,
|
||||
isError = inputError && !processing,
|
||||
)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = expiresIn,
|
||||
onValueChange = { expiresIn = it },
|
||||
label = { Text(text = "days to expiry") },
|
||||
visualTransformation = VisualTransformation.None,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Number,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
singleLine = true,
|
||||
isError = inputError && !processing,
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = !processing && accessToken.text.isNotBlank(),
|
||||
onClick = {
|
||||
scope.launchIO {
|
||||
processing = true
|
||||
val result = checkLogin(
|
||||
context = context,
|
||||
service = service,
|
||||
username = accessToken.text,
|
||||
password = expiresIn.text,
|
||||
)
|
||||
inputError = !result
|
||||
if (result) onDismissRequest()
|
||||
processing = false
|
||||
}
|
||||
},
|
||||
) {
|
||||
val id = if (processing) R.string.loading else R.string.login
|
||||
Text(text = stringResource(id))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
private suspend fun checkLogin(
|
||||
context: Context,
|
||||
service: TrackService,
|
||||
|
@ -346,6 +443,11 @@ private data class LoginDialog(
|
|||
@StringRes val uNameStringRes: Int,
|
||||
)
|
||||
|
||||
private data class BangumiLoginDialog(
|
||||
val service: TrackService,
|
||||
@StringRes val uNameStringRes: Int,
|
||||
)
|
||||
|
||||
private data class LogoutDialog(
|
||||
val service: TrackService,
|
||||
)
|
||||
|
|
|
@ -105,13 +105,21 @@ class Bangumi(id: Long) : TrackService(id, "Bangumi") {
|
|||
|
||||
override fun getCompletionStatus(): Int = COMPLETED
|
||||
|
||||
override suspend fun login(username: String, password: String) = login(password)
|
||||
override suspend fun login(username: String, password: String) = login(username, password.toLong())
|
||||
|
||||
suspend fun login(code: String) {
|
||||
try {
|
||||
val oauth = api.accessToken(code)
|
||||
suspend fun login(code:String, expiresIn:Long){
|
||||
try{
|
||||
val oauth = OAuth(
|
||||
access_token = code,
|
||||
token_type = "",
|
||||
expires_in = expiresIn * 86400000,
|
||||
refresh_token = null,
|
||||
user_id = id
|
||||
)
|
||||
interceptor.newAuth(oauth)
|
||||
val id = api.checkPersonalToken(code).toLong()
|
||||
saveCredentials(oauth.user_id.toString(), oauth.access_token)
|
||||
saveToken(oauth)
|
||||
} catch (e: Throwable) {
|
||||
logout()
|
||||
}
|
||||
|
|
|
@ -175,6 +175,34 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
|
|||
.build(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Check that access token user gave is valid
|
||||
*/
|
||||
suspend fun checkPersonalToken(code:String): Int {
|
||||
return withIOContext {
|
||||
val response = authClient.newCall(
|
||||
GET(
|
||||
"$apiUrl/v0/me",
|
||||
),
|
||||
).awaitSuccess()
|
||||
val responseBody = response.body.string()
|
||||
if (responseBody.isEmpty()) {
|
||||
throw Exception("Null Response")
|
||||
}
|
||||
if (response.code != 200) {
|
||||
throw Exception("Failed Response")
|
||||
} else {
|
||||
var id:Int = -1
|
||||
json.decodeFromString<User>(responseBody).let {
|
||||
id = it.id!!
|
||||
}
|
||||
if(id == -1) throw Exception("Failed Response")
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val clientId = "bgm10555cda0762e80ca"
|
||||
private const val clientSecret = "8fff394a8627b4c388cbf349ec865775"
|
||||
|
|
|
@ -32,12 +32,14 @@ class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
|
|||
val authRequest = if (originalRequest.method == "GET") {
|
||||
originalRequest.newBuilder()
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
.header("Authorization","Bearer ${oauth!!.access_token}")
|
||||
.url(
|
||||
originalRequest.url.newBuilder()
|
||||
.addQueryParameter("access_token", currAuth.access_token).build(),
|
||||
)
|
||||
.build()
|
||||
} else {
|
||||
|
||||
originalRequest.newBuilder()
|
||||
.post(addToken(currAuth.access_token, originalRequest.body as FormBody))
|
||||
.header("User-Agent", "Tachiyomi")
|
||||
|
|
|
@ -9,7 +9,6 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
|
|||
override fun handleResult(data: Uri?) {
|
||||
when (data?.host) {
|
||||
"anilist-auth" -> handleAnilist(data)
|
||||
"bangumi-auth" -> handleBangumi(data)
|
||||
"myanimelist-auth" -> handleMyAnimeList(data)
|
||||
"shikimori-auth" -> handleShikimori(data)
|
||||
}
|
||||
|
@ -33,7 +32,7 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
|
|||
val code = data.getQueryParameter("code")
|
||||
if (code != null) {
|
||||
lifecycleScope.launchIO {
|
||||
trackManager.bangumi.login(code)
|
||||
trackManager.bangumi.login(code,"0")
|
||||
returnToSettings()
|
||||
}
|
||||
} else {
|
||||
|
|
Reference in a new issue