1.修复漏洞:BaseDao 中的 SQL 注入

2.移除ACCESS_MOCK_LOCATION  权限
3.MMKV启用AES-256加密保存
This commit is contained in:
2026-07-02 17:47:59 +08:00
parent 5ac14912bb
commit 815464ef20
22 changed files with 350 additions and 150 deletions

View File

@@ -4,7 +4,7 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-06-25T10:07:54.478573Z">
<DropdownSelection timestamp="2026-07-01T08:15:08.155754600Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=3A251FDJH000CE" />

View File

@@ -28,9 +28,9 @@ android {
applicationId "com.abbidot.tracker"
minSdkVersion 23
targetSdkVersion 35
versionCode 2214
// versionName "2.2.14"
versionName "2.2.14-Beta1"
versionCode 2215
// versionName "2.2.15"
versionName "2.2.15-Beta1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

177
app/proguard-rules.pro vendored
View File

@@ -23,12 +23,175 @@
#禁用所有优化规则,查看混淆后 APK是否可以正常工作
-dontoptimize
#-dontoptimize
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.baidu.mapsdkplatform.comapi.util.SysOSAPI
-dontwarn com.daimajia.easing.Glider
-dontwarn com.daimajia.easing.Skill
# 基础混淆配置
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep class android.*
# 保留注解、泛型、异常信息
-keepattributes *Annotation*,Signature,Exception
-keepattributes InnerClasses,EnclosingMethod
# Android基础组件保护
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
# 保留Activity生命周期方法
-keepclassmembers public class * extends android.app.Activity {
public void *(android.view.View);
public void on*(...);
}
# 保留Service生命周期方法
-keepclassmembers public class * extends android.app.Service {
public void on*(...);
}
# 保留BroadcastReceiver接收方法
-keepclassmembers class * extends android.content.BroadcastReceiver {
public void onReceive(android.content.Context, android.content.Intent);
}
# 保留ContentProvider方法
-keepclassmembers class * extends android.content.ContentProvider {
public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
}
# 保留R文件
-keep class **.R$* { *; }
# Parcelable和Serializable支持
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# Kotlin特定规则
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
<fields>;
}
-keepclassmembers class kotlinx.coroutines.internal.MainCoroutineDispatcherFactory {
public <init>();
}
-keepclassmembers class kotlinx.coroutines.CoroutineExceptionHandler {
public <init>();
}
# 数据类保护(根据项目实际数据类包名调整)
-keep class com.abbidot.tracker.data.** { *; }
-keep class com.abbidot.tracker.model.** { *; }
# ViewBinding支持
-keep class com.abbidot.tracker.databinding.** { *; }
# Hilt依赖注入
-keep class dagger.hilt.android.lifecycle.HiltViewModelFactory { *; }
-keep class * extends dagger.hilt.android.lifecycle.HiltViewModel { *; }
-keep @dagger.hilt.android.lifecycle.HiltViewModel class * { *; }
-keep @dagger.hilt.android.AndroidEntryPoint class * { *; }
-keep @dagger.Module class * { *; }
-keep @dagger.hilt.EntryPoint class * { *; }
# Room数据库
-keep class * extends androidx.room.RoomDatabase { *; }
-keep @androidx.room.Entity class * { *; }
-keep interface * extends androidx.room.Dao { *; }
-keepclassmembers class * extends androidx.room.RoomDatabase {
<methods>;
}
# 第三方库规则
# Coil图片加载
-keep class io.coil.** { *; }
-dontwarn io.coil.**
# Lottie动画
-keep class com.airbnb.lottie.** { *; }
-dontwarn com.airbnb.lottie.**
# BaseRecyclerViewAdapterHelper
-keep class com.chad.library.adapter.** { *; }
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
# Mapbox
-keep class com.mapbox.** { *; }
-dontwarn com.mapbox.**
# 百度地图
-keep class com.baidu.** { *; }
-keep class vi.com.gdi.bgl.android.** { *; }
-dontwarn com.baidu.**
# 极光推送
-keep class cn.jiguang.** { *; }
-dontwarn cn.jiguang.**
# Stripe支付
-keep class com.stripe.** { *; }
-dontwarn com.stripe.**
# Braintree支付
-keep class com.braintreepayments.** { *; }
-dontwarn com.braintreepayments.**
# 微信SDK
-keep class com.tencent.mm.opensdk.** { *; }
-keep class com.tencent.wxop.** { *; }
-keep class com.tencent.mm.sdk.** { *; }
# 支付宝SDK
-keep class com.alipay.** { *; }
-dontwarn com.alipay.**
# DFU升级库
-keep class no.nordicsemi.android.** { *; }
-dontwarn no.nordicsemi.android.**
# 测试相关
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
# 移除日志(发布版)
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
# 混淆映射输出
-printmapping proguardMapping.txt
-printseeds proguardSeeds.txt
-printusage proguardUsage.txt

