Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import com.github.miwu.databinding.ActivityDeviceBinding as Binding

class DeviceActivity : ViewActivityX<Binding>(Binding::inflate), DeviceManagerCallback {
override val viewModel: DeviceViewModel by viewModel()
private val device by lazy { intent.getStringExtra("device")!!.to<MiotDevice>() }
private val user by lazy { intent.getStringExtra("user")!!.to<MiotUser>() }

// 如果确认接收的Extra是符合预期的这里可以直接Unwrap
private val device by lazy { intent.getStringExtra("device")!!.to<MiotDevice>().getOrThrow() }
private val user by lazy { intent.getStringExtra("user")!!.to<MiotUser>().getOrThrow() }
private val logger = Logger()
private val miotDeviceClient by lazy { MiotDeviceClient(user) }
private val specAttrProvider: MiotSpecAttrProvider by inject()
Expand Down Expand Up @@ -149,7 +151,7 @@ class DeviceActivity : ViewActivityX<Binding>(Binding::inflate), DeviceManagerCa
return if (!file.isFile) {
return null
} else {
file.readText().to<SpecAtt>()
file.readText().to<SpecAtt>().getOrThrow()
}
} catch (e: Exception) {
return null
Expand All @@ -167,7 +169,7 @@ class DeviceActivity : ViewActivityX<Binding>(Binding::inflate), DeviceManagerCa
return if (!file.isFile) {
return null
} else {
file.readText().to<Map<String, String>>()
file.readText().to<Map<String, String>>().getOrThrow()
}
} catch (e: Exception) {
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@ class MiotLoginProviderImpl : MiotLoginProvider {
readTimeout(5, TimeUnit.MINUTES)
}

override suspend fun login(
user: String,
pwd: String
) = runCatching {
override suspend fun login(user: String, pwd: String): Result<MiotUser> = runCatching {
cookieJar.clear()
val sidDetails = getLocation()
val sidDetails = getLocation().getOrThrow()
val pwdHash = pwd.md5()
val body = FormBody {
add("qs", sidDetails.qs)
Expand All @@ -64,26 +61,29 @@ class MiotLoginProviderImpl : MiotLoginProvider {
add("_json", "true")
}
get<String>(SERVICE_LOGIN_AUTH_URL, body)
.getOrThrow()
.to<Login>()
.getOrThrow()
.execute()
.getOrThrow()
}

override suspend fun loginByQrCode(loginUrl: String) = runCatching {
override suspend fun loginByQrCode(loginUrl: String): Result<MiotUser> = runCatching {
cookieJar.clear()
get<String>(loginUrl)
.getOrThrow()
.removePrefix()
.to<Login>()
.getOrThrow()
.also {
val (location, securityToken) = getServiceData()
val (location, securityToken) = getServiceData().getOrThrow()
it.location = location
it.ssecurity = securityToken
}
.execute()
.getOrThrow()
}


override suspend fun loginByQrCode(
loginUrl: String,
onSuccess: suspend CoroutineScope.(MiotUser) -> Unit,
Expand All @@ -94,10 +94,12 @@ class MiotLoginProviderImpl : MiotLoginProvider {
cookieJar.clear()
try {
get<String>(loginUrl)
.getOrThrow()
.removePrefix()
.to<Login>()
.getOrThrow()
.also {
val (location, securityToken) = getServiceData()
val (location, securityToken) = getServiceData().getOrThrow()
it.location = location
it.ssecurity = securityToken
}
Expand All @@ -115,7 +117,8 @@ class MiotLoginProviderImpl : MiotLoginProvider {
}
}

override suspend fun generateLoginQrCode(): Result<LoginQrCode> = withContext(Dispatchers.IO) {
// 为啥这里要在IO上下文执行?
override suspend fun generateLoginQrCode(): Result<LoginQrCode> = runCatching {
val generateQrCode = """
${QRCODE_GENERATE_URL}?
${
Expand All @@ -130,19 +133,18 @@ class MiotLoginProviderImpl : MiotLoginProvider {
""".trimIndent().urlEncode()
}
""".trimIndent()
runCatching {
get<String>(generateQrCode)
.removePrefix()
.to<LoginQrCode>()
}
get<String>(generateQrCode)
.getOrThrow()
.removePrefix()
.to<LoginQrCode>()
.getOrThrow()
}

override suspend fun refreshServiceToken(miotUser: MiotUser) = runCatching {
override suspend fun refreshServiceToken(miotUser: MiotUser): Result<MiotUser> = runCatching {
cookieJar.clear()
val url = HttpUrl.Builder()
.host("https://account.xiaomi.com")
.build()
with(miotUser) {
// 因为SimpleCookieJar的Store和Url无关,如果是手动添加Cookie是否不需要添加Url了呢
// 所以添加一个手动实现addAll函数表示是手动添加Cookie
val cookies = with(miotUser) {
listOf(
Cookie("userId", userId),
Cookie("cUserId", cUserId),
Expand All @@ -151,14 +153,14 @@ class MiotLoginProviderImpl : MiotLoginProvider {
Cookie("psecurity", passToken),
Cookie("passToken", passToken),
)
}.let { cookieJar.saveFromResponse(url, it) }
val data = getLocation()
data.toException()?.let { throw it }
}
cookieJar.addAll(cookies)
val data = getLocation().getOrThrow()
val location = data.location
val serviceToken = getServiceToken(location).getOrThrow()
miotUser.copy(
ssecurity = data.ssecurity,
serviceToken = serviceToken
serviceToken = serviceToken,
)
}

Expand All @@ -183,15 +185,13 @@ class MiotLoginProviderImpl : MiotLoginProvider {
)
}

private suspend fun getServiceToken(location: String) = runCatching {
val response = try {
get<Response>(location)
} catch (e: TimeoutException) {
throw MiotTimeoutException("Login", e)
} catch (e: IOException) {
throw MiotConnectionException("Login", e)
} catch (e: Exception) {
throw MiotHttpException("Login", e)
private suspend fun getServiceToken(location: String): Result<String> = runCatching {
val response = get<Response>(location).getOrElse { e ->
when (e) {
is TimeoutException -> throw MiotTimeoutException("Login", e)
is IOException -> throw MiotConnectionException("Login", e)
else -> throw MiotHttpException("Login", e)
}
}
val cookiesHeader = response.headers["Set-Cookie"]!!
cookiesHeader.split(", ").firstNotNullOfOrNull { cookieString ->
Expand All @@ -204,19 +204,26 @@ class MiotLoginProviderImpl : MiotLoginProvider {
} ?: throw MiotAuthException.tokenMissing()
}

private suspend fun getLocation() =
private suspend fun getLocation(): Result<Location> = runCatching {
// 如果Location的code不是预期的是否可以在这里就把结果包装成异常
get<String>(SERVICE_LOGIN_URL)
.getOrThrow()
.removePrefix()
.to<Location>()
.getOrThrow()
.getOrThrowAuthException()
}

private suspend fun getServiceData() =
private suspend fun getServiceData(): Result<ServiceData> = runCatching {
get<String>(SERVICE_LOGIN_URL)
.getOrThrow()
.removePrefix()
.to<ServiceData>()
.getOrThrow()
}

private suspend inline fun <reified T> get(
url: String, body: RequestBody? = null
): T = miotLoginClient.get<T>(url, body)
private suspend inline fun <reified T> get(url: String, body: RequestBody? = null): Result<T> =
miotLoginClient.get<T>(url, body)

class SimpleCookieJar : CookieJar {
private val storage = arrayListOf<Cookie>()
Expand All @@ -225,7 +232,9 @@ class MiotLoginProviderImpl : MiotLoginProvider {
storage.addAll(cookies)
}

override fun loadForRequest(url: HttpUrl) = storage
override fun loadForRequest(url: HttpUrl): List<Cookie> = storage

fun addAll(cookies: List<Cookie>) = storage.addAll(cookies)

fun clear() = storage.clear()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class MiotSpecAttrProviderImpl : MiotSpecAttrProvider {
override suspend fun getIconUrl(model: String) = withContext(Dispatchers.IO) {
runCatching {
val url = "https://home.mi.com/cgi-op/api/v1/baike/v2/product?model=${model}"
val info = client.get<Info>(url)
val info = client.get<Info>(url).getOrThrow()
if (info.code != 0) throw MiotClientException.getIconUrlFailed(model)
info.data.realIcon
}
Expand Down
4 changes: 3 additions & 1 deletion miot-api-impl/src/main/java/miwu/miot/utils/Crypto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import okio.ByteString.Companion.encodeUtf8
import java.net.URLEncoder
import kotlin.io.encoding.Base64

inline fun <reified T> String.to(): T = json.decodeFromString<T>(this)
inline fun <reified T> String.to(): Result<T> = runCatching {
json.decodeFromString<T>(this)
}

fun String.md5() = this.encodeUtf8()
.md5()
Expand Down
45 changes: 20 additions & 25 deletions miot-api-impl/src/main/java/miwu/miot/utils/OkHttp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import okio.Buffer
import java.nio.charset.Charset


fun OkHttpClient.Builder.userAgent(ua: String) = addInterceptor { chain ->
fun OkHttpClient.Builder.userAgent(ua: String): OkHttpClient.Builder = addInterceptor { chain ->
chain.proceed(
chain.request()
.newBuilder()
Expand All @@ -21,37 +21,29 @@ fun OkHttpClient.Builder.userAgent(ua: String) = addInterceptor { chain ->
)
}

fun OkHttpClient(block: OkHttpClient.Builder.() -> Unit = {}) =
fun OkHttpClient(block: OkHttpClient.Builder.() -> Unit = {}): OkHttpClient =
OkHttpClient.Builder().apply(block).build()

internal suspend inline fun <reified T> OkHttpClient.get(
url: String,
body: RequestBody? = null
): T = withContext(Dispatchers.IO) {
run {
newCall(
Request.Builder().url(url).apply {
if (body != null) post(body)
}.build()
).execute().use { response ->
when (T::class) {
String::class -> response.body.string() as T
body: RequestBody? = null,
): Result<T> = withContext(Dispatchers.IO) {
runCatching {
val request = Request.Builder()
.url(url)
.apply { if (body != null) post(body) }
.build()
newCall(request).execute().use { response ->
return@runCatching when (T::class) {
Response::class -> response as T
else ->
try {
response.body.string().to<T>()
} catch (e: Exception) {
throw IllegalArgumentException(
"Unsupported type: ${T::class.simpleName}",
e
)
}
String::class -> response.body.string() as T
else -> response.body.string().to<T>().getOrThrow()
}
}
}
}

fun FormBody(block: FormBody.Builder.() -> Unit) = FormBody.Builder().apply(block).build()
fun FormBody(block: FormBody.Builder.() -> Unit): FormBody = FormBody.Builder().apply(block).build()

fun RequestBody.readToString(): String {
Buffer().apply {
Expand All @@ -68,6 +60,9 @@ fun RequestBody.readToString(): String {
throw RuntimeException("data of requestBody is empty")
}


fun Request.Builder.addHeaders(vararg headers: Pair<String, String>) =
this.also { builder -> headers.forEach { addHeader(it.first, it.second) } }
fun Request.Builder.addHeaders(vararg headers: Pair<String, String>): Request.Builder {
for ((k, v) in headers) {
addHeader(k, v)
}
return this
}
Loading
Loading