mirror of
https://github.com/PaiGramTeam/GetToken.git
synced 2024-11-21 22:48:07 +00:00
copy tokens directly
This commit is contained in:
parent
95dcb5e919
commit
bf8073a331
@ -2,8 +2,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:label="GetToken"
|
android:label="GetToken"
|
||||||
|
@ -9,10 +9,7 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.compose.animation.AnimatedContent
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@ -27,7 +24,6 @@ import androidx.compose.ui.graphics.ColorFilter
|
|||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
@ -35,13 +31,7 @@ import androidx.constraintlayout.compose.ConstraintLayout
|
|||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import hat.holo.token.models.AccountInfo
|
import hat.holo.token.models.AccountInfo
|
||||||
import hat.holo.token.models.BaseResponse
|
|
||||||
import hat.holo.token.models.FetchRsp
|
|
||||||
import hat.holo.token.utils.*
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
val textColor = Color(0xFF424242)
|
val textColor = Color(0xFF424242)
|
||||||
|
|
||||||
@ -79,55 +69,6 @@ private fun TokenActivity.showDialog(msg: String) = runOnUiThread {
|
|||||||
}.create().show()
|
}.create().show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun TokenActivity.genAuthCode(
|
|
||||||
acc: AccountInfo,
|
|
||||||
useSToken: Boolean
|
|
||||||
): String? = runCatching {
|
|
||||||
val params = mutableMapOf<String, Any?>("app_id" to "4", "device" to "0")
|
|
||||||
val fetchResult = buildHttpRequest {
|
|
||||||
url("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch")
|
|
||||||
params.post(this)
|
|
||||||
}.getAsJson<BaseResponse>()
|
|
||||||
if (fetchResult.retcode != 0) {
|
|
||||||
showDialog("请求失败: ${fetchResult.message}")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val ticket = fetchResult.data<FetchRsp>().getUri().getQueryParameter("ticket")
|
|
||||||
val scanResult = buildHttpRequest {
|
|
||||||
url("https://api-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/scan")
|
|
||||||
params.apply {
|
|
||||||
put("ticket", ticket)
|
|
||||||
}.post(this)
|
|
||||||
}.getAsJson<BaseResponse>()
|
|
||||||
if (scanResult.retcode != 0) {
|
|
||||||
showDialog("请求失败: ${scanResult.message}")
|
|
||||||
return@runCatching null
|
|
||||||
}
|
|
||||||
val confirmResult = buildHttpRequest {
|
|
||||||
url("https://api-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/confirm")
|
|
||||||
params.apply {
|
|
||||||
put("payload", buildMap {
|
|
||||||
put("proto", "Account")
|
|
||||||
put("raw", buildMap {
|
|
||||||
put("uid", acc.uid)
|
|
||||||
put("ltoken", acc.lToken)
|
|
||||||
if (useSToken) {
|
|
||||||
put("mid", acc.mid)
|
|
||||||
put("stoken", acc.sToken)
|
|
||||||
}
|
|
||||||
}.toJson())
|
|
||||||
})
|
|
||||||
}.post(this)
|
|
||||||
}.getAsJson<BaseResponse>()
|
|
||||||
if (confirmResult.retcode != 0) {
|
|
||||||
showDialog("请求失败: ${confirmResult.message}")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return ticket
|
|
||||||
}.onFailure {
|
|
||||||
showDialog("网络异常,请稍后重试")
|
|
||||||
}.getOrNull()
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
@ -136,9 +77,8 @@ private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(15.dp)
|
modifier = Modifier.padding(15.dp)
|
||||||
) {
|
) {
|
||||||
var isLoading by remember { mutableStateOf(false) }
|
|
||||||
var authTicket by remember { mutableStateOf("") }
|
|
||||||
var grantSToken by remember { mutableStateOf(false) }
|
var grantSToken by remember { mutableStateOf(false) }
|
||||||
|
var showDoneIcon by remember { mutableStateOf(false) }
|
||||||
CustomCheckBox(
|
CustomCheckBox(
|
||||||
checked = true,
|
checked = true,
|
||||||
onCheckedChange = {},
|
onCheckedChange = {},
|
||||||
@ -161,91 +101,22 @@ private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
|||||||
} // TODO: More description
|
} // TODO: More description
|
||||||
)
|
)
|
||||||
Divider()
|
Divider()
|
||||||
AnimatedVisibility(
|
|
||||||
visible = authTicket.isNotEmpty(),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
AuthTicketView(authTicket)
|
|
||||||
}
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.End
|
horizontalArrangement = Arrangement.End
|
||||||
) {
|
) {
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val confirmResult = buildHttpRequest {
|
|
||||||
url("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query")
|
|
||||||
buildMap {
|
|
||||||
put("app_id", "4")
|
|
||||||
put("device", "0")
|
|
||||||
put("ticket", authTicket.removePrefix("ma_"))
|
|
||||||
}.post(this)
|
|
||||||
}.getAsJson<BaseResponse>()
|
|
||||||
if (confirmResult.retcode != 0) {
|
|
||||||
showDialog("请求失败: ${confirmResult.message}")
|
|
||||||
return@launch
|
|
||||||
} else {
|
|
||||||
showDialog(confirmResult.data?.toJson() ?: "success, but data is null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = "query")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
authTicket = ""
|
|
||||||
isLoading = true
|
|
||||||
authTicket = genAuthCode(accountInfo, grantSToken)?.let { s -> "ma_${s}" } ?: ""
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enabled = !isLoading
|
|
||||||
) {
|
|
||||||
AnimatedVisibility(visible = isLoading) {
|
|
||||||
CircularProgressIndicator(
|
|
||||||
modifier = Modifier.size(24.dp),
|
|
||||||
strokeWidth = 3.dp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
AnimatedVisibility(visible = !isLoading) {
|
|
||||||
// 120s available
|
|
||||||
Text("生成登录代码")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
|
||||||
@Composable
|
|
||||||
private fun TokenActivity.AuthTicketView(authTicket: String) = ConstraintLayout(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
val (divider, code, copyBtn) = createRefs()
|
|
||||||
Text(
|
|
||||||
text = authTicket,
|
|
||||||
modifier = Modifier.constrainAs(code) {
|
|
||||||
top.linkTo(parent.top, 12.dp)
|
|
||||||
start.linkTo(parent.start, 12.dp)
|
|
||||||
},
|
|
||||||
fontFamily = FontFamily.Monospace
|
|
||||||
)
|
|
||||||
var showDoneIcon by remember { mutableStateOf(false) }
|
|
||||||
val iconColor by animateColorAsState(if (showDoneIcon) Color(0xFF4CAF50) else textColor)
|
|
||||||
IconButton(
|
|
||||||
modifier = Modifier.constrainAs(copyBtn) {
|
|
||||||
top.linkTo(parent.top)
|
|
||||||
end.linkTo(parent.end)
|
|
||||||
bottom.linkTo(parent.bottom)
|
|
||||||
},
|
|
||||||
onClick = {
|
onClick = {
|
||||||
runCatching {
|
runCatching {
|
||||||
val clip = ClipData.newPlainText(null, authTicket)
|
val authStr = buildMap {
|
||||||
|
put("uid", accountInfo.uid)
|
||||||
|
put("ltoken", accountInfo.lToken)
|
||||||
|
if (grantSToken) {
|
||||||
|
put("mid", accountInfo.mid)
|
||||||
|
put("stoken", accountInfo.sToken)
|
||||||
|
}
|
||||||
|
}.map { (k, v) -> "$k=$v" }.joinToString(";")
|
||||||
|
val clip = ClipData.newPlainText(null, authStr)
|
||||||
getSystemService<ClipboardManager>()!!.setPrimaryClip(clip)
|
getSystemService<ClipboardManager>()!!.setPrimaryClip(clip)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
showDialog("复制失败")
|
showDialog("复制失败")
|
||||||
@ -254,13 +125,16 @@ private fun TokenActivity.AuthTicketView(authTicket: String) = ConstraintLayout(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
AnimatedContent(targetState = if (showDoneIcon) Icons.Outlined.Done else Res.iconCopy) { targetIcon ->
|
AnimatedVisibility(visible = showDoneIcon) {
|
||||||
Image(
|
Image(
|
||||||
imageVector = targetIcon,
|
imageVector = Icons.Outlined.Done,
|
||||||
contentDescription = "Copy auth ticket",
|
contentDescription = null,
|
||||||
colorFilter = ColorFilter.tint(iconColor)
|
colorFilter = ColorFilter.tint(Color(0xFF4CAF50))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Text("复制登录信息")
|
||||||
|
}
|
||||||
|
}
|
||||||
LaunchedEffect(showDoneIcon) {
|
LaunchedEffect(showDoneIcon) {
|
||||||
if (showDoneIcon) {
|
if (showDoneIcon) {
|
||||||
delay(1500)
|
delay(1500)
|
||||||
@ -268,12 +142,6 @@ private fun TokenActivity.AuthTicketView(authTicket: String) = ConstraintLayout(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Divider(
|
|
||||||
modifier = Modifier.constrainAs(divider) {
|
|
||||||
top.linkTo(code.bottom, 12.dp)
|
|
||||||
bottom.linkTo(parent.bottom)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package hat.holo.token.models
|
|
||||||
|
|
||||||
import androidx.annotation.Keep
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import hat.holo.token.utils.convertTo
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
data class BaseResponse(
|
|
||||||
val retcode: Int = 0,
|
|
||||||
val message: String = "ok",
|
|
||||||
val data: JsonElement? = null
|
|
||||||
) {
|
|
||||||
inline fun <reified T : Any> data(): T {
|
|
||||||
return data!!.convertTo()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package hat.holo.token.models
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.annotation.Keep
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
data class FetchRsp(
|
|
||||||
val url: String
|
|
||||||
) {
|
|
||||||
fun getUri(): Uri = Uri.parse(url)
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package hat.holo.token.utils
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import hat.holo.token.BuildConfig
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import okhttp3.*
|
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import java.io.IOException
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
|
|
||||||
private val gson by lazy { Gson() }
|
|
||||||
|
|
||||||
fun buildHttpRequest(block: Request.Builder.() -> Unit) = Request.Builder().apply(block).build()
|
|
||||||
|
|
||||||
fun <K,V> Map<K,V>.toJson() = gson.toJson(this)!!
|
|
||||||
|
|
||||||
fun JsonElement.toJson() = gson.toJson(this)!!
|
|
||||||
|
|
||||||
fun <K,V> Map<K,V>.post(builder: Request.Builder) {
|
|
||||||
builder.post(toJson().toRequestBody("application/json".toMediaType()))
|
|
||||||
}
|
|
||||||
|
|
||||||
val defaultOkClient = OkHttpClient.Builder().apply {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
val i = HttpLoggingInterceptor {
|
|
||||||
Log.d("LSPosed-Bridge", it)
|
|
||||||
}
|
|
||||||
i.level = HttpLoggingInterceptor.Level.BODY
|
|
||||||
addInterceptor(i)
|
|
||||||
}
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
suspend inline fun <reified T : Any> Request.getAsJson(client: OkHttpClient = defaultOkClient): T {
|
|
||||||
return Gson().fromJson(this@getAsJson.getAsText(client), T::class.java)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun Request.getAsText(client: OkHttpClient = defaultOkClient) = withContext(Dispatchers.IO) {
|
|
||||||
client.newCall(this@getAsText).await().use { r->
|
|
||||||
checkNotNull(r.body).use { b ->
|
|
||||||
b.string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T: Any> JsonElement.convertTo(): T = Gson().fromJson(this, T::class.java)
|
|
||||||
|
|
||||||
suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation ->
|
|
||||||
enqueue(object : Callback {
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
continuation.resume(response)
|
|
||||||
}
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
|
||||||
if (continuation.isCancelled) return
|
|
||||||
continuation.resumeWithException(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
continuation.invokeOnCancellation {
|
|
||||||
try {
|
|
||||||
cancel()
|
|
||||||
} catch (_: Throwable) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,6 @@
|
|||||||
package hat.holo.token.utils
|
package hat.holo.token.utils
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.compose.material.icons.materialIcon
|
|
||||||
import androidx.compose.material.icons.materialPath
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
@ -16,44 +13,4 @@ object Res {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val iconCopy: ImageVector
|
|
||||||
get() {
|
|
||||||
if (_contentCopy != null) {
|
|
||||||
return _contentCopy!!
|
|
||||||
}
|
|
||||||
_contentCopy = materialIcon(name = "Outlined.ContentCopy") {
|
|
||||||
materialPath {
|
|
||||||
moveTo(16.0f, 1.0f)
|
|
||||||
lineTo(4.0f, 1.0f)
|
|
||||||
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
|
|
||||||
verticalLineToRelative(14.0f)
|
|
||||||
horizontalLineToRelative(2.0f)
|
|
||||||
lineTo(4.0f, 3.0f)
|
|
||||||
horizontalLineToRelative(12.0f)
|
|
||||||
lineTo(16.0f, 1.0f)
|
|
||||||
close()
|
|
||||||
moveTo(19.0f, 5.0f)
|
|
||||||
lineTo(8.0f, 5.0f)
|
|
||||||
curveToRelative(-1.1f, 0.0f, -2.0f, 0.9f, -2.0f, 2.0f)
|
|
||||||
verticalLineToRelative(14.0f)
|
|
||||||
curveToRelative(0.0f, 1.1f, 0.9f, 2.0f, 2.0f, 2.0f)
|
|
||||||
horizontalLineToRelative(11.0f)
|
|
||||||
curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f)
|
|
||||||
lineTo(21.0f, 7.0f)
|
|
||||||
curveToRelative(0.0f, -1.1f, -0.9f, -2.0f, -2.0f, -2.0f)
|
|
||||||
close()
|
|
||||||
moveTo(19.0f, 21.0f)
|
|
||||||
lineTo(8.0f, 21.0f)
|
|
||||||
lineTo(8.0f, 7.0f)
|
|
||||||
horizontalLineToRelative(11.0f)
|
|
||||||
verticalLineToRelative(14.0f)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _contentCopy!!
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName")
|
|
||||||
private var _contentCopy: ImageVector? = null
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user