diff --git a/.gradle/8.13/executionHistory/executionHistory.bin b/.gradle/8.13/executionHistory/executionHistory.bin
index 3c05a85..1d05c8a 100644
Binary files a/.gradle/8.13/executionHistory/executionHistory.bin and b/.gradle/8.13/executionHistory/executionHistory.bin differ
diff --git a/.gradle/8.13/executionHistory/executionHistory.lock b/.gradle/8.13/executionHistory/executionHistory.lock
index 881a7e5..a7ed870 100644
Binary files a/.gradle/8.13/executionHistory/executionHistory.lock and b/.gradle/8.13/executionHistory/executionHistory.lock differ
diff --git a/.gradle/8.13/fileHashes/fileHashes.bin b/.gradle/8.13/fileHashes/fileHashes.bin
index 6ff3adc..6c98b6e 100644
Binary files a/.gradle/8.13/fileHashes/fileHashes.bin and b/.gradle/8.13/fileHashes/fileHashes.bin differ
diff --git a/.gradle/8.13/fileHashes/fileHashes.lock b/.gradle/8.13/fileHashes/fileHashes.lock
index 85e5057..b970a1d 100644
Binary files a/.gradle/8.13/fileHashes/fileHashes.lock and b/.gradle/8.13/fileHashes/fileHashes.lock differ
diff --git a/.gradle/8.13/fileHashes/resourceHashesCache.bin b/.gradle/8.13/fileHashes/resourceHashesCache.bin
index d6e9e8d..e951736 100644
Binary files a/.gradle/8.13/fileHashes/resourceHashesCache.bin and b/.gradle/8.13/fileHashes/resourceHashesCache.bin differ
diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock
index 11ad109..1a76699 100644
Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe
index 7e500f0..484309e 100644
Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
new file mode 100644
index 0000000..4d3d6d9
--- /dev/null
+++ b/.idea/caches/deviceStreaming.xml
@@ -0,0 +1,2289 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2a45ead..09ff73e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -53,6 +53,9 @@
+
()
var pos = -1
var isAuto = false
- var payType = "1"
+ var payType = "0"
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityOpenMemberBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -220,7 +222,10 @@ class OpenMemberActivity : CKLHBaseActionBarActivity() {
fun aliPay(orderStr : String){
GlobalScope.launch(Dispatchers.IO){
-
+ // [支付宝沙箱测试] 仅 Debug 包启用,Release 包继续使用正式环境。
+ if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
+ EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX)
+ }
val alipay = PayTask(this@OpenMemberActivity)
val result = alipay.payV2(orderStr, true)
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/order/PayOrderActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/order/PayOrderActivity.kt
index 116cdd8..25bd14e 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/order/PayOrderActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/order/PayOrderActivity.kt
@@ -1,6 +1,7 @@
package com.ck.ckcollar.app.ui.act.order
import android.content.Context
+import android.content.pm.ApplicationInfo
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
@@ -8,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.alipay.sdk.app.EnvUtils
import com.alipay.sdk.app.PayTask
import com.bumptech.glide.Glide
import com.ck.ckcollar.app.CKApp
@@ -55,7 +57,7 @@ class PayOrderActivity : CKBaseActivity() {
var memberModel: UserInfoModel? = null
- var payType = "1"
+ var payType = "0"
var selectBalance = false
var selectPoints = false
@@ -83,9 +85,6 @@ class PayOrderActivity : CKBaseActivity() {
binding.btnAli.setOnClickListener(payClick)
binding.btnWX.setOnClickListener(payClick)
- binding.btnBalance.setOnClickListener(payClick)
- binding.btnPoints.setOnClickListener(payClick)
-
binding.btnBalance.setOnClickListener {
@@ -102,6 +101,11 @@ class PayOrderActivity : CKBaseActivity() {
}
}
binding.btnPoints.setOnClickListener {
+ if ((memberModel?.points ?: 0.0) <= 0.0) {
+ toast(getString(R.string.points_over))
+ return@setOnClickListener
+ }
+
selectPoints = !selectPoints
if (selectPoints){
binding.image4.setImageResource(R.mipmap.image_address_check_select)
@@ -113,8 +117,7 @@ class PayOrderActivity : CKBaseActivity() {
binding.btnBuyNow.setOnClickListener {
-
- if (selectBalance){
+ if (selectBalance && !canPointsCoverPayAmount()){
getPayPassword()
}else{
order("")
@@ -171,7 +174,7 @@ class PayOrderActivity : CKBaseActivity() {
if (TextUtils.isEmpty(payType) && memberModel?.balance == 0.0){
- if (!selectPoints || "1".equals(confirmOrderModel?.isPoint)){
+ if (!selectPoints || "1".equals(confirmOrderModel?.isPoint.toString())){
toast(getString(R.string.balance_over))
return
}
@@ -207,10 +210,7 @@ class PayOrderActivity : CKBaseActivity() {
var showBalance = BigDecimal.valueOf(model.payAmount).setScale(2, RoundingMode.HALF_UP)
if (selectPoints){
- memberModel?.intoAmounts?.let { intoAmounts ->
- val pointsDeduction = BigDecimal.valueOf(memberModel!!.points)
- .multiply(BigDecimal(intoAmounts))
- .setScale(2, RoundingMode.HALF_UP)
+ calculatePointsDeduction()?.let { pointsDeduction ->
showBalance = showBalance.subtract(pointsDeduction)
.max(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP))
}
@@ -234,6 +234,11 @@ class PayOrderActivity : CKBaseActivity() {
var payType = payType
if(selectBalance){
+ if (TextUtils.isEmpty(payType) && memberModel?.balance == 0.0){
+ toast(getString(R.string.balance_over))
+ return
+ }
+
if (TextUtils.isEmpty(payType)){
payType = "2"
}else{
@@ -241,6 +246,11 @@ class PayOrderActivity : CKBaseActivity() {
}
}
if(selectPoints){
+ if (TextUtils.isEmpty(payType) && memberModel?.points == 0.0){
+ toast(getString(R.string.points_over))
+ return
+ }
+
if (TextUtils.isEmpty(payType)){
payType = "3"
}else{
@@ -304,9 +314,28 @@ class PayOrderActivity : CKBaseActivity() {
})
}
- fun aliPay(orderStr: String) {
- GlobalScope.launch(Dispatchers.IO) {
+ private fun canPointsCoverPayAmount(): Boolean {
+ if (!selectPoints) return false
+ val pointsDeduction = calculatePointsDeduction() ?: return false
+ val payAmount = BigDecimal.valueOf(model.payAmount).setScale(2, RoundingMode.HALF_UP)
+ return pointsDeduction >= payAmount
+ }
+ private fun calculatePointsDeduction(): BigDecimal? {
+ val member = memberModel ?: return null
+ val intoAmounts = member.intoAmounts?.toBigDecimalOrNull() ?: return null
+ return BigDecimal.valueOf(member.points)
+ .multiply(intoAmounts)
+ .setScale(2, RoundingMode.HALF_UP)
+ }
+
+ fun aliPay(orderStr: String) {
+
+ GlobalScope.launch(Dispatchers.IO) {
+ // [支付宝沙箱测试] 仅 Debug 包启用,Release 包继续使用正式环境。
+ if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
+ EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX)
+ }
val alipay = PayTask(this@PayOrderActivity)
val result = alipay.payV2(orderStr, true)
@@ -347,7 +376,7 @@ class PayOrderActivity : CKBaseActivity() {
}
override fun getResult(data: DataCollection, resultCode: Int) {
- if (resultCode == 10001){
+ if (resultCode == 10001){
showPayBottomView()
}
}
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/petmanager/AddPetActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/petmanager/AddPetActivity.kt
index d8a34e9..2f16226 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/petmanager/AddPetActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/petmanager/AddPetActivity.kt
@@ -342,8 +342,8 @@ class AddPetActivity : CKBaseActivity() {
data?.data?.let(::handleSelectedImage)
?: toast(getString(R.string.read_photo_failed))
}else if (requestCode == 555 && resultCode == RESULT_OK) {
- var path = PreferencesUtils.getString(this@AddPetActivity, Const.IMAGE_PATH,"")
- uploadImage(path!!)
+ val path = PreferencesUtils.getString(this@AddPetActivity, Const.IMAGE_PATH, "")
+ compressAndUploadImage(path.orEmpty())
}
binding.textPhotoNum.setText((this.data.size - 1 ).toString() + "/3")
}
@@ -389,6 +389,29 @@ class AddPetActivity : CKBaseActivity() {
)
}
+ private fun compressAndUploadImage(path: String) {
+ if (path.isBlank()) {
+ toast(getString(R.string.read_photo_failed))
+ return
+ }
+
+ LubanCompressUtil.getCompressFile(
+ this,
+ File(path),
+ object : LubanCompressUtil.CompressCallback {
+ override fun success(file: File) {
+ if (isFinishing || isDestroyed) return
+ uploadImage(file.absolutePath)
+ }
+
+ override fun fail(failMsg: String) {
+ if (isFinishing || isDestroyed) return
+ toast(failMsg)
+ }
+ }
+ )
+ }
+
private fun copySelectedImageToCache(uri: Uri): String? {
val extension = contentResolver.getType(uri)
?.let(MimeTypeMap.getSingleton()::getExtensionFromMimeType)
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingActivity.kt
index 1c424b3..b814526 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingActivity.kt
@@ -5,94 +5,66 @@ import android.os.Bundle
import android.view.View
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
+import com.ck.ckcollar.app.config.Config
import com.ck.ckcollar.app.databinding.ActivitySettingBinding
+import com.ck.ckcollar.app.ui.act.home.MainActivity
import com.ck.ckcollar.app.ui.act.login.AgreementActivity
import com.ck.ckcollar.app.ui.act.login.LoginActivity
-import com.ck.ckcollar.app.ui.act.home.MainActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
class SettingActivity : CKBaseActivity(), View.OnClickListener {
- lateinit var binding: ActivitySettingBinding
+ private lateinit var binding: ActivitySettingBinding
+
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySettingBinding.inflate(layoutInflater)
setContentView(binding.root)
super.onCreate(savedInstanceState)
-
- setStatusBarColor(resources.getColor(R.color.white))
- setActionBarColor(resources.getColor(R.color.white))
-
+ setStatusBarColor(getColor(R.color.white))
+ setActionBarColor(getColor(R.color.white))
setTitleText(getString(R.string.setting))
}
override fun initView() {
binding.btnLogout.visibility = if (CKApp.app.isUserLoggedIn) View.VISIBLE else View.GONE
-
- binding.contentLoginPassword.setOnClickListener(this)
- binding.contentPaymentPassword.setOnClickListener(this)
- binding.contentAboutUs.setOnClickListener(this)
- binding.contentUserAgreement.setOnClickListener(this)
- binding.contentPrivacyAgreement.setOnClickListener(this)
- binding.contentShippingAddress.setOnClickListener(this)
- binding.contentCancellation.setOnClickListener(this)
- binding.btnLogout.setOnClickListener(this)
+ listOf(binding.contentLoginPassword, binding.contentPaymentPassword, binding.contentAboutUs,
+ binding.contentUserAgreement, binding.contentPrivacyAgreement, binding.contentShippingAddress,
+ binding.contentCancellation, binding.btnLogout).forEach { it.setOnClickListener(this) }
}
- override fun initData() {
- }
+ override fun initData() = Unit
override fun onClick(v: View?) {
-
- when (v?.id){
- R.id.contentLoginPassword->{
- if (!requireLogin()) return
- toActivity(PasswordManagerActivity::class.java, DataCollection())
- }
- R.id.contentPaymentPassword->{
- if (!requireLogin()) return
- toActivity(PayPasswordManagerActivity::class.java, DataCollection())
-
- }
- R.id.contentAboutUs->{
- toActivity(AgreementActivity::class.java, DataCollection().apply { add(Data("title","关于我们")) })
- }
- R.id.contentUserAgreement->{
- toActivity(AgreementActivity::class.java, DataCollection().apply { add(Data("title","用户协议")) })
- }
- R.id.contentPrivacyAgreement->{
- toActivity(AgreementActivity::class.java, DataCollection().apply { add(Data("title","隐私协议")) })
- }
- R.id.contentShippingAddress->{
- if (!requireLogin()) return
- toActivity(AddressActivity::class.java, DataCollection().apply { add(Data("flag","1"))
- })
- }
- R.id.contentCancellation->{
- if (!requireLogin()) return
- toActivity(AccountCancellationActivity::class.java, DataCollection())
- }
- R.id.btnLogout->{
- CKAlertDialog(this@SettingActivity,getString(R.string.logout),getString(R.string.logout_alert),object : CKAlertDialog.AlertClickListener{
- override fun onBack(isConfirm: Boolean) {
- if (isConfirm){
- CKApp.app.clearLoginState()
- startActivity(
- Intent(this@SettingActivity, MainActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- putExtra(MainActivity.EXTRA_SHOW_HOME, true)
- }
- )
- finish()
- }
+ when (v?.id) {
+ R.id.contentLoginPassword -> if (requireLogin()) toActivity(PasswordManagerActivity::class.java, DataCollection())
+ R.id.contentPaymentPassword -> if (requireLogin()) toActivity(PayPasswordManagerActivity::class.java, DataCollection())
+ R.id.contentAboutUs -> toActivity(AgreementActivity::class.java, DataCollection().apply { add(Data("title", getString(R.string.about_us))) })
+ R.id.contentUserAgreement -> toActivity(AgreementActivity::class.java, DataCollection().apply { add(Data("title", getString(R.string.user_agreement))) })
+ R.id.contentPrivacyAgreement -> toActivity(SettingWebViewActivity::class.java, pageParams(Config.PRIVACY_POLICY, getString(R.string.privacy_agreement)))
+ R.id.contentShippingAddress -> if (requireLogin()) toActivity(AddressActivity::class.java, DataCollection().apply { add(Data("flag", "1")) })
+ R.id.contentCancellation -> if (requireLogin()) toActivity(AccountCancellationActivity::class.java, DataCollection())
+ R.id.btnLogout -> CKAlertDialog(this, getString(R.string.logout), getString(R.string.logout_alert), object : CKAlertDialog.AlertClickListener {
+ override fun onBack(isConfirm: Boolean) {
+ if (isConfirm) {
+ CKApp.app.clearLoginState()
+ startActivity(Intent(this@SettingActivity, MainActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ putExtra(MainActivity.EXTRA_SHOW_HOME, true)
+ })
+ finish()
}
- }).show()
-
- }
+ }
+ }).show()
}
}
+ private fun pageParams(url: String, title: String) = DataCollection().apply {
+ add(Data("url", url))
+ add(Data("title", title))
+ }
+
private fun requireLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
toActivity(LoginActivity::class.java, DataCollection())
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingWebViewActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingWebViewActivity.kt
new file mode 100644
index 0000000..e45e4a3
--- /dev/null
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/setting/SettingWebViewActivity.kt
@@ -0,0 +1,65 @@
+package com.ck.ckcollar.app.ui.act.setting
+
+import android.os.Bundle
+import android.webkit.WebSettings
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.net.Uri
+import com.ck.ckcollar.app.R
+import com.ck.ckcollar.app.databinding.ActivitySettingWebViewBinding
+import com.ck.ckcollar.app.ui.base.CKBaseActivity
+
+/** Displays a setting document supplied by the caller. */
+class SettingWebViewActivity : CKBaseActivity() {
+ private lateinit var binding: ActivitySettingWebViewBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ binding = ActivitySettingWebViewBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ super.onCreate(savedInstanceState)
+ setActionBarColor(getColor(R.color.white))
+ setStatusBarColor(getColor(R.color.white))
+ }
+
+ override fun initView() {
+ binding.webView.settings.apply {
+ javaScriptEnabled = true; domStorageEnabled = true
+ useWideViewPort = true; loadWithOverviewMode = true
+ setSupportZoom(true); builtInZoomControls = true; displayZoomControls = false
+ layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS
+ }
+ binding.webView.apply {
+ isFocusableInTouchMode = true
+ requestFocus()
+ webViewClient = object : WebViewClient() {
+ override fun onPageFinished(view: WebView?, url: String?) {
+ super.onPageFinished(view, url)
+ // Do this after the page's anchor/default restoration has run.
+ view?.post {
+ view.scrollTo(0, 0)
+ view.evaluateJavascript("window.scrollTo(0, 0);", null)
+ }
+ view?.postDelayed({ view.scrollTo(0, 0) }, 300)
+ }
+ }
+ }
+ }
+
+ override fun initData() {
+ val params = getReceiveParam()
+ setTitleText(params.get("title")?.stringValue?.toString().orEmpty())
+ val url = params.get("url")?.stringValue?.toString().orEmpty()
+ if (url.isNotEmpty()) {
+ // Fragment anchors (for example #a6 in the privacy URL) override the
+ // initial position, while this screen always starts at the document top.
+ val topUrl = runCatching { Uri.parse(url).buildUpon().fragment(null).build().toString() }
+ .getOrDefault(url)
+ binding.webView.loadUrl(topUrl)
+ }
+ }
+
+ override fun onDestroy() {
+ binding.webView.apply { stopLoading(); destroy() }
+ super.onDestroy()
+ }
+}
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/shop/OrderActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/shop/OrderActivity.kt
index cc12a50..6818710 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/shop/OrderActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/shop/OrderActivity.kt
@@ -65,7 +65,7 @@ class OrderActivity : CKBaseActivity() {
var memberModel: UserInfoModel? = null
- var payType = "1"
+ var payType = "0"
var selectBalance = false
var selectPoints = false
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/user/UserInfoActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/user/UserInfoActivity.kt
index b0ad97b..fa06bbc 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/user/UserInfoActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/user/UserInfoActivity.kt
@@ -26,6 +26,7 @@ import com.ck.ckcollar.app.ui.dialog.CKEditNameDialog
import com.ck.ckcollar.app.ui.dialog.CKEditPhoneDialog
import com.ck.ckcollar.app.ui.dialog.CKPhotoDialog
import com.ck.ckcollar.app.utils.Const
+import com.ck.ckcollar.app.utils.IDCardValidate
import com.ck.ckcollar.app.utils.networkone.ICallback
import com.ck.ckcollar.app.utils.networkone.TTHttpClient
import com.ck.ckcollar.app.utils.networkone.model.TTRequestData
@@ -86,7 +87,7 @@ class UserInfoActivity: CKBaseActivity() {
binding.textPhone.setOnClickListener {
CKEditPhoneDialog(
this@UserInfoActivity,
- getString(R.string.bind_phone),
+ getString(R.string.modify_phone_number),
object : CKEditPhoneDialog.AlertClickListener {
override fun onBack(text: String) {
binding.textPhone.text = text
@@ -99,7 +100,12 @@ class UserInfoActivity: CKBaseActivity() {
binding.textIdCard.setOnClickListener {
CKEditDialog(this@UserInfoActivity,getString(R.string.id_card),binding.textIdCard.text.toString(),object : CKEditDialog.AlertClickListener{
override fun onBack(text: String) {
- binding.textIdCard.text = text
+ val idCard = text.trim().uppercase()
+ if (!IDCardValidate.validateEffective(idCard)) {
+ toast("请输入正确的身份证号码")
+ return
+ }
+ binding.textIdCard.text = idCard
save()
}
}).show()
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/act/welcome/IndexActivity.kt b/app/src/main/java/com/ck/ckcollar/app/ui/act/welcome/IndexActivity.kt
index 0a2d08a..2f77772 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/act/welcome/IndexActivity.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/act/welcome/IndexActivity.kt
@@ -4,9 +4,27 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
+import android.app.AlertDialog
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.text.SpannableString
+import android.text.Spanned
+import android.text.method.LinkMovementMethod
+import android.text.style.ClickableSpan
+import android.view.View
+import android.view.Gravity
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
+import android.widget.ScrollView
import com.ck.ckcollar.app.CKApp
+import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityIndexBinding
import com.ck.ckcollar.app.ui.act.home.MainActivity
+import com.ck.ckcollar.app.ui.act.login.AgreementActivity
+import com.ck.ckcollar.app.ui.act.setting.SettingWebViewActivity
+import com.ck.ckcollar.app.config.Config
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
import com.cyclone.shadowsocks.utils.PreferencesUtils
@@ -23,19 +41,89 @@ class IndexActivity : CKBaseActivity() {
}
var handler = object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
- if (!CKApp.app.isUserLoggedIn){
- var isFirst = PreferencesUtils.getBolean(this@IndexActivity, Const.FIRST,true) as Boolean
- if (isFirst){
- toActivity(WelcomeActivity::class.java, DataCollection())
- }else{
- toActivity(MainActivity::class.java, DataCollection())
- }
- }else{
- toActivity(MainActivity::class.java, DataCollection())
- }
+ continueAfterAgreement()
+ }
+ }
+ private fun continueAfterAgreement() {
+ val agreed = PreferencesUtils.getBolean(this, Const.AGREEMENT_CHECK, false) as Boolean
+ if (!agreed) {
+ showUserNotice()
+ return
+ }
+ val isFirst = PreferencesUtils.getBolean(this, Const.FIRST, true) as Boolean
+ toActivity(if (isFirst) WelcomeActivity::class.java else MainActivity::class.java, DataCollection())
+ finish()
+ }
- finish()
+ private fun showUserNotice() {
+ fun dp(value: Int) = (value * resources.displayMetrics.density).toInt()
+ val title = TextView(this).apply {
+ text = "用户须知"
+ setTextColor(Color.rgb(35, 39, 55))
+ textSize = 20f
+ gravity = Gravity.CENTER
+ setTypeface(typeface, android.graphics.Typeface.BOLD)
+ }
+ val content = TextView(this).apply {
+ setTextColor(Color.rgb(95, 99, 115))
+ textSize = 14f
+ setLineSpacing(0f, 1.35f)
+ movementMethod = LinkMovementMethod.getInstance()
+ val text = "感谢您使用宠盾!我们非常重视您的个人信息和隐私保护。请阅读并同意《用户协议》和《隐私协议》后继续使用。\n\n1. 为提供宠物管理、设备连接、消息通知和 AI 识别等服务,我们可能会收集必要的账号、设备及使用信息。\n\n2. 在您使用拍照、扫码、地图定位、蓝牙或相册等功能时,我们会在获得授权后申请相应权限;您可以随时在系统设置中关闭权限,但可能无法使用对应功能。\n\n3. 我们会采取合理的安全措施保护您的信息,不会将信息用于协议约定以外的用途,也不会向无关方出售或出租您的信息。\n\n4. 为保障服务稳定运行,部分功能可能由第三方服务提供商协助完成,我们会要求其按照法律法规和隐私政策保护您的信息。\n\n5. 当协议内容或服务发生重要变化时,我们会通过弹窗或其他适当方式提醒您。您可以通过应用内相关入口再次查看完整协议。"
+ val spannable = SpannableString(text)
+ val userStart = text.indexOf("《用户协议》")
+ val privacyStart = text.indexOf("《隐私协议》")
+ spannable.setSpan(object : ClickableSpan() {
+ override fun onClick(widget: View) = toActivity(AgreementActivity::class.java, DataCollection().apply { add(com.tt.kit.model.Data("title", getString(R.string.user_agreement))) })
+ }, userStart, userStart + 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+ spannable.setSpan(object : ClickableSpan() {
+ override fun onClick(widget: View) = toActivity(SettingWebViewActivity::class.java, DataCollection().apply {
+ add(com.tt.kit.model.Data("url", Config.PRIVACY_POLICY)); add(com.tt.kit.model.Data("title", getString(R.string.privacy_agreement)))
+ })
+ }, privacyStart, privacyStart + 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+ setText(spannable)
+ }
+ val contentScroll = ScrollView(this).apply {
+ isFillViewport = true
+ isVerticalScrollBarEnabled = false
+ addView(content, ViewGroup.LayoutParams(-1, -2))
+ }
+ lateinit var dialog: AlertDialog
+ val buttons = LinearLayout(this).apply {
+ orientation = LinearLayout.HORIZONTAL
+ gravity = Gravity.CENTER
+ }
+ fun action(text: String, backgroundColor: Int, textColor: Int, onClick: () -> Unit) = TextView(this).apply {
+ this.text = text; gravity = Gravity.CENTER; setTextColor(textColor); textSize = 15f
+ setTypeface(typeface, android.graphics.Typeface.BOLD); setPadding(0, dp(13), 0, dp(13))
+ background = GradientDrawable().apply { setColor(backgroundColor); cornerRadius = dp(10).toFloat() }
+ setOnClickListener { onClick() }
+ }
+ val rejectButton = action("不同意", Color.rgb(245, 246, 250), Color.rgb(35, 39, 55), {
+ dialog.dismiss(); finishAndRemoveTask()
+ })
+ val agreeButton = action("同意", getColor2(R.attr.buttonColor), Color.WHITE) {
+ PreferencesUtils.putBoolean(this@IndexActivity, Const.AGREEMENT_CHECK, true)
+ dialog.dismiss(); continueAfterAgreement()
+ }
+ buttons.addView(rejectButton, LinearLayout.LayoutParams(0, dp(52), 1f).apply { marginEnd = dp(7) })
+ buttons.addView(agreeButton, LinearLayout.LayoutParams(0, dp(52), 1f).apply { marginStart = dp(7) })
+ val panel = LinearLayout(this).apply {
+ orientation = LinearLayout.VERTICAL
+ setPadding(dp(24), dp(25), dp(24), dp(24))
+ addView(title, LinearLayout.LayoutParams(-1, dp(40)))
+ addView(contentScroll, LinearLayout.LayoutParams(-1, dp(390)).apply { topMargin = dp(12); bottomMargin = dp(20) })
+ addView(buttons, LinearLayout.LayoutParams(-1, dp(52)))
+ background = GradientDrawable().apply { setColor(Color.WHITE); cornerRadius = dp(22).toFloat() }
+ }
+ dialog = AlertDialog.Builder(this).setView(panel).setCancelable(false).create()
+ dialog.show()
+ dialog.window?.apply {
+ setBackgroundDrawableResource(android.R.color.transparent)
+ setDimAmount(0.55f)
+ setGravity(Gravity.CENTER)
+ setLayout((resources.displayMetrics.widthPixels * 0.82f).toInt(), WindowManager.LayoutParams.WRAP_CONTENT)
}
}
diff --git a/app/src/main/java/com/ck/ckcollar/app/ui/dialog/CKEditPhoneDialog.kt b/app/src/main/java/com/ck/ckcollar/app/ui/dialog/CKEditPhoneDialog.kt
index 64fd3dd..2c7dcd2 100644
--- a/app/src/main/java/com/ck/ckcollar/app/ui/dialog/CKEditPhoneDialog.kt
+++ b/app/src/main/java/com/ck/ckcollar/app/ui/dialog/CKEditPhoneDialog.kt
@@ -1,6 +1,7 @@
package com.ck.ckcollar.app.ui.dialog
import android.content.Context
+import android.content.res.ColorStateList
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
@@ -33,8 +34,15 @@ class CKEditPhoneDialog(
dialogAlertBinding.title.text = title
}
dialogAlertBinding.btnSendCode.setOnClickListener { sendCode() }
+ dialogAlertBinding.btnSendCode.setTextColor(
+ ColorStateList(
+ arrayOf(intArrayOf(-android.R.attr.state_enabled), intArrayOf()),
+ intArrayOf(mContext.getColor(R.color.textSecondColor), mContext.getColor(R.color.appStyle))
+ )
+ )
dialogAlertBinding.cancelButton.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
+ dialogAlertBinding.btnSendCode.stopCountDown()
cancel()
}
})
@@ -72,7 +80,7 @@ class CKEditPhoneDialog(
add(TTRequestData("smsCode",dialogAlertBinding.textCode.text.toString()))
},object : ICallback{
override fun onSuccess(data: String) {
- mContext.toast(mContext.getString(R.string.send_success))
+ mContext.toast(mContext.getString(R.string.edit_success))
cancel()
}
@@ -94,7 +102,8 @@ class CKEditPhoneDialog(
add(TTRequestData("scene","changePhone"))
},object : ICallback{
override fun onSuccess(data: String) {
- mContext.toast(mContext.getString(R.string.edit_success))
+ mContext.toast(mContext.getString(R.string.verification_code_sent))
+ dialogAlertBinding.btnSendCode.startCountDown()
alertClickListener.onBack(dialogAlertBinding.textPhone.text.toString())
}
@@ -108,4 +117,4 @@ class CKEditPhoneDialog(
interface AlertClickListener {
fun onBack(text : String)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/ck/ckcollar/app/utils/IDCardValidate.kt b/app/src/main/java/com/ck/ckcollar/app/utils/IDCardValidate.kt
new file mode 100644
index 0000000..a50557d
--- /dev/null
+++ b/app/src/main/java/com/ck/ckcollar/app/utils/IDCardValidate.kt
@@ -0,0 +1,81 @@
+package com.ck.ckcollar.app.utils
+
+import java.time.DateTimeException
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+import java.time.format.ResolverStyle
+
+/** 校验中国大陆地区的 15 位和 18 位居民身份证号码。 */
+object IDCardValidate {
+ private val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
+ private val verifyCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
+ private val areaCodes = setOf(
+ "11", "12", "13", "14", "15",
+ "21", "22", "23",
+ "31", "32", "33", "34", "35", "36", "37",
+ "41", "42", "43", "44", "45", "46",
+ "50", "51", "52", "53", "54",
+ "61", "62", "63", "64", "65",
+ "71", "81", "82", "91"
+ )
+ private val birthDateFormatter = DateTimeFormatter
+ .ofPattern("uuuuMMdd")
+ .withResolverStyle(ResolverStyle.STRICT)
+ private val earliestBirthDate = LocalDate.of(1900, 1, 1)
+
+ @JvmStatic
+ fun validate_effective(idcardNumber: String?): Boolean = validateEffective(idcardNumber)
+
+ /**
+ * 为兼容 Water 项目而保留,第二个参数不会影响校验结果。
+ */
+ @JvmStatic
+ @Suppress("UNUSED_PARAMETER")
+ fun validate_effective(idcardNumber: String?, isreturn_AutoCard: Boolean): Boolean =
+ validateEffective(idcardNumber)
+
+ @JvmStatic
+ fun validateEffective(idCardNumber: String?): Boolean {
+ val source = idCardNumber?.trim()?.uppercase() ?: return false
+ val cardNumber = when (source.length) {
+ OLD_CARD_NUMBER_LENGTH -> convertToNewCardNumber(source) ?: return false
+ NEW_CARD_NUMBER_LENGTH -> source
+ else -> return false
+ }
+
+ if (!cardNumber.take(17).all(Char::isDigit)) return false
+ if (cardNumber.last() != 'X' && !cardNumber.last().isDigit()) return false
+ if (cardNumber.substring(0, 2) !in areaCodes) return false
+ if (!hasValidBirthDate(cardNumber.substring(6, 14))) return false
+ return cardNumber.last() == calculateVerifyCode(cardNumber)
+ }
+
+ private fun convertToNewCardNumber(oldCardNumber: String): String? {
+ if (!oldCardNumber.all(Char::isDigit)) return null
+ val firstSeventeen = buildString(17) {
+ append(oldCardNumber, 0, 6)
+ append("19")
+ append(oldCardNumber, 6, oldCardNumber.length)
+ }
+ return firstSeventeen + calculateVerifyCode(firstSeventeen)
+ }
+
+ private fun hasValidBirthDate(value: String): Boolean {
+ val birthDate = try {
+ LocalDate.parse(value, birthDateFormatter)
+ } catch (_: DateTimeException) {
+ return false
+ }
+ return birthDate.isAfter(earliestBirthDate) && birthDate.isBefore(LocalDate.now())
+ }
+
+ private fun calculateVerifyCode(cardNumber: CharSequence): Char {
+ val sum = weights.indices.sumOf { index ->
+ (cardNumber[index] - '0') * weights[index]
+ }
+ return verifyCodes[sum % 11]
+ }
+
+ private const val NEW_CARD_NUMBER_LENGTH = 18
+ private const val OLD_CARD_NUMBER_LENGTH = 15
+}
diff --git a/app/src/main/res/layout/activity_after_sales.xml b/app/src/main/res/layout/activity_after_sales.xml
index a0016ce..778f821 100644
--- a/app/src/main/res/layout/activity_after_sales.xml
+++ b/app/src/main/res/layout/activity_after_sales.xml
@@ -102,7 +102,7 @@
android:text="@string/no_reason_text"
android:background="@drawable/bg_cz_button"
android:paddingHorizontal="5dp"
- >
+ android:visibility="gone"/>
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_pay_order.xml b/app/src/main/res/layout/activity_pay_order.xml
index 5b2d452..31dfec3 100644
--- a/app/src/main/res/layout/activity_pay_order.xml
+++ b/app/src/main/res/layout/activity_pay_order.xml
@@ -232,7 +232,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
- android:visibility="gone"
+ android:visibility="visible"
>
diff --git a/app/src/main/res/layout/activity_recharge.xml b/app/src/main/res/layout/activity_recharge.xml
index ec3f2da..72d8db3 100644
--- a/app/src/main/res/layout/activity_recharge.xml
+++ b/app/src/main/res/layout/activity_recharge.xml
@@ -56,38 +56,6 @@
android:layout_marginBottom="10dp"
>
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
取消成功
收货成功
绑定成功
+ 修改手机号
下一步
立即体验
@@ -44,7 +45,7 @@
进入平台(%sS)
- (%s) 秒
+ (%s) 秒后重发
发送验证码
登录
注册
@@ -505,6 +506,7 @@
输入详细地址
选择标签
输入正确手机号
+ 验证码已发送
注册成功
找回成功