Skip to content

Commit

Permalink
feat: add new kuaidi100 data provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Robotxm committed Jun 30, 2023
1 parent 981c0a3 commit 370973b
Show file tree
Hide file tree
Showing 18 changed files with 376 additions and 161 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ BetterMiuiExpress is an Xposed module that prevents express details to be displa
The following instructions are based on LSPosed.

- Make sure that your device has installed LSPosed or other Xposed-compatible frameworks **with XSharedPreferences support**
- Download this module from CoolApk or compile by yourself
- Download this module from CoolApk or compile by yourself. **If you are updating from 1.4.6 and below to newer, it is recommended to uninstall the old version first**
- Enable this module and check MIUI Personal Assistant as the scope. You may unchecked the System Apps option in the menu to find it
- If you want to retrieve express details from KuaiDi100, you need KuaiDi100 customer and secret key
- The new KuaiDi100 provider does not require customer and key anymore. However, if you want to retrieve express details from legacy **KuaiDi100 (without 'New' tag)**, you still need KuaiDi100 customer and key
- Kill the MIUI Personal Assistant apps manually in LSPosed or Settings
- Swipe to MIUI Personal Assistant in the launcher. Now clicking the express items will not lead to jumping to third-party apps, and the latest express details will be displayed in the app widget

## Supported data provider

- KuaiDi100 (New)
- KuaiDi100: **Customer and key required**
- Cainiao

## For express delivered to CaiNiao Post House

You may need a code (取件码) to get your express from CaiNiao post house. Click the menu in express details activity and you will find an option to jump to third-apps manually.
Expand Down
10 changes: 8 additions & 2 deletions README.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ BetterMiuiExpress 用于禁止 MIUI 负一屏快递跳转到淘宝、菜鸟等
以下用法基于 LSPosed。

- 确保已经安装 LSPosed 或其他**支持 XSharedPreferences** 的 Xposed 框架
- 下载或自行编译模块
- 下载或自行编译模块**如果从 1.4.6 或更低的版本升级,建议卸载旧版本后再安装**
- 在 LSPosed 管理器中启用模块,并在作用域中勾选“智能助理”。可能需要在右上角的菜单中取消勾选“系统应用”
- 如需使用快递 100 数据源,需要申请 API 得到 customer 和 key
- 新版快递 100 数据源不再需要 customer 和 key。如需使用旧版快递 100 数据源,仍然需要申请 API 得到 customer 和 key
- 通过 LSPosed 管理器或系统设置强制停止“智能助理”运行
- 在桌面上滑到负一屏,现在点击快递卡片不会再跳转到第三方应用,同时可以在卡片中看到最新的快递动态

## 支持的数据源

- 新版快递 100
- 快递 100: **需要 customer 和 key**
- 菜鸟

## 需要取件码的快递

如果有需要取件码的快递,点击快递详情页面右上角的菜单,选择“转到第三方应用”即可。
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.22'
id 'kotlin-parcelize'
id 'com.google.devtools.ksp' version '1.7.20-1.0.8'
id 'com.google.devtools.ksp' version '1.8.22-1.0.11'
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import com.drake.brv.utils.models
import com.drake.brv.utils.setup
import com.github.vipulasri.timelineview.TimelineView
import com.moefactory.bettermiuiexpress.R
import com.moefactory.bettermiuiexpress.base.app.DATA_PROVIDER_NEW_KUAIDI100
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_CUSTOMER
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_DATA_SOURCE
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_DATA_PROVIDER
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_SECRET_KEY
import com.moefactory.bettermiuiexpress.base.app.PREF_NAME
import com.moefactory.bettermiuiexpress.base.ui.BaseActivity
Expand Down Expand Up @@ -82,15 +83,8 @@ class ExpressDetailsActivity : BaseActivity<ActivityExpressDetailsBinding>(false
get() = pref?.getString(PREF_KEY_SECRET_KEY, null)
private val customer: String?
get() = pref?.getString(PREF_KEY_CUSTOMER, null)
private val shouldFetchFromCaiNiao: Boolean
get() {
val useKuaidi100 = pref?.getBoolean(PREF_KEY_DATA_SOURCE, false) ?: false
return if (useKuaidi100) {
secretKey.isNullOrBlank() || customer.isNullOrBlank()
} else {
true
}
}
private val dataProvider: Int
get() = pref?.getInt(PREF_KEY_DATA_PROVIDER, DATA_PROVIDER_NEW_KUAIDI100) ?: DATA_PROVIDER_NEW_KUAIDI100

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -142,7 +136,7 @@ class ExpressDetailsActivity : BaseActivity<ActivityExpressDetailsBinding>(false
miuiExpress!!.mailNumber,
it.companyCode,
miuiExpress!!.phoneNumber,
shouldFetchFromCaiNiao,
dataProvider,
secretKey!!,
customer!!
)
Expand All @@ -154,7 +148,7 @@ class ExpressDetailsActivity : BaseActivity<ActivityExpressDetailsBinding>(false
viewBinding.stateLayout.showEmpty()
return@observe
}
viewBinding.tvSource.text = getString(R.string.data_source, response.dataSource)
viewBinding.tvSource.text = getString(R.string.data_provider_tips, response.dataSource)
viewBinding.tvStatus.text = response.status
viewBinding.rvTimeline.models = response.traces
viewBinding.stateLayout.showContent()
Expand All @@ -172,7 +166,7 @@ class ExpressDetailsActivity : BaseActivity<ActivityExpressDetailsBinding>(false
miuiExpress!!.mailNumber,
miuiExpress!!.companyCode,
miuiExpress!!.phoneNumber,
shouldFetchFromCaiNiao,
dataProvider,
secretKey, customer
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.AdapterView.INVALID_POSITION
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.Toast
import androidx.core.content.edit
import androidx.core.view.isVisible
import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.YukiHookAPI.Status.Executor
import com.moefactory.bettermiuiexpress.R
import com.moefactory.bettermiuiexpress.base.app.DATA_PROVIDER_LEGACY_KUAIDI100
import com.moefactory.bettermiuiexpress.base.app.DATA_PROVIDER_NEW_KUAIDI100
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_CUSTOMER
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_DATA_SOURCE
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_DATA_PROVIDER
import com.moefactory.bettermiuiexpress.base.app.PREF_KEY_SECRET_KEY
import com.moefactory.bettermiuiexpress.base.app.PREF_NAME
import com.moefactory.bettermiuiexpress.base.ui.BaseActivity
Expand All @@ -30,17 +35,26 @@ class MainActivity : BaseActivity<ActivityMainBinding>(false) {
setSupportActionBar(viewBinding.mtToolbar)

viewBinding.btnSave.setOnClickListener {
val useKuaiDi100 = viewBinding.swDataSource.isChecked && !viewBinding.tietCustomer.text.isNullOrEmpty() && !viewBinding.tietKey.text.isNullOrEmpty()
pref.edit {
putString(PREF_KEY_SECRET_KEY, viewBinding.tietKey.text?.toString() ?: "")
putString(PREF_KEY_CUSTOMER, viewBinding.tietCustomer.text?.toString() ?: "")
putBoolean(PREF_KEY_DATA_SOURCE, useKuaiDi100)
val dataProvider =
viewBinding.spDataProvider.selectedItemPosition.takeIf { it > INVALID_POSITION }
?: 0
if (dataProvider == DATA_PROVIDER_LEGACY_KUAIDI100) {
if (viewBinding.tietCustomer.text.isNullOrEmpty() || viewBinding.tietKey.text.isNullOrEmpty()) {
Toast.makeText(this, R.string.kuaidi100_fields_required, Toast.LENGTH_SHORT)
.show()
return@setOnClickListener
}
}
if (useKuaiDi100) {
Toast.makeText(this, R.string.save_successfully_kuaidi_100, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, R.string.save_successfully_cainiao, Toast.LENGTH_SHORT).show()

pref.edit {
if (dataProvider == DATA_PROVIDER_LEGACY_KUAIDI100) {
putString(PREF_KEY_SECRET_KEY, viewBinding.tietKey.text?.toString() ?: "")
putString(PREF_KEY_CUSTOMER, viewBinding.tietCustomer.text?.toString() ?: "")
}
putInt(PREF_KEY_DATA_PROVIDER, dataProvider)
}

Toast.makeText(this, R.string.save_successfully, Toast.LENGTH_SHORT).show()
}
viewBinding.btnGithub.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/Robotxm/BetterMiuiExpress")))
Expand All @@ -57,29 +71,37 @@ class MainActivity : BaseActivity<ActivityMainBinding>(false) {

viewBinding.tietCustomer.setText(pref.getString(PREF_KEY_CUSTOMER, "") ?: "")
viewBinding.tietKey.setText(pref.getString(PREF_KEY_SECRET_KEY, "") ?: "")
val isFilledKuaidi100Credentials = !viewBinding.tietCustomer.text.isNullOrEmpty() && !viewBinding.tietKey.text.isNullOrEmpty()
viewBinding.swDataSource.isChecked =
pref.getBoolean(
PREF_KEY_DATA_SOURCE,
isFilledKuaidi100Credentials
)
viewBinding.swDataSource.setText(if (viewBinding.swDataSource.isChecked) R.string.use_kuadi100 else R.string.use_cainiao )
viewBinding.groupKuaidi100.isVisible = viewBinding.swDataSource.isChecked

viewBinding.swDataSource.setOnCheckedChangeListener { view, isChecked ->
viewBinding.groupKuaidi100.isVisible = isChecked
view.setText(if (isChecked) R.string.use_kuadi100 else R.string.use_cainiao )
viewBinding.spDataProvider.setSelection(
pref.getInt(PREF_KEY_DATA_PROVIDER, DATA_PROVIDER_NEW_KUAIDI100)
)

viewBinding.spDataProvider.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parent: android.widget.AdapterView<*>?, view: android.view.View?, position: Int, id: Long
) {
viewBinding.groupKuaidi100.isVisible = position == DATA_PROVIDER_LEGACY_KUAIDI100
}

override fun onNothingSelected(parent: android.widget.AdapterView<*>?) {
viewBinding.groupKuaidi100.isVisible = viewBinding.spDataProvider.selectedItemPosition == DATA_PROVIDER_LEGACY_KUAIDI100
}
}

viewBinding.groupKuaidi100.isVisible = viewBinding.spDataProvider.selectedItemPosition == DATA_PROVIDER_LEGACY_KUAIDI100

viewBinding.tvYukiVersion.text =
getString(R.string.yuki_version, YukiHookAPI.API_VERSION_NAME, YukiHookAPI.API_VERSION_CODE)
getString(
R.string.yuki_version,
YukiHookAPI.API_VERSION_NAME,
YukiHookAPI.API_VERSION_CODE
)

if (YukiHookAPI.Status.isModuleActive) {
viewBinding.tvStatus.setText(R.string.active)
viewBinding.tvStatusDescription.text = getString(
R.string.active_hook_framework_version,
YukiHookAPI.Status.executorName,
YukiHookAPI.Status.executorVersion
Executor.name,
Executor.apiLevel
)
viewBinding.ivStatus.setImageResource(R.drawable.ic_active)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ package com.moefactory.bettermiuiexpress.api

import com.moefactory.bettermiuiexpress.model.BaseKuaiDi100Response
import com.moefactory.bettermiuiexpress.model.KuaiDi100RequestParam
import com.moefactory.bettermiuiexpress.model.NewKuaiDi100BaseResponse
import com.moefactory.bettermiuiexpress.model.NewKuaiDi100RequestParam
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.Query

interface KuaiDi100Api {

Expand All @@ -22,4 +27,21 @@ interface KuaiDi100Api {
@Field("key") secretKey: String,
@Field("num") mailNumber: String
): String

@POST("https://p.kuaidi100.com/mobile/mobileapi.do?method=query")
@Headers("User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; PCT-AL10 Build/LYZ28N)")
@FormUrlEncoded
suspend fun queryPackageNew(
@Field("method") method: String = "query",
@Field("json") paramString: String,
@Field("token") token: String = "",
@Field("hash") hash: String,
@Field("userid") userId: Int = 0
): NewKuaiDi100BaseResponse

@GET("https://www.kuaidi100.com/autonumber/auto")
@Headers("User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; PCT-AL10 Build/LYZ28N)")
suspend fun queryExpressCompanyNew(
@Query("num") mailNumber: String
): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.moefactory.bettermiuiexpress.base.app
const val PREF_NAME = "better_miui_express"
const val PREF_KEY_SECRET_KEY = "secretKey"
const val PREF_KEY_CUSTOMER = "customer"
const val PREF_KEY_DATA_SOURCE = "dataSource"
const val PREF_KEY_DATA_PROVIDER = "dataProvider"

const val BME_MAIN_ACTIVITY_ALIAS = "com.moefactory.bettermiuiexpress.activity.MainActivityAlias"

Expand Down Expand Up @@ -37,4 +37,8 @@ const val PA_EXPRESS_INFO_DETAIL_OLD =
const val PA_EXPRESS_INFO_DETAIL =
"com.miui.personalassistant.service.express.bean.ExpressInfo\$Detail"

const val CAI_NIAO_APP_KEY = "12574478"
const val CAI_NIAO_APP_KEY = "12574478"

const val DATA_PROVIDER_NEW_KUAIDI100 = 0
const val DATA_PROVIDER_LEGACY_KUAIDI100 = 1
const val DATA_PROVIDER_CAINIAO = 2
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,8 @@ class HookEntry : IYukiHookXposedInit {
get() = pref?.getString(PREF_KEY_SECRET_KEY, null)
private val customer: String?
get() = pref?.getString(PREF_KEY_CUSTOMER, null)
private val shouldFetchFromCaiNiao: Boolean
get() {
val useKuaidi100 = pref?.getBoolean(PREF_KEY_DATA_SOURCE, false) ?: false
return if (useKuaidi100) {
secretKey.isNullOrBlank() || customer.isNullOrBlank()
} else {
true
}
}
private val dataProvider: Int
get() = pref?.getInt(PREF_KEY_DATA_PROVIDER, DATA_PROVIDER_NEW_KUAIDI100) ?: DATA_PROVIDER_NEW_KUAIDI100

override fun onInit() = configs {
isDebug = BuildConfig.DEBUG
Expand Down Expand Up @@ -240,23 +233,34 @@ class HookEntry : IYukiHookXposedInit {
private suspend fun fetchExpressDetails(
mailNumber: String, originalCompanyCode: String, phoneNumber: String
): List<ExpressTrace>? {
return if (shouldFetchFromCaiNiao) {
ExpressActualRepository.queryExpressDetailsFromCaiNiaoActual(mailNumber)
?.fullTraceDetails?.map { it.toExpressTrace() }
} else {
// Get the company code
val convertedCompanyCode = ExpressCompanyUtils.convertCode(originalCompanyCode)
?: ExpressActualRepository.queryCompanyActual(
mailNumber,
secretKey!!
)[0].companyCode
when (dataProvider) {
DATA_PROVIDER_NEW_KUAIDI100 -> {
// Get the company code
val convertedCompanyCode = ExpressCompanyUtils.convertCode(originalCompanyCode)
?: ExpressActualRepository.queryCompanyActual(mailNumber, secretKey)[0].companyCode

// Get the details
val response = ExpressActualRepository.queryExpressDetailsFromKuaiDi100Actual(
convertedCompanyCode, mailNumber, phoneNumber, secretKey!!, customer!!
)
val response = ExpressActualRepository.queryExpressDetailsFromNewKuaiDi100Actual(convertedCompanyCode, mailNumber, phoneNumber)

return response.lastResult?.data?.map { it.toExpressTrace() }?.sortedDescending()
}
DATA_PROVIDER_LEGACY_KUAIDI100 -> {
val convertedCompanyCode = ExpressCompanyUtils.convertCode(originalCompanyCode)
?: ExpressActualRepository.queryCompanyActual(mailNumber, secretKey)[0].companyCode

val response = ExpressActualRepository.queryExpressDetailsFromKuaiDi100Actual(
convertedCompanyCode, mailNumber, phoneNumber, secretKey!!, customer!!
)

return response.data?.map { it.toExpressTrace() }?.sortedDescending()
}
DATA_PROVIDER_CAINIAO -> {
return ExpressActualRepository.queryExpressDetailsFromCaiNiaoActual(mailNumber)
?.fullTraceDetails
?.map { it.toExpressTrace() }
?.sortedDescending()
}
else -> return null
}

response.data?.map { it.toExpressTrace() }
}?.sortedDescending()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.moefactory.bettermiuiexpress.model
import com.moefactory.bettermiuiexpress.serializer.IntAsBooleanSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.util.UUID

@Serializable
data class BaseKuaiDi100Response(
Expand Down Expand Up @@ -41,6 +42,40 @@ data class KuaiDi100RequestParam(
val phone: String? = null
)

@Serializable
open class NewKuaiDi100BaseRequestParam(
val type: String = "detail",
@SerialName("appid") val appId: String = "com.Kingdee.Express",
val versionCode: Int = 693,
@SerialName("os_version") val osVersion: String = "android5.1.1",
@SerialName("os_name") val osName: String = "PCT-AL10",
@SerialName("t") val time: String = System.currentTimeMillis().toString(),
@SerialName("tra") val trackId: String = UUID.randomUUID().toString(),
@SerialName("uchannel") val userChannel: String = "null",
@SerialName("nt") val network: String = "wifi",
val deviceId: String = UUID.randomUUID().hashCode().toString(),
@SerialName("apiversion") val apiVersion: Int = 18,
)

@Serializable
data class NewKuaiDi100RequestParam(
val phone: String? = null,
@SerialName("num") val mailNumber: String,
@SerialName("com") val companyCode: String
) : NewKuaiDi100BaseRequestParam()

@Serializable
data class NewKuaiDi100BaseResponse(
val status: String? = null,
val lastResult: NewKuaiDi100LastResult? = null
)

@Serializable
data class NewKuaiDi100LastResult(
val state: String? = null,
val data: List<KuaiDi100ExpressDetails>? = null
)

@Serializable
data class KuaiDi100Company(
val lengthPre: Int,
Expand Down
Loading

0 comments on commit 370973b

Please sign in to comment.