View File

@@ -43,9 +43,9 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 读取缓存数据 -->
<!-- 蓝牙扫描权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 获取模拟定位信息 -->
<uses-permission
android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="MockLocation,ProtectedPermissions" />
<!-- <uses-permission-->
<!-- android:name="android.permission.ACCESS_MOCK_LOCATION"-->
<!-- tools:ignore="MockLocation,ProtectedPermissions" />-->
<permission
android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
@@ -95,7 +95,8 @@
<application
android:name=".MyApplication"
android:allowBackup="true"
android:allowBackup="false"
android:fullBackupContent="false"
android:icon="@mipmap/logo_abbidot"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"

View File

@@ -578,10 +578,11 @@ abstract class BaseActivity<T : ViewBinding>(val inflater: (inflater: LayoutInfl
fun <T> dealRequestResult(
it: Result<T>,
getResultCallback: GetResultCallback,
showLoading: Boolean = false,
isShowNoDataTip: Boolean = true,
isShowRequestErrorTip: Boolean = true,
isShowCodeError: Boolean = true,
showLoading: Boolean = false,//是否显示loading弹窗
isShowNoDataTip: Boolean = true,//是否显示无数据提示
isShowRequestErrorTip: Boolean = true,//是否显示请求错误提示
isShowCodeError: Boolean = true,//是否显示错误码提示
isShowNetCodeMsg: Boolean = false,//是否显示服务器返回message提示
errorTip: String = getString(R.string.txt_net_error)
) {
try {
@@ -614,7 +615,9 @@ abstract class BaseActivity<T : ViewBinding>(val inflater: (inflater: LayoutInfl
val data = it.getOrNull()
if (null == data) {
if (isShowRequestErrorTip) {
if (isShowNetCodeMsg && exceptionCode == ErrorCode.ERROR.toString()) {
showToast(exceptionMessage ?: errorTip)
} else if (isShowRequestErrorTip) {
if (NetworkUtil.isNetworkAvailable(mContext)) {
showToast(errorTip)
} else {

View File

@@ -179,10 +179,11 @@ abstract class BaseFragment<T : ViewBinding>(
fun <T> dealRequestResult(
it: Result<T>,
getResultCallback: GetResultCallback,
showLoading: Boolean = false,
isShowNoDataTip: Boolean = true,
isRequestErrorTip: Boolean = true,
isShowCodeError: Boolean = true,
showLoading: Boolean = false,//是否显示loading弹窗
isShowNoDataTip: Boolean = true,//是否显示无数据提示
isRequestErrorTip: Boolean = true,//是否显示请求错误提示
isShowCodeError: Boolean = true,//是否显示错误码提示
isShowNetCodeMsg: Boolean = false,//是否显示服务器返回message提示
errorTip: String = getString(R.string.txt_net_error)
) {
(activity as BaseActivity<*>).dealRequestResult(
@@ -191,8 +192,8 @@ abstract class BaseFragment<T : ViewBinding>(
showLoading,
isShowNoDataTip,
isRequestErrorTip,
isShowCodeError,
errorTip
isShowCodeError, isShowNetCodeMsg,
errorTip,
)
}
}

View File

@@ -41,7 +41,7 @@ abstract class MyDatabase : RoomDatabase() {
private val db: MyDatabase by lazy {
Room.databaseBuilder(MyApplication.instance, MyDatabase::class.java, "tracker_dp")
// .addMigrations(MIGRATION_1_2)
.fallbackToDestructiveMigration()//删除旧表格并创建新表格,的方式升级数据库版本
.fallbackToDestructiveMigration(true)//删除旧表格并创建新表格,的方式升级数据库版本
.build()
}

View File

@@ -1,9 +1,13 @@
package com.abbidot.tracker.database.dao
import android.util.Log
import androidx.room.*
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.RawQuery
import androidx.room.Update
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import com.abbidot.baselibrary.util.LogUtil
/**
*Created by .yzq on 2022/4/1/001.
@@ -28,43 +32,43 @@ interface BaseDao<T> {
* OnConflictStrategy.REPLACE新数据替换旧数据
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(obj: T): Long
fun insert(obj: T): Long
/**
* 添加数组对象数据
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg objs: T): LongArray?
fun insert(vararg objs: T): LongArray?
/**
* 添加对象集合
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(personList: List<T>): List<Long>
fun insert(personList: List<T>): List<Long>
/**
* 根据对象中的主键删除(主键是自动增长的,无需手动赋值)
*/
@Delete
fun delete(obj: T)
fun delete(obj: T)
/**
* 根据对象中的主键更新(主键是自动增长的,无需手动赋值)
*/
@Update
fun update(vararg obj: T): Int
fun update(vararg obj: T): Int
fun deleteAll(tableName:String): Int {
fun deleteAll(tableName: String): Int {
val query = SimpleSQLiteQuery("delete from $tableName")
return doDeleteAll(query)
}
fun findAll(tableName:String): List<T>? {
fun findAll(tableName: String): List<T>? {
val query = SimpleSQLiteQuery("select * from $tableName")
return doFindAll(query)
}
fun find(id: Long,tableName:String): T? {
fun find(id: Long, tableName: String): T? {
val query = SimpleSQLiteQuery("select * from $tableName where id = ?", arrayOf<Any>(id))
return doFind(query)
}
@@ -73,9 +77,9 @@ interface BaseDao<T> {
* [params] 列名
* [value] 列的值
*/
fun deleteByParams(params: String, value: String,tableName:String): Int {
val query = SimpleSQLiteQuery("delete from $tableName where $params='${value}'")
Log.d("ez", "deleteByParams: ${"delete from $tableName where $params='${value}'"}")
fun deleteByParams(params: String, value: String, tableName: String): Int {
val query = SimpleSQLiteQuery("delete from $tableName where $params= ?", arrayOf(value))
LogUtil.d("ez", "deleteByParams: ${"delete from $tableName where $params = ?"}")
return doDeleteByParams(query)
}
@@ -83,18 +87,26 @@ interface BaseDao<T> {
* 分页查询,支持传入多个字段,但必须要按照顺序传入
* key = valuekey = value 的形式,一一对应(可以使用 stringbuilder 去构造一下,这里就不演示了)
*/
fun doQueryByLimit(tableName:String,vararg string: String, limit: Int = 10, offset: Int = 0): List<T>? {
fun doQueryByLimit(
tableName: String, vararg string: String, limit: Int = 10, offset: Int = 0
): List<T>? {
val query = SimpleSQLiteQuery(
"SELECT * FROM $tableName WHERE ${string[0]} = '${string[1]}' limit $limit offset $offset")
"SELECT * FROM $tableName WHERE ${string[0]} = ? limit $limit offset $offset",
arrayOf(string[1])
)
return doQueryByLimit(query)
}
/**
* 降序分页查询
*/
fun doQueryByOrder(tableName:String,vararg string: String, limit: Int = 10, offset: Int = 10): List<T>? {
fun doQueryByOrder(
tableName: String, vararg string: String, limit: Int = 10, offset: Int = 10
): List<T>? {
// 修复 SQL 注入1. 去掉 limit 和 offset 的单引号 2. 确保排序字段安全(建议增加白名单验证)
val query = SimpleSQLiteQuery(
"SELECT * FROM $tableName ORDER BY ${string[0]} desc limit '${limit}' offset '${offset}'")
"SELECT * FROM $tableName ORDER BY ${string[0]} desc limit $limit offset $offset"
)
return doQueryByOrder(query)
}

View File

@@ -10,7 +10,6 @@ import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.bean.CreditCardBean
import com.abbidot.tracker.constant.ConstantString
import com.abbidot.tracker.database.MyDatabase
import com.abbidot.tracker.databinding.ActivityAddCreditCardBinding
import com.abbidot.tracker.util.Util
import kotlinx.coroutines.Dispatchers
@@ -123,7 +122,7 @@ class AddCreditCardActivity :
card.firstName = firstName
card.expirationDate = creditCardValidity
card.cvv = cvv
MyDatabase.creditCardDao().insert(card)
// MyDatabase.creditCardDao().insert(card)
runOnUiThread {
val intent = Intent()
intent.putExtra(ConstantString.Card, card)

View File

@@ -20,16 +20,13 @@ import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.database.MyDatabase
import com.abbidot.tracker.databinding.ActivityLoginV2Binding
import com.abbidot.tracker.ui.activity.set.FirstPermissionsActivity
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.vm.LoginViewModel
import com.abbidot.tracker.vm.TrackerInfoViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LoginV2Activity : BaseActivity<ActivityLoginV2Binding>(ActivityLoginV2Binding::inflate) {
@@ -136,14 +133,14 @@ class LoginV2Activity : BaseActivity<ActivityLoginV2Binding>(ActivityLoginV2Bind
)
}
}
withContext(Dispatchers.IO) {
//保存本地数据库
for (device in list) {
if (!TextUtils.isEmpty(device.deviceOutId) && null != device.pet) {
MyDatabase.deviceDao().insert(device)
}
}
}
// withContext(Dispatchers.IO) {
// //保存本地数据库
// for (device in list) {
// if (!TextUtils.isEmpty(device.deviceOutId) && null != device.pet) {
// MyDatabase.deviceDao().insert(device)
// }
// }
// }
showToast(R.string.txt_login_successful, gravity = Gravity.CENTER)

View File

@@ -166,7 +166,7 @@ class MyTrackerV2Activity :
//保存本地数据库
if (!TextUtils.isEmpty(device.deviceOutId) && null != device.pet) {
bindDeviceNumber++
MyDatabase.deviceDao().insert(device)
// MyDatabase.deviceDao().insert(device)
} else if (!TextUtils.isEmpty(device.deviceOutId) && null == device.pet) {
//删除本地没绑定的设备
MyDatabase.deviceDao().deleteByParams(

View File

@@ -118,18 +118,18 @@ class FirstPetProfileActivity :
mTrackerViewModel.getDeviceListLiveData.observe(this) {
dealRequestResult(it, object : GetResultCallback {
override fun onResult(any: Any) {
it.getOrNull()?.let { list ->
lifecycleScope.launch {
withContext(Dispatchers.IO) {
for (device in list) {
//保存本地数据库
if (!TextUtils.isEmpty(device.deviceOutId) && null != device.pet) {
MyDatabase.deviceDao().insert(device)
}
}
}
}
}
// it.getOrNull()?.let { list ->
// lifecycleScope.launch {
// withContext(Dispatchers.IO) {
// for (device in list) {
// //保存本地数据库
// if (!TextUtils.isEmpty(device.deviceOutId) && null != device.pet) {
// MyDatabase.deviceDao().insert(device)
// }
// }
// }
// }
// }
showToast(R.string.txt_bind_success, isFinish = true)
mViewBinding.root.postDelayed({

View File

@@ -1,5 +1,6 @@
package com.abbidot.tracker.ui.activity.subscribe
import android.content.Intent
import android.text.InputFilter
import android.text.InputFilter.LengthFilter
import android.text.InputType
@@ -143,7 +144,7 @@ class AddCreditCardPaymentActivity :
override fun onRequestError(exceptionCode: String?) {
setButtonEnabled(mViewBinding.btnSaveNewCreditCard, ConstantInt.Type1)
}
})
}, isShowNetCodeMsg = true)
}
}
@@ -336,7 +337,7 @@ class AddCreditCardPaymentActivity :
it.cardName = cardName
it.expirationDate = creditCardValidity
it.cvv = cvv
MyDatabase.creditCardDao().insert(it)
// MyDatabase.creditCardDao().insert(it)
}
runOnUiThread {
@@ -410,28 +411,21 @@ class AddCreditCardPaymentActivity :
override fun onSuccess(result: PaymentMethod) {
LogUtil.e(result.toString())
MyDatabase.creditCardDao()
.deleteAll(MyDatabase.CREDIT_CARD_TABLE)
it.cardNumber = cardNumber
it.cardName = cardName
it.expirationDate = creditCardValidity
it.cvv = cvv
MyDatabase.creditCardDao().insert(it)
val paymentMethodID = "${result.id}"
mSubscriptionPayViewModel.savePaymentMethodInfo(
paymentMethodID
)
mSubscriptionPayViewModel.savePaymentMethodInfo(paymentMethodID)
}
})
} else {
MyDatabase.creditCardDao().deleteAll(MyDatabase.CREDIT_CARD_TABLE)
// MyDatabase.creditCardDao().deleteAll(MyDatabase.CREDIT_CARD_TABLE)
it.cardNumber = cardNumber
it.cardName = cardName
it.expirationDate = creditCardValidity
it.cvv = cvv
MyDatabase.creditCardDao().insert(it)
// MyDatabase.creditCardDao().insert(it)
runOnUiThread {
setResult(ResultCode.ResultCode_1, null)
val intent = Intent()
intent.putExtra(ConstantString.Card, it)
setResult(ResultCode.ResultCode_1, intent)
showToast(R.string.txt_save_successful, isFinish = true)
}
}

View File

@@ -6,7 +6,6 @@ import android.text.TextUtils
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.abbidot.baselibrary.constant.ResultCode
import com.abbidot.baselibrary.list.BaseRecyclerAdapter
import com.abbidot.baselibrary.util.AppUtils
@@ -17,20 +16,18 @@ import com.abbidot.tracker.base.BaseFragment
import com.abbidot.tracker.bean.CreditCardBean
import com.abbidot.tracker.constant.ConstantString
import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.database.MyDatabase
import com.abbidot.tracker.databinding.FragmentCreditCardPaymentBinding
import com.abbidot.tracker.ui.activity.subscribe.AddCreditCardPaymentActivity
import com.abbidot.tracker.ui.activity.subscribe.PaymentMethodActivity
import com.abbidot.tracker.ui.activity.subscribe.PaymentSuccessActivity
import com.abbidot.tracker.util.ThirdPartyUtil
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.vm.SubscriptionPayViewModel
import com.stripe.android.ApiResultCallback
import com.stripe.android.Stripe
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.PaymentMethodCreateParams
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
@@ -87,6 +84,7 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
mContext!!, rvCreditCardPaymentList, mCreditCardAdapter, top = AppUtils.dpToPx(10)
)
ilCreditCardPaymentNoData.btnSureSubscribePlan1Continue.visibility = View.GONE
setOnClickListenerViews(btnCreditCardPaymentAdd, btnMakePaymentCreditCard)
}
// getCardData()
@@ -94,17 +92,28 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
fun getCardData(stripeMethodInfo: CreditCardBean) {
if (TextUtils.isEmpty(stripeMethodInfo.lastFourNumber)) {
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
val list = MyDatabase.creditCardDao().findAll(MyDatabase.CREDIT_CARD_TABLE)
list?.let {
mActivity?.runOnUiThread {
val sortedByList =
list.sortedByDescending { l -> l.isPrimary }.toMutableList()
setCheckAndPrimaryState(sortedByList)
mCreditCardAdapter.setData(sortedByList, true)
}
// viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
// val list = MyDatabase.creditCardDao().findAll(MyDatabase.CREDIT_CARD_TABLE)
// list?.let {
// mActivity?.runOnUiThread {
// val sortedByList =
// list.sortedByDescending { l -> l.isPrimary }.toMutableList()
// setCheckAndPrimaryState(sortedByList)
// mCreditCardAdapter.setData(sortedByList, true)
// }
// }
// }
//没有卡,显示无卡提示 不用去添加卡
mViewBinding.apply {
if (null == getHostActivity(PaymentMethodActivity::class.java)?.mOrderBean) {
ilCreditCardPaymentNoData.root.visibility = View.VISIBLE
rvCreditCardPaymentList.visibility = View.GONE
} else {
ilCreditCardPaymentNoData.root.visibility = View.GONE
rvCreditCardPaymentList.visibility = View.VISIBLE
}
}
} else {
stripeMethodInfo.apply {
expirationDate = "$expMonth/$expYear"
@@ -127,8 +136,9 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
add(stripeMethodInfo)
mCreditCardAdapter.setData(this, true)
}
mViewBinding.ilCreditCardPaymentNoData.root.visibility = View.GONE
ViewUtil.instance.viewShow(mViewBinding.rvCreditCardPaymentList)
}
ViewUtil.instance.viewShow(mViewBinding.rvCreditCardPaymentList)
}
fun goEditCard(cardBean: CreditCardBean) {
@@ -162,7 +172,7 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
override fun onRequestError(exceptionCode: String?) {
showNoCancelableLoading(false)
}
})
}, isShowNetCodeMsg = true)
}
mCreateStripeOrderLiveData.observe(viewLifecycleOwner) {
@@ -245,7 +255,19 @@ class CreditCardPaymentFragment : BaseFragment<FragmentCreditCardPaymentBinding>
// }
if (it.resultCode == ResultCode.ResultCode_1) {
// getCardData()
getHostActivity(PaymentMethodActivity::class.java)?.getUserPaymentMethodID()
if (null != it.data) {
val card = Util.getParcelableAdaptive(
it.data!!, ConstantString.Card, CreditCardBean::class.java
)
if (null != card) {
val list = mutableListOf<CreditCardBean>()
list.add(card)
setCheckAndPrimaryState(list)
mCreditCardAdapter.setData(list, true)
} else getHostActivity(PaymentMethodActivity::class.java)?.getUserPaymentMethodID()
} else {
getHostActivity(PaymentMethodActivity::class.java)?.getUserPaymentMethodID()
}
}
}

View File

@@ -19,11 +19,10 @@ import com.easysocket.interfaces.conn.ISocketActionListener
*/
class SocketUtilManage {
private val iP = "3.101.8.36"
// private val iP = "192.168.0.69"
private val iP = ""
//默认国外服务器端口
private var port = 8868
private var port = 0
@OptIn(ExperimentalUnsignedTypes::class)
private val crcTable = ushortArrayOf(
@@ -51,7 +50,7 @@ class SocketUtilManage {
fun initEasySocket(context: Context) {
if (AppUtils.isChina()) {
//国内socket端口号
port = 9168
port = 1
}
//socket配置
val options = EasySocketOptions.Builder().setSocketAddress(SocketAddress(iP, port))

View File

@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.baselibrary.util.Utils
import com.abbidot.baselibrary.util.rsa.RSA
import com.abbidot.tracker.bean.CreditCardBean
import com.abbidot.tracker.bean.PayResultBean
@@ -33,7 +34,7 @@ class SubscriptionPayViewModel : ViewModel() {
/**
* Stripe支付创建消费用户
*/
fun createCustomer(card: CreditCardBean,paymentMethodID: String) {
fun createCustomer(card: CreditCardBean, paymentMethodID: String) {
// activity.showLoading(true)
viewModelScope.launch(Dispatchers.IO) {
val userId = MMKVUtil.getString(MMKVKey.UserId)
@@ -48,12 +49,10 @@ class SubscriptionPayViewModel : ViewModel() {
cardParamsMap["cardName"] = card.cardName
cardParamsMap["cardnum"] = card.cardNumber
val cardJso = Gson().toJson(cardParamsMap)
val b =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofuJbnkwyF7SWg3eRadSYT+ttfHN+CUmCzH0WW1EWTUQOG+01Nv8ux+UJUlxbZA2X7959Et/+VKE03+4OY0/26dlgkROMlXCUnzsucuaoHW+9whGi6xklRZrDLi22QBae/8XV1YDEU2vfYXtcvwf66J0t+bRIhGytQUZvUrL7OEaAHvwJU0QwCy3wQGWcCgSkPpDruIe4ar19iZMleE+BdPGIfKfVdPvWFls63g1eMFECb8F9zN6ydGTCvlqvx/4gfDuFQnF+JuTSre8euSpTRTQn/Q0s3K6KiROVcAYCqDW6cKxNUr2ha1C9ZFxMws9C0qhx5wYzfSlIjFthPC1qwIDAQAB"
val rsaKey = RSA.encrypt(cardJso, b)
val rsaKey = RSA.encrypt(cardJso, Utils.CARD_RSA_KEY)
LogUtil.e(cardJso)
val result = NetworkApi.createCustomer(rsaKey, userId, userName, email,paymentMethodID)
val result = NetworkApi.createCustomer(rsaKey, userId, userName, email, paymentMethodID)
mSubscriptionCustomerLiveData.postValue(result)
}
}

View File

@@ -37,12 +37,17 @@
app:qmui_radius="@dimen/dp_64"
app:typeface="@string/roboto_bold_font" />
<include
android:id="@+id/il_credit_card_payment_no_data"
android:visibility="gone"
layout="@layout/layout_credit_card_no_data" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_credit_card_payment_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:layout_above="@id/btn_make_payment_credit_card"
android:layout_below="@id/btn_credit_card_payment_add"
android:layout_marginTop="@dimen/dp_12" />
android:layout_marginTop="@dimen/dp_12"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -2,5 +2,5 @@
<!-- 解决 Android P 禁用 http 请求的问题 -->
<network-security-config>
<!-- 允许明文通信 -->
<base-config cleartextTrafficPermitted="true" />
<base-config cleartextTrafficPermitted="false" />
</network-security-config>

View File

@@ -15,84 +15,90 @@ object MMKVUtil {
MMKV.initialize(application)
}
fun getMMKV(): MMKV {
return MMKV.defaultMMKV(MMKV.SINGLE_PROCESS_MODE, Utils.SECRET_KEY, true)
}
fun putBoolean(@MMKVKey key: String, value: Boolean) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getBoolean(@MMKVKey key: String, defaultValue: Boolean = false): Boolean {
return MMKV.defaultMMKV().decodeBool(key, defaultValue)
return getMMKV().decodeBool(key, defaultValue)
}
fun putString(@MMKVKey key: String, value: String) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getString(@MMKVKey key: String, defaultValue: String = ""): String {
return MMKV.defaultMMKV().decodeString(key, defaultValue)!!
return getMMKV().decodeString(key, defaultValue)!!
}
fun putInt(@MMKVKey key: String, value: Int) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getInt(@MMKVKey key: String, defaultValue: Int = 0): Int {
return MMKV.defaultMMKV().decodeInt(key, defaultValue)
return getMMKV().decodeInt(key, defaultValue)
}
fun putFloat(@MMKVKey key: String, value: Float) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getFloat(@MMKVKey key: String, defaultValue: Float = 0F): Float {
return MMKV.defaultMMKV().decodeFloat(key, defaultValue)
return getMMKV().decodeFloat(key, defaultValue)
}
fun putLong(@MMKVKey key: String, value: Long) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getLong(@MMKVKey key: String, defaultValue: Long = 0L): Long {
return MMKV.defaultMMKV().decodeLong(key, defaultValue)
return getMMKV().decodeLong(key, defaultValue)
}
fun putDouble(@MMKVKey key: String, value: Double) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getDouble(@MMKVKey key: String, defaultValue: Double = 0.0): Double {
return MMKV.defaultMMKV().decodeDouble(key, defaultValue)
return getMMKV().decodeDouble(key, defaultValue)
}
fun putByteArray(@MMKVKey key: String, value: ByteArray) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getByteArray(@MMKVKey key: String, defaultValue: ByteArray = ByteArray(0)): ByteArray {
return MMKV.defaultMMKV().decodeBytes(key, defaultValue)!!
return getMMKV().decodeBytes(key, defaultValue)!!
}
fun putStringSet(@MMKVKey key: String, value: Set<String>) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
fun getStringSet(@MMKVKey key: String,
defaultValue: Set<String> = mutableSetOf()): Set<String> {
return MMKV.defaultMMKV().decodeStringSet(key, defaultValue)!!
fun getStringSet(
@MMKVKey key: String,
defaultValue: Set<String> = mutableSetOf()
): Set<String> {
return getMMKV().decodeStringSet(key, defaultValue)!!
}
fun putParcelable(@MMKVKey key: String, value: Parcelable) {
MMKV.defaultMMKV().encode(key, value)
getMMKV().encode(key, value)
}
//清空所有sp
fun clearAll() {
//正式测试标识不清空
val debugIp = getBoolean(MMKVKey.DebugIp, true)
MMKV.defaultMMKV().clearAll()
getMMKV().clearAll()
putBoolean(MMKVKey.DebugIp, debugIp)
}
inline fun <reified T : Parcelable> getParcelable(@MMKVKey key: String): T? {
return MMKV.defaultMMKV().decodeParcelable(key, T::class.java)
return getMMKV().decodeParcelable(key, T::class.java)
}
}

View File

@@ -30,6 +30,7 @@ class Utils {
//HTTP请求加密key
var SECRET_KEY = ""
val CARD_RSA_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofuJbnkwyF7SWg3eRadSYT+ttfHN+CUmCzH0WW1EWTUQOG+01Nv8ux+UJUlxbZA2X7959Et/+VKE03+4OY0/26dlgkROMlXCUnzsucuaoHW+9whGi6xklRZrDLi22QBae/8XV1YDEU2vfYXtcvwf66J0t+bRIhGytQUZvUrL7OEaAHvwJU0QwCy3wQGWcCgSkPpDruIe4ar19iZMleE+BdPGIfKfVdPvWFls63g1eMFECb8F9zN6ydGTCvlqvx/4gfDuFQnF+JuTSre8euSpTRTQn/Q0s3K6KiROVcAYCqDW6cKxNUr2ha1C9ZFxMws9C0qhx5wYzfSlIjFthPC1qwIDAQAB"
const val DATE_FORMAT_PATTERN_CN = "yyyy-MM-dd"
const val DATE_FORMAT_PATTERN_CN2 = "yyyy-MM-dd HH:mm:ss"
const val DATE_FORMAT_PATTERN_CN3 = "yyyy/MM/dd HH:mm:ss"

View File

@@ -16,7 +16,7 @@
<application
android:allowBackup="true"
android:allowBackup="false"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true">
<activity

View File

@@ -5,7 +5,6 @@ import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@@ -37,7 +36,6 @@ public class FileUtils {
* TAG for log messages.
*/
static final String TAG = "FileUtils";
private static final boolean DEBUG = false; // Set to true to enable logging
/**
* File and folder comparator. TODO Expose sorting option method
*/
@@ -216,7 +214,7 @@ public class FileUtils {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs
, null);
if (cursor != null && cursor.moveToFirst()) {
if (DEBUG) DatabaseUtils.dumpCursor(cursor);
// if (DEBUG) DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
@@ -248,12 +246,12 @@ public class FileUtils {
private static String getLocalPath(final Context context, final Uri uri) {
if (DEBUG)
Log.d(TAG + " File -",
"Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() + ", "
+ "Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme"
+ ": " + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: "
+ uri.getPathSegments().toString());
// if (DEBUG)
// Log.d(TAG + " File -",
// "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() + ", "
// + "Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme"
// + ": " + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: "
// + uri.getPathSegments().toString());
// DocumentProvider
@@ -487,12 +485,12 @@ public class FileUtils {
}
private static void logDir(File dir) {
if (!DEBUG) return;
Log.d(TAG, "Dir=" + dir);
File[] files = dir.listFiles();
for (File file : files) {
Log.d(TAG, "File=" + file.getPath());
}
// if (!DEBUG) return;
// Log.d(TAG, "Dir=" + dir);
// File[] files = dir.listFiles();
// for (File file : files) {
// Log.d(TAG, "File=" + file.getPath());
// }
}
@Nullable