mirror of
https://github.com/PaiGramTeam/GetToken.git
synced 2024-11-21 14:38:24 +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"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="GetToken"
|
||||
|
@ -9,10 +9,7 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.Keep
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
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.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@ -35,13 +31,7 @@ import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.core.content.getSystemService
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
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.launch
|
||||
|
||||
val textColor = Color(0xFF424242)
|
||||
|
||||
@ -79,55 +69,6 @@ private fun TokenActivity.showDialog(msg: String) = runOnUiThread {
|
||||
}.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
|
||||
private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
@ -136,9 +77,8 @@ private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
||||
Column(
|
||||
modifier = Modifier.padding(15.dp)
|
||||
) {
|
||||
var isLoading by remember { mutableStateOf(false) }
|
||||
var authTicket by remember { mutableStateOf("") }
|
||||
var grantSToken by remember { mutableStateOf(false) }
|
||||
var showDoneIcon by remember { mutableStateOf(false) }
|
||||
CustomCheckBox(
|
||||
checked = true,
|
||||
onCheckedChange = {},
|
||||
@ -161,106 +101,40 @@ private fun TokenActivity.Content(accountInfo: AccountInfo) = Column(
|
||||
} // TODO: More description
|
||||
)
|
||||
Divider()
|
||||
AnimatedVisibility(
|
||||
visible = authTicket.isNotEmpty(),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
AuthTicketView(authTicket)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
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
|
||||
runCatching {
|
||||
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)
|
||||
}.onFailure {
|
||||
showDialog("复制失败")
|
||||
}.onSuccess {
|
||||
showDoneIcon = true
|
||||
}
|
||||
},
|
||||
enabled = !isLoading
|
||||
}
|
||||
) {
|
||||
AnimatedVisibility(visible = isLoading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(24.dp),
|
||||
strokeWidth = 3.dp
|
||||
AnimatedVisibility(visible = showDoneIcon) {
|
||||
Image(
|
||||
imageVector = Icons.Outlined.Done,
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(Color(0xFF4CAF50))
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(visible = !isLoading) {
|
||||
// 120s available
|
||||
Text("生成登录代码")
|
||||
}
|
||||
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 = {
|
||||
runCatching {
|
||||
val clip = ClipData.newPlainText(null, authTicket)
|
||||
getSystemService<ClipboardManager>()!!.setPrimaryClip(clip)
|
||||
}.onFailure {
|
||||
showDialog("复制失败")
|
||||
}.onSuccess {
|
||||
showDoneIcon = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
AnimatedContent(targetState = if (showDoneIcon) Icons.Outlined.Done else Res.iconCopy) { targetIcon ->
|
||||
Image(
|
||||
imageVector = targetIcon,
|
||||
contentDescription = "Copy auth ticket",
|
||||
colorFilter = ColorFilter.tint(iconColor)
|
||||
)
|
||||
}
|
||||
LaunchedEffect(showDoneIcon) {
|
||||
if (showDoneIcon) {
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
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.*
|
||||
|
||||
@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