Compare commits

16 Commits
main ... dev

Author SHA1 Message Date
fengjignqi
4c11b3f9c3 去除WRITE_EXTERNAL_STORAGE权限 2026-08-15 11:01:14 +08:00
fengjignqi
577f49e032 更新版本 2026-08-15 10:53:36 +08:00
fengjignqi
a279230669 更新版本 2026-08-14 14:04:09 +08:00
fengjignqi
969338f3bd 优化 2026-08-14 08:50:25 +08:00
fengjignqi
c2dfa9145a 取消订单返回余额 2026-08-12 11:05:55 +08:00
fengjignqi
499673782c 接入微信支付,微信提现 2026-08-12 08:32:42 +08:00
fengjignqi
f3006f4255 接入微信支付 2026-08-10 15:41:43 +08:00
fengjignqi
579e9427c8 接入微信支付 2026-08-09 08:37:09 +08:00
fengjignqi
39dbe5f1e4 优化修改bug 2026-08-08 14:45:08 +08:00
fengjignqi
9806e66790 优化 2026-08-07 08:33:52 +08:00
fengjignqi
c6eeb1321c 优化 2026-08-05 15:00:34 +08:00
fengjignqi
9d432dba67 优化ai对话页面 2026-08-04 10:44:12 +08:00
fengjignqi
54dc0b5598 优化ai对话页面 2026-08-03 15:06:08 +08:00
fengjignqi
170936fd23 优化ai对话页面 2026-08-03 11:44:42 +08:00
fengjignqi
bc0c7adb41 优化ai对话页面,以及权限申请 2026-08-03 10:16:31 +08:00
fengjignqi
12fecfbe41 dev分支 2026-07-30 15:25:01 +08:00
129 changed files with 5051 additions and 3350 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,13 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-08-14T02:57:51.212958400Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=D122222104A9" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>

13
.idea/deviceManager.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View File

@@ -84,6 +84,8 @@ dependencies {
implementation libs.androidx.compose.material3
implementation libs.material
implementation libs.appcompat
implementation libs.luban
implementation 'io.noties.markwon:core:4.6.2'
testImplementation libs.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espresso.core
@@ -156,4 +158,7 @@ dependencies {
implementation 'com.github.yyued:SVGAPlayer-Android:2.6.1'
//圆形视图库
implementation 'com.makeramen:roundedimageview:2.3.0'
}

View File

@@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".CKApp"
android:allowBackup="true"
@@ -29,10 +32,10 @@
</activity>
<activity
android:name=".ui.act.welcome.WelcomeActivity"
android:theme="@style/Theme.CkCollar_app.NoActionBar"></activity>
android:theme="@style/Theme.CkCollar_app.NoActionBar"/>
<activity
android:name=".ui.act.welcome.PermisstionActivity"
android:theme="@style/Theme.CkCollar_app.NoActionBar"></activity>
android:theme="@style/Theme.CkCollar_app.NoActionBar"/>
<activity
android:name=".ui.act.login.LoginActivity"
android:launchMode="singleTask"
@@ -53,6 +56,9 @@
<activity
android:name=".ui.act.login.AgreementActivity"
android:theme="@style/Theme.CkCollar_app.NoActionBar"></activity>
<activity
android:name=".ui.act.setting.SettingWebViewActivity"
android:theme="@style/Theme.CkCollar_app.NoActionBar"></activity>
<activity
android:name=".ui.act.home.MainActivity"
android:launchMode="singleTask"
@@ -64,6 +70,12 @@
android:launchMode="singleTask"
android:taskAffinity="com.ck.ckcollar.app"
android:theme="@android:style/Theme.Translucent.NoTitleBar"></activity>
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity="com.ck.ckcollar.app"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name=".ui.act.petmanager.PetManagerActivity"
android:theme="@style/Theme.CkCollar_app.NoActionBar"></activity>
@@ -269,7 +281,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.ck.ckcollar.app.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
@@ -291,11 +303,10 @@
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />-->
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />-->
<!-- <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />-->
@@ -305,12 +316,12 @@
<!-- android:required="true" />-->
<!-- &lt;!&ndash; 必要的蓝牙权限 &ndash;&gt;-->
<!-- <uses-permission android:name="android.permission.BLUETOOTH" />-->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />-->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<!-- &lt;!&ndash; 从 Android 12 (API 31) 开始,需要新的蓝牙扫描/连接权限 &ndash;&gt;-->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />-->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />-->

View File

@@ -22,6 +22,8 @@ class CKApp : Application() {
var activitys = mutableListOf<TTBaseActivity>()
lateinit var token : String
var isUserLoggedIn: Boolean = false
private set
var wxJump = 0
var userInfo : UserInfoModel? = null
companion object {
@@ -33,7 +35,8 @@ class CKApp : Application() {
super.onCreate()
app = this
token = PreferencesUtils.getString(this, Const.TOKEN,"").toString()
token = PreferencesUtils.getString(this, Const.TOKEN, "").orEmpty()
isUserLoggedIn = token.isNotBlank()
var userData = PreferencesUtils.getString(this, Const.USER_DATA,"").toString()
if (!TextUtils.isEmpty(userData)){
userInfo = Gson().fromJson<UserInfoModel>(userData, UserInfoModel::class.java)
@@ -51,16 +54,31 @@ class CKApp : Application() {
Glide.init(this, GlideBuilder().setDefaultRequestOptions(defaultOptions))
//SkinManager.getInstance().init(this);
UMConfigure.setLogEnabled(true)
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.MANUAL);
UMConfigure.preInit(this, "6a618cf0e88ae439bf379f37", "UM");
UMConfigure.init(this, "6a618cf0e88ae439bf379f37", "UM", UMConfigure.DEVICE_TYPE_PHONE, "");
// UMConfigure.setLogEnabled(true)
// MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.MANUAL);
// UMConfigure.preInit(this, "6a618cf0e88ae439bf379f37", "UM");
// UMConfigure.init(this, "6a618cf0e88ae439bf379f37", "UM", UMConfigure.DEVICE_TYPE_PHONE, "");
TTLog.isLogEable(true)
}
fun setLoginToken(value: String) {
token = value
isUserLoggedIn = value.isNotBlank()
PreferencesUtils.putString(this, Const.TOKEN, value)
if (isUserLoggedIn) {
PreferencesUtils.putBoolean(this, Const.FIRST, false)
}
}
fun clearLoginState() {
token = ""
userInfo = null
isUserLoggedIn = false
PreferencesUtils.putString(this, Const.TOKEN, "")
PreferencesUtils.putString(this, Const.USER_DATA, "")
PreferencesUtils.putString(this, Const.PET_DATA, "")
}
}

View File

@@ -7,7 +7,10 @@ object ActionCode {
//const val BASE_URL = "https://ragged-parting-unrushed.ngrok-free.dev";
//const val BASE_URL = "https://ragged-parting-unrushed.ngrok-free.dev";
// const val BASE_URL = "https://ragged-parting-unrushed.ngrok-free.dev";
const val BASE_URL = "https://d.chongdunpet.com"
// const val BASE_URL = "http://192.168.1.187:8080"
const val CUSTOMER_URL = "wss://d.chongdunpet.com/ws/chat?role=customer&token=";
@@ -152,6 +155,7 @@ object ActionCode {
"找回支付密码" -> url = "/app/setting/payPassword/reset"
"查询支付密码状态" -> url = "/app/setting/payPassword/status"
"订单支付" -> url = "/app/order/pay"
"取消待支付订单" -> url = "/app/order/cancelPending"
"修改个人信息" -> url = "/app/profile/update"
@@ -175,6 +179,7 @@ object ActionCode {
"绑定微信" -> url = "/app/profile/wx/bind"
}
TTLog.e(url)
return url;

View File

@@ -34,4 +34,9 @@ object Config : TileSourceFactory() {
override fun getTileURLString(pMapTileIndex: Long) =
("$baseUrl&X=${MapTileIndex.getX(pMapTileIndex)}&Y=${MapTileIndex.getY(pMapTileIndex)}&L=${MapTileIndex.getZoom(pMapTileIndex)}")
}
//隐私政策
var PRIVACY_POLICY = "http://f.chongdunpet.com/privacy_policy.html#a6";
//三方SDK
var THIRD_PARTY_SDK = "http://h.chongdunpet.com/third_party_sdk.html";
}

View File

@@ -1,6 +1,7 @@
package com.ck.ckcollar.app.model
class BannarModel : CkBaseModel(){
var localImageRes: Int = 0
var bannerStatus: String? = ""
var bannerUrl: String? = ""
var createBy: String? = ""

View File

@@ -10,6 +10,14 @@ class MemberModel {
var amount: Double = 0.0 // 金额
var payType: String? = "" // 支付方式
var orderNo: String? = "" // 订单编号
var timeStamp: String? = null // 微信支付时间戳
var packageVal: String? = null // 微信支付扩展字段
var paySign: String? = null // 微信支付签名
var appId: String? = null // 微信 AppID
var partnerId: String? = null // 微信支付商户号
var prepayId: String? = null // 微信预支付订单号
var signType: String? = null // 签名类型
var nonceStr: String? = null // 随机字符串
var order: MemberOrder? = null // 订单信息
class MemberOrder {

View File

@@ -0,0 +1,13 @@
package com.ck.ckcollar.app.model
import com.google.gson.JsonElement
class OrderPaymentModel {
var orderId: Int = 0
var orderStr: JsonElement? = null
var wxMap: JsonElement? = null
var pointsAmount: Double = 0.0
var balanceAmount: Double = 0.0
var alipayAmount: Double = 0.0
var info: String? = ""
}

View File

@@ -4,5 +4,13 @@ class PayOrderModel : CkBaseModel(){
var orderStr: String? = "" // 订单字符串
var amount: String? = "" // 金额
var orderNo: String? = "" // 订单编号
var timeStamp: String? = null // 微信支付时间戳
var packageVal: String? = null // 微信支付扩展字段
var paySign: String? = null // 微信支付签名
var appId: String? = null // 微信 AppID
var partnerId: String? = null // 微信支付商户号
var prepayId: String? = null // 微信预支付订单号
var signType: String? = null // 签名类型
var nonceStr: String? = null // 随机字符串
var info : String? = ""
}

View File

@@ -1,7 +1,22 @@
package com.ck.ckcollar.app.model
class RechargeModel {
var orderStr :String? = null
var amount : Double? = null
// Alipay
var orderStr: String? = null
// Common
var amount: Double? = null
var orderNo: String? = null
// WeChat Pay
var timeStamp: String? = null
var packageVal: String? = null
var paySign: String? = null
var appId: String? = null
var partnerId: String? = null
var prepayId: String? = null
var signType: String? = null
var nonceStr: String? = null
var RC1782974779440000002 : String? = null
}

View File

@@ -1,11 +1,10 @@
package com.ck.ckcollar.app.model
class UpdateModel : CkBaseModel(){
//{"androidVersion":1,"content":"","iosVersion":1,"isForce":false}
var androidVersion : Int = 0
var iosVersion : Int = 0
var isForce : Boolean = false
var content : String? = ""
var content: String? = ""
var downloadUrl: String? = ""
var hasUpdate: Boolean = false
var isForce: Boolean = false
var versionCode: Int = 0
var versionName: String? = ""
}

View File

@@ -4,4 +4,10 @@ class WithDrawModel {
var amount :String? = ""
var payMethod :String? = ""
var authPackageInfo :String? = null
var thirdOrderNo :String? = null
var outBillNo :String? = null
var mchId :String? = null
var appId :String? = null
var needConfirm :Boolean = false
}

View File

@@ -29,7 +29,11 @@ import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks
class BlueToothActivity : CKBaseActivity() , PermissionCallbacks{
lateinit var activityBluetoothBinding: ActivityBluetoothBinding
val permissions = listOf(Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN,Manifest.permission.BLUETOOTH_SCAN,Manifest.permission.BLUETOOTH_CONNECT)
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
listOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT)
} else {
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
}
lateinit var blueBoothAdapter: BlueBoothAdapter
var devices = mutableListOf<String>()
@@ -185,7 +189,17 @@ class BlueToothActivity : CKBaseActivity() , PermissionCallbacks{
requestCode: Int,
perms: List<String?>
) {
val deniedPermissions = perms.filterNotNull().toTypedArray()
val permanentlyDenied = deniedPermissions.any {
!ActivityCompat.shouldShowRequestPermissionRationale(this, it)
}
if (permanentlyDenied) {
super.onPermissionsDenied(requestCode, deniedPermissions, true)
}
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
initData()
}
class BlueBoothAdapter(var context: CKBaseActivity, var scanResults: List<String>) : RecyclerView.Adapter<BlueBoothAdapter.BlueBoothHolder>() {

View File

@@ -143,7 +143,17 @@ class WifiListActivity : CKBaseActivity(), PermissionCallbacks {
requestCode: Int,
perms: List<String?>
) {
val deniedPermissions = perms.filterNotNull().toTypedArray()
val permanentlyDenied = deniedPermissions.any {
!ActivityCompat.shouldShowRequestPermissionRationale(this, it)
}
if (permanentlyDenied) {
super.onPermissionsDenied(requestCode, deniedPermissions, true)
}
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
initData()
}
class WifiAdapter(var context: CKBaseActivity, var scanResults: List<ScanResult>) : RecyclerView.Adapter<WifiAdapter.WifiiewHoldr>() {

View File

@@ -39,6 +39,8 @@ import java.text.SimpleDateFormat
import java.util.Date
class BalanceActivity : CKLHBaseActionBarActivity() , View.OnClickListener{
override val shouldRequestPermissionsOnCreate: Boolean = false
lateinit var binding: ActivityBalanceBinding
var data = listOf<RecordModel>()
lateinit var adapter: RecordAdapter
@@ -116,9 +118,16 @@ class BalanceActivity : CKLHBaseActionBarActivity() , View.OnClickListener{
override fun onStart() {
super.onStart()
getBalanceInfo()
getList()
showProgeassDialog()
var pendingRequests = 2
val onRequestFinished = {
pendingRequests--
if (pendingRequests == 0) {
hiddenProgeassDialog()
}
}
getBalanceInfo(onRequestFinished)
getList(onRequestFinished)
}
override fun getResult(data: DataCollection, resultCode: Int) {
if (resultCode == 1001){
@@ -127,22 +136,24 @@ class BalanceActivity : CKLHBaseActionBarActivity() , View.OnClickListener{
getList()
}
}
fun getBalanceInfo(){
fun getBalanceInfo(onFinished: (() -> Unit)? = null){
TTHttpClient.get(this,"余额", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
model = Gson().fromJson<BalanceModle>(data, BalanceModle::class.java)
binding.textBalance.text = model?.balance.toString()
onFinished?.invoke()
}
override fun onFailed(code: String, msg: String) {
onFinished?.invoke()
toast(msg)
}
})
}
fun getList(){
fun getList(onFinished: (() -> Unit)? = null){
TTHttpClient.get(this,"余额明细列表", TTRequestParam().apply { add(TTRequestData("type",type))
add(TTRequestData("month",""))},object : ICallback{
override fun onSuccess(data: String) {
@@ -150,9 +161,11 @@ class BalanceActivity : CKLHBaseActionBarActivity() , View.OnClickListener{
adapter.data = this@BalanceActivity.data
adapter.notifyDataSetChanged()
onFinished?.invoke()
}
override fun onFailed(code: String, msg: String) {
onFinished?.invoke()
toast(msg)
}
})

View File

@@ -1,9 +1,12 @@
package com.ck.ckcollar.app.ui.act.balance
import android.content.pm.ApplicationInfo
import android.os.Bundle
import android.text.TextUtils
import com.alipay.sdk.app.EnvUtils
import com.alipay.sdk.app.PayTask
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.databinding.ActivityRechargeBinding
import com.ck.ckcollar.app.model.PayResult
import com.ck.ckcollar.app.model.RechargeModel
@@ -14,7 +17,10 @@ import com.ck.ckcollar.app.utils.networkone.model.TTRequestData
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.ck.ckcollar.app.widget.CKEditView
import com.google.gson.Gson
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.LiveDataBus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -23,7 +29,7 @@ import kotlinx.coroutines.withContext
class RechargeActivity : CKBaseActivity() {
lateinit var binding: ActivityRechargeBinding
var payType = "1"
var payType = "0"
var model : RechargeModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityRechargeBinding.inflate(layoutInflater)
@@ -35,6 +41,17 @@ class RechargeActivity : CKBaseActivity() {
override fun initView() {
LiveDataBus.get().with(WX_PAY_RESULT, Int::class.java).observe(this) { result ->
when (result) {
0 -> {
toast(getString(R.string.recharge_success))
finish(DataCollection(), 1001)
}
-2 -> toast("已取消微信支付")
else -> toast(getString(R.string.recharge_faild))
}
}
binding.btnAli.setOnClickListener {
binding.image1.setImageResource(R.mipmap.image_address_check_select)
binding.image2.setImageResource(R.mipmap.image_address_check_narmal)
@@ -69,6 +86,8 @@ class RechargeActivity : CKBaseActivity() {
if ("1".equals(payType)){
aliPay()
}else if("0".equals(payType)){
wxPay()
}
}
@@ -87,7 +106,10 @@ class RechargeActivity : CKBaseActivity() {
fun aliPay(){
GlobalScope.launch(Dispatchers.IO){
// [支付宝沙箱测试] 仅 Debug 包启用Release 包继续使用正式环境。
if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
// EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX)
}
val alipay = PayTask(this@RechargeActivity)
val result = alipay.payV2(model?.orderStr, true)
@@ -107,4 +129,54 @@ class RechargeActivity : CKBaseActivity() {
}
}
private fun wxPay() {
val payModel = model ?: return
val requiredValues = listOf(
payModel.appId,
payModel.partnerId,
payModel.prepayId,
payModel.packageVal,
payModel.nonceStr,
payModel.timeStamp,
payModel.paySign
)
if (requiredValues.any { it.isNullOrBlank() }) {
hiddenProgeassDialog()
toast("微信支付参数不完整,请检查商户号等支付参数")
return
}
if (payModel.appId != Const.WX_APP_ID) {
hiddenProgeassDialog()
toast("微信支付 AppID 不匹配")
return
}
val api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, true)
api.registerApp(Const.WX_APP_ID)
if (!api.isWXAppInstalled) {
hiddenProgeassDialog()
toast("请先安装微信")
return
}
val request = PayReq().apply {
appId = payModel.appId
partnerId = payModel.partnerId
prepayId = payModel.prepayId
packageValue = payModel.packageVal
nonceStr = payModel.nonceStr
timeStamp = payModel.timeStamp
sign = payModel.paySign
extData = payModel.orderNo
}
hiddenProgeassDialog()
if (!api.sendReq(request)) {
toast("微信支付拉起失败")
}
}
companion object {
const val WX_PAY_RESULT = "wx_pay_result"
}
}

View File

@@ -20,13 +20,19 @@ import com.google.gson.Gson
import com.gyf.immersionbar.ImmersionBar
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.LiveDataBus
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessView
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.ck.ckcollar.app.utils.Const
import java.net.URLEncoder
import java.math.BigDecimal
class WithDrawActivity : CKLHBaseActionBarActivity() {
lateinit var binding: ActivityWithdrawBinding
var model : BalanceModle? = null
private var pendingWithdraw: WithDrawModel? = null
var payType = "1"
var payType = "0"
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityWithdrawBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -39,6 +45,14 @@ class WithDrawActivity : CKLHBaseActionBarActivity() {
}
override fun initView() {
LiveDataBus.get().with(WX_WITHDRAW_RESULT, Int::class.java).observe(this) { resultCode ->
val withdraw = pendingWithdraw ?: return@observe
if (resultCode == 0) {
showWithdrawSuccess(withdraw)
} else {
toast(if (resultCode == -2) "已取消微信收款确认" else "微信提现确认失败")
}
}
binding.btnAli.setOnClickListener {
binding.image1.setImageResource(R.mipmap.image_address_check_select)
@@ -70,29 +84,34 @@ class WithDrawActivity : CKLHBaseActionBarActivity() {
toast(getString(R.string.no_balance_data))
return
}
if (TextUtils.isEmpty(binding.editAmount.text.toString())){
val amountText = binding.editAmount.text.toString()
if (TextUtils.isEmpty(amountText)){
toast(getString(R.string.withdraw_amount_not_empty))
return
}
if (binding.editAmount.text.toString().toDouble() > model!!.balance){
val amount = amountText.toBigDecimalOrNull()
if (amount == null || amount < BigDecimal("0.1")) {
toast(getString(R.string.withdraw_amount_minimum))
return
}
if (amount > BigDecimal.valueOf(model!!.balance)){
toast(getString(R.string.withdraw_amount_alert))
return
}
showProgeassDialog(getString(R.string.withdraw))
TTHttpClient.postJson(this, "提现", TTRequestParam().apply {
add(TTRequestData("amount", binding.editAmount.text.toString()))
add(TTRequestData("amount", amountText))
add(TTRequestData("payType", payType))
}, object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<WithDrawModel>(data, WithDrawModel::class.java)
toActivity(WithDrawSuccessActivity::class.java, DataCollection().apply {
add(Data("data", Gson().toJson(model)))
})
finish()
val withdraw = Gson().fromJson(data, WithDrawModel::class.java)
pendingWithdraw = withdraw
if (withdraw.needConfirm && "0" == payType) {
openWeChatTransferConfirm(withdraw)
} else {
showWithdrawSuccess(withdraw)
}
}
@@ -104,6 +123,52 @@ class WithDrawActivity : CKLHBaseActionBarActivity() {
}
private fun openWeChatTransferConfirm(withdraw: WithDrawModel) {
val mchId = withdraw.mchId
val appId = withdraw.appId
val packageInfo = withdraw.authPackageInfo
if (mchId.isNullOrBlank() || appId.isNullOrBlank() || packageInfo.isNullOrBlank()) {
toast("微信收款确认参数不完整")
return
}
if (appId != Const.WX_APP_ID) {
toast("微信支付 AppID 不匹配")
return
}
val api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, true)
api.registerApp(Const.WX_APP_ID)
if (!api.isWXAppInstalled) {
toast("请先安装微信")
return
}
if (api.wxAppSupportAPI < 0x28002d33) {
toast("当前微信版本过低,请升级微信")
return
}
val request = WXOpenBusinessView.Req().apply {
businessType = "requestMerchantTransfer"
query = "mchId=${URLEncoder.encode(mchId, "UTF-8")}" +
"&appId=${URLEncoder.encode(appId, "UTF-8")}" +
"&package=${URLEncoder.encode(packageInfo, "UTF-8")}"
}
if (!api.sendReq(request)) {
toast("微信收款确认页拉起失败")
}
}
private fun showWithdrawSuccess(withdraw: WithDrawModel) {
toActivity(WithDrawSuccessActivity::class.java, DataCollection().apply {
add(Data("data", Gson().toJson(withdraw)))
})
finish()
}
companion object {
const val WX_WITHDRAW_RESULT = "wx_withdraw_result"
}
fun getBalanceInfo(){
TTHttpClient.get(this,"余额", TTRequestParam(),object : ICallback{

View File

@@ -0,0 +1,69 @@
package com.ck.ckcollar.app.ui.act.customer
import android.content.Context
import android.graphics.drawable.Drawable
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.ck.ckcollar.app.R
object ChatImageLoader {
fun load(context: Context, imageView: ImageView, imageUrl: String) {
imageView.tag = imageUrl
resetSize(imageView)
Glide.with(context)
.load(imageUrl)
.placeholder(R.mipmap.image_default)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean = false
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
if (imageView.tag == imageUrl) {
setSize(imageView, resource.intrinsicWidth, resource.intrinsicHeight)
}
return false
}
})
.into(imageView)
}
private fun resetSize(imageView: ImageView) {
val density = imageView.resources.displayMetrics.density
imageView.layoutParams = imageView.layoutParams.apply {
width = maxWidth(imageView, density)
height = (200 * density).toInt()
}
}
private fun setSize(imageView: ImageView, sourceWidth: Int, sourceHeight: Int) {
if (sourceWidth <= 0 || sourceHeight <= 0) return
val density = imageView.resources.displayMetrics.density
val maxWidth = maxWidth(imageView, density).toFloat()
val maxHeight = 200 * density
val scale = minOf(1f, maxWidth / sourceWidth, maxHeight / sourceHeight)
imageView.layoutParams = imageView.layoutParams.apply {
width = (sourceWidth * scale).toInt().coerceAtLeast(1)
height = (sourceHeight * scale).toInt().coerceAtLeast(1)
}
}
private fun maxWidth(imageView: ImageView, density: Float): Int {
val requestedWidth = (200 * density).toInt()
val availableWidth = imageView.resources.displayMetrics.widthPixels - (155 * density).toInt()
return minOf(requestedWidth, availableWidth.coerceAtLeast(1))
}
}

View File

@@ -1,7 +1,9 @@
package com.ck.ckcollar.app.ui.act.customer
import android.Manifest
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -10,21 +12,23 @@ import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.text.Editable
import android.text.Spannable
import android.text.SpannableString
import android.text.TextUtils
import android.text.TextWatcher
import android.text.style.ImageSpan
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.action.ActionCode
@@ -40,6 +44,7 @@ import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -53,9 +58,49 @@ import com.tt.kit.model.DataCollection
import com.tt.kit.utils.KeyboardUtils
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.TTUtils
import io.noties.markwon.Markwon
import java.io.File
import java.util.UUID
import kotlin.math.min
private object CustomerMarkdownDetector {
private val blockMarkdown = Regex(
"(?m)^(?:\\s{0,3}(?:#{1,6}\\s+|[-*+]\\s+|>\\s+|\\d+\\.\\s+|```|~~~|(?:-{3,}|_{3,}|\\*{3,})\\s*$))"
)
private val inlineMarkdown = Regex("(?:\\*\\*[^\\n]+?\\*\\*|`[^\\n]+?`|\\[[^\\]]+?\\]\\([^\\s)]+?\\))")
fun isMarkdown(content: String): Boolean = content.isNotBlank() &&
(blockMarkdown.containsMatchIn(content) || inlineMarkdown.containsMatchIn(content))
fun normalize(content: String): String {
val normalized = content
// SSE chunks contain JSON-escaped line breaks; Markwon otherwise renders
// "\\n\\n" as "nn" and keeps the first list item inside the heading.
.replace("\\r\\n", "\n")
.replace("\\n", "\n")
// AI responses commonly omit the required spaces after block markers.
.replace(Regex("(?m)^(\\s{0,3}#{1,6})(?=\\S)"), "$1 ")
.replace(Regex("(?m)^(\\s{0,3}>)(?=\\S)"), "$1 ")
.replace(Regex("(?m)^(\\s{0,3}[-+])(?!\\s)(?=\\S)"), "$1 ")
.replace(Regex("(?m)^(\\s{0,3}\\*)(?![\\s*])(?=\\S)"), "$1 ")
.replace(Regex("(?m)^(\\s{0,3}\\d+\\.)(?=\\S)"), "$1 ")
// Split compact numbered sections such as "。2.**标题**" into list rows.
.replace(Regex("(\\d+)\\.\\s*(?=\\*\\*)"), "$1. ")
.replace(Regex("([^\\n])(?=\\d+\\. \\*\\*)")) { match ->
match.value + "\n"
}
.replace(Regex("([^\\n])(?=免责声明[:])")) { match ->
match.value + "\n\n"
}
// TextView has no CSS-like heading margins, so preserve a blank line
// after Markdown headings for readable streamed responses.
return normalized.replace(
Regex("(?m)^(\\s{0,3}#{1,6}\\s+[^\\n]+)\\n(?!\\n)")
) { match ->
match.groupValues[1] + "\n\n"
}
}
}
class CustomerActivity : CKBaseActivity() {
@@ -64,6 +109,8 @@ class CustomerActivity : CKBaseActivity() {
var data = mutableListOf<CustomerMessageModel.MessageItemModel>()
var path = ""
private var isSending = false
private var isAiResponding = false
var url = ""
var lock = true
@@ -71,7 +118,6 @@ class CustomerActivity : CKBaseActivity() {
binding = ActivityCustomerBinding.inflate(layoutInflater)
setContentView(binding.root)
super.onCreate(savedInstanceState)
checkAppPermissions()
setTitleText(getString(R.string.customer_service4))
@@ -94,6 +140,28 @@ class CustomerActivity : CKBaseActivity() {
binding.recyclerView.adapter = customerMessageAdapter
binding.editMessage.setHorizontallyScrolling(false);
binding.btnSend.setOnClickListener {
if (isAiResponding) {
toast("请等待当前问题回答结束")
return@setOnClickListener
}
isSending = true
// Reset the action controls immediately. The send flow captures the
// submitted text and image path before clearing the visible draft.
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
binding.editMessage.onEditorAction(EditorInfo.IME_ACTION_SEND)
}
updateSendControls()
// Use the traditional multiline keyboard action; sending is handled by the
// activity's own message flow rather than the IME action button.
binding.editMessage.setSingleLine(false)
binding.editMessage.imeOptions = EditorInfo.IME_ACTION_NONE
binding.editMessage.setRawInputType(android.text.InputType.TYPE_CLASS_TEXT or android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE)
// setSingleLine(false) resets the XML maxLines value, so apply it last.
binding.editMessage.maxLines = 4
binding.editMessage.maxHeight = (70 * resources.displayMetrics.density).toInt()
binding.editMessage.isVerticalScrollBarEnabled = true
binding.editMessage.setOnEditorActionListener(object : TextView.OnEditorActionListener {
override fun onEditorAction(
@@ -101,21 +169,21 @@ class CustomerActivity : CKBaseActivity() {
actionId: Int,
ketEvent: KeyEvent?
): Boolean {
if (actionId == EditorInfo.IME_ACTION_UNSPECIFIED ||
actionId == EditorInfo.IME_ACTION_SEND ||
(ketEvent != null && ketEvent.keyCode == KeyEvent.KEYCODE_ENTER)
) {
if (actionId == EditorInfo.IME_ACTION_SEND) {
if (lock) {
var text = binding.editMessage.text.toString()
var index = text.indexOf("$$$$$") >= 0
if (!index) {
path = ""
}
text = text.replace("$$$$$", "")
if (!TextUtils.isEmpty(text) || !TextUtils.isEmpty(path)) {
// Whitespace-only input is not a message. A selected image may still be sent alone.
val text = binding.editMessage.text.toString().trim()
if (text.isNotEmpty() || path.isNotEmpty()) {
lock = false
KeyboardUtils.hideKeyboard(this@CustomerActivity)
binding.editMessage.clearFocus()
handler.post {
sendMessage(UUID.randomUUID().toString(),text,false)
if (!TextUtils.isEmpty(path)) {
uploadImage(text, path)
} else {
sendMessage(UUID.randomUUID().toString(), text, false, "")
}
clearInputViews()
}
}
}
@@ -153,31 +221,19 @@ class CustomerActivity : CKBaseActivity() {
p2: Int,
p3: Int
) {
try {
var text = binding.editMessage.text.toString()
var index = text.indexOf("$$$$$") >= 0
// if (!index) {
// path = ""
// }
val lineCount: Int? = binding.editMessage.getLineCount()
val lineHeight: Int = binding.editMessage.getLineHeight()
val maxHeight: Int = lineCount!! * lineHeight
binding.editMessage.setHeight(min(maxHeight, maxHeight))
}catch (e : Exception){
e.toString()
}
if (p0?.toString()?.trim()?.isNotEmpty() == true) isSending = false
updateSendControls()
}
})
binding.btnMore.setOnClickListener {
if (binding.contentBottom.visibility == View.GONE) {
KeyboardUtils.hideKeyboard(this@CustomerActivity)
val inputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0)
binding.editMessage.clearFocus()
binding.contentBottom.visibility = View.VISIBLE
} else {
binding.contentBottom.visibility = View.GONE
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
}
}
binding.btnSmile.setOnClickListener {
@@ -185,25 +241,35 @@ class CustomerActivity : CKBaseActivity() {
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
}
binding.btnDeleteImage.setOnClickListener {
clearSelectedImage()
}
//拍照
binding.conetnt1.setOnClickListener {
if (TextUtils.isEmpty(path)) {
takePhoto()
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
} else {
toast(getString(R.string.only_one_photo))
}
}
//相册
binding.conetnt2.setOnClickListener {
if (TextUtils.isEmpty(path)) {
//相册
val i = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
getPermissionRequst(
permission,
REQUEST_PHOTO_PERMISSION
)
i.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
startActivityForResult(i, 56)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -223,7 +289,22 @@ class CustomerActivity : CKBaseActivity() {
}
}
handleMessage()
binding.btnSmile.visibility = View.GONE;
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> takePhoto()
REQUEST_PHOTO_PERMISSION -> {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, 56)
}
}
}
override fun initData() {
@@ -270,7 +351,7 @@ class CustomerActivity : CKBaseActivity() {
if (!TextUtils.isEmpty(sendConetnt) || !TextUtils.isEmpty(url)){
handler.post {
sendMessage2(UUID.randomUUID().toString(),sendConetnt,true)
sendMessage2(UUID.randomUUID().toString(),sendConetnt,true, url)
}
}
}
@@ -297,15 +378,18 @@ class CustomerActivity : CKBaseActivity() {
}).show()
}
fun sendMessage(uuid :String ,content: String,isResend : Boolean) {
fun sendMessage(uuid :String ,content: String,isResend : Boolean, imageUrl: String) {
if (!TextUtils.isEmpty(url)) {
isAiResponding = true
val rawStreamResponse = StringBuilder()
if (!TextUtils.isEmpty(imageUrl)) {
this@CustomerActivity.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = CKApp.app.userInfo?.id!!
this.content = content
this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = CKApp.app.userInfo?.custName ?: ""
this.url = this@CustomerActivity.url
this.url = imageUrl
})
}
if (!TextUtils.isEmpty(content) && !isResend) {
@@ -325,13 +409,13 @@ class CustomerActivity : CKBaseActivity() {
TTHttpClient.postJsonStream3(this, "发送会话消息stream", TTRequestParam().apply {
add(TTRequestData("imageUrl", url))
add(TTRequestData("imageUrl", imageUrl))
add(TTRequestData("question", content))
}, object : ICallback {
override fun onSuccess(data: String) {
lock = true
rawStreamResponse.append(data)
// var model = Gson().fromJson<AIMessageModel>(data, AIMessageModel::class.java)
@@ -354,9 +438,6 @@ class CustomerActivity : CKBaseActivity() {
}
binding.editMessage.setText("")
path = ""
url = ""
customerMessageAdapter.data = this@CustomerActivity.data
customerMessageAdapter.notifyDataSetChanged()
@@ -376,13 +457,19 @@ class CustomerActivity : CKBaseActivity() {
}
override fun onFailed(code: String, msg: String) {
isAiResponding = false
lock = true
TTLog.e("AI_STREAM_FULL[$uuid][FAILED]: $rawStreamResponse")
// toast(msg)
//TTLog.e(msg)
}
})
binding.editMessage.setText("")
override fun onComplete() {
isAiResponding = false
lock = true
TTLog.e("AI_STREAM_FULL[$uuid]: $rawStreamResponse")
}
})
customerMessageAdapter.data = this@CustomerActivity.data
customerMessageAdapter.notifyDataSetChanged()
@@ -393,7 +480,9 @@ class CustomerActivity : CKBaseActivity() {
fun sendMessage2(uuid :String ,content: String,isResend : Boolean) {
fun sendMessage2(uuid :String ,content: String,isResend : Boolean, imageUrl: String) {
isAiResponding = true
val rawStreamResponse = StringBuilder()
lock = false
if (!TextUtils.isEmpty(content) && !isResend) {
this@CustomerActivity.data.add(CustomerMessageModel.MessageItemModel().apply {
@@ -412,13 +501,13 @@ class CustomerActivity : CKBaseActivity() {
TTHttpClient.postJsonStream3(this, "发送会话消息stream", TTRequestParam().apply {
add(TTRequestData("imageUrl", url))
add(TTRequestData("imageUrl", imageUrl))
add(TTRequestData("question", content))
}, object : ICallback {
override fun onSuccess(data: String) {
lock = true
rawStreamResponse.append(data)
// var model = Gson().fromJson<AIMessageModel>(data, AIMessageModel::class.java)
@@ -441,9 +530,6 @@ class CustomerActivity : CKBaseActivity() {
}
binding.editMessage.setText("")
path = ""
url = ""
customerMessageAdapter.data = this@CustomerActivity.data
customerMessageAdapter.notifyDataSetChanged()
@@ -463,13 +549,19 @@ class CustomerActivity : CKBaseActivity() {
}
override fun onFailed(code: String, msg: String) {
isAiResponding = false
lock = true
TTLog.e("AI_STREAM_FULL[$uuid][FAILED]: $rawStreamResponse")
// toast(msg)
//TTLog.e(msg)
}
})
binding.editMessage.setText("")
override fun onComplete() {
isAiResponding = false
lock = true
TTLog.e("AI_STREAM_FULL[$uuid]: $rawStreamResponse")
}
})
customerMessageAdapter.data = this@CustomerActivity.data
customerMessageAdapter.notifyDataSetChanged()
@@ -479,10 +571,10 @@ class CustomerActivity : CKBaseActivity() {
}
fun uploadImage() {
fun uploadImage(content: String, imagePath: String) {
var param = TTRequestParam()
param.files.add(File(path))
param.files.add(File(imagePath))
showProgeassDialog()
TTHttpClient.postFile(this, "上传照片s", param, object : ICallback {
@@ -490,33 +582,25 @@ class CustomerActivity : CKBaseActivity() {
hiddenProgeassDialog()
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
url = model.urls
handler.post {
sendMessage(UUID.randomUUID().toString(), content, false, model.urls)
}
}
override fun onFailed(code: String, msg: String) {
hiddenProgeassDialog()
lock = true
if (path.isEmpty() && binding.editMessage.text.toString().isEmpty()) {
path = imagePath
showImagePreview(imagePath)
binding.editMessage.setText(content)
}
toast(msg)
}
})
}
fun addImage(path: String) {
try {
var bitmap = TTUtils.getBitmap(path, 300, 300)
var imageSpan = ImageSpan(bitmap!!);
var spannableString = SpannableString("$$$$$");
spannableString.setSpan(
imageSpan, 0, 5,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
binding.editMessage.text?.insert(0, spannableString);
} catch (e: Exception) {
e.printStackTrace();
}
}
fun getList() {
TTHttpClient.get(this, "会话消息列表", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
@@ -546,17 +630,89 @@ class CustomerActivity : CKBaseActivity() {
if (requestCode == 55 && resultCode == RESULT_OK) {
path = PreferencesUtils.getString(this@CustomerActivity, Const.IMAGE_PATH, "")!!
addImage(path!!)
uploadImage()
prepareSelectedImage()
} else if (requestCode == 56 && resultCode == RESULT_OK) {
path = TTUtils.getImageUrl(this, data!!.getData());
addImage(path)
uploadImage()
prepareSelectedImage()
}
}
private fun prepareSelectedImage() {
if (TextUtils.isEmpty(path)) return
LubanCompressUtil.getCompressFile(
this,
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (isFinishing || isDestroyed) return
path = file.absolutePath
lock = true
binding.contentBottom.visibility = View.GONE
showImagePreview(path)
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
// Rebind the IME after selecting an image so its action button is
// available even though the text field itself is empty.
binding.editMessage.postDelayed({
val imm = getSystemService(INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
imm?.restartInput(binding.editMessage)
}, 150L)
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
lock = true
path = ""
toast(failMsg)
}
}
)
}
private fun showImagePreview(imagePath: String) {
isSending = false
binding.contentImagePreview.visibility = View.VISIBLE
updateSendControls()
binding.imagePreview.scaleType = android.widget.ImageView.ScaleType.CENTER_CROP
Glide.with(this)
.load(imagePath)
.into(binding.imagePreview)
}
private fun clearSelectedImage() {
path = ""
url = ""
binding.imagePreview.setImageDrawable(null)
binding.contentImagePreview.visibility = View.GONE
updateSendControls()
}
private fun clearInputViews() {
binding.editMessage.setText("")
path = ""
url = ""
binding.imagePreview.setImageDrawable(null)
binding.contentImagePreview.visibility = View.GONE
// Sending has already been initiated; keep the controls in their idle state
// until the user starts composing the next draft.
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
}
private fun updateSendControls() {
val hasImage = path.isNotEmpty()
val hasText = binding.editMessage.text.toString().trim().isNotEmpty()
if (isSending) {
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
return
}
binding.btnSend.visibility = if (hasImage || hasText) View.VISIBLE else View.GONE
binding.btnMore.visibility = if (hasImage) View.GONE else View.VISIBLE
}
override fun onDestroy() {
super.onDestroy()
@@ -602,6 +758,8 @@ class CustomerActivity : CKBaseActivity() {
var data: List<CustomerMessageModel.MessageItemModel>,
var onItemClickListenner: OnItemClickListenner
) : RecyclerView.Adapter<CustomerMessageAdapter.CustomerMessageHolder>() {
private val markwon = Markwon.create(context)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
@@ -626,7 +784,16 @@ class CustomerActivity : CKBaseActivity() {
holder.binding.contentTo.visibility = View.GONE
holder.binding.contentFrom.visibility = View.VISIBLE
holder.binding.textFrom.text = model.content
val content = model.content.orEmpty()
val normalizedContent = CustomerMarkdownDetector.normalize(content)
if (CustomerMarkdownDetector.isMarkdown(normalizedContent)) {
markwon.setMarkdown(
holder.binding.textFrom,
normalizedContent
)
} else {
holder.binding.textFrom.text = content
}
holder.binding.textFromNick.text = model.senderName
Glide.with(context)
@@ -658,14 +825,42 @@ class CustomerActivity : CKBaseActivity() {
if (!TextUtils.isEmpty(model.url)) {
holder.binding.textTo.visibility = View.GONE
holder.binding.imageContentTo.visibility = View.VISIBLE
val imageView = holder.binding.imageContentTo
val imageUrl = model.url!!
imageView.setTag(imageUrl)
resetImageContentSize(imageView)
Glide.with(context)
.load( model.url)
.load(imageUrl)
.placeholder(R.mipmap.image_default)
.into(holder.binding.imageContentTo)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean = false
holder.binding.imageContentTo.setTag(model.url)
holder.binding.imageContentTo.setOnClickListener(imageClick)
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
if (imageView.tag == imageUrl) {
setImageContentSize(
imageView,
resource.intrinsicWidth,
resource.intrinsicHeight
)
}
return false
}
})
.into(imageView)
imageView.setOnClickListener(imageClick)
} else {
holder.binding.textTo.visibility = View.VISIBLE
holder.binding.imageContentTo.visibility = View.GONE
@@ -680,6 +875,39 @@ class CustomerActivity : CKBaseActivity() {
}
}
private fun resetImageContentSize(imageView: View) {
val density = imageView.resources.displayMetrics.density
imageView.layoutParams = imageView.layoutParams.apply {
width = getMaxImageWidth(density)
height = (200 * density).toInt()
}
}
private fun setImageContentSize(imageView: View, sourceWidth: Int, sourceHeight: Int) {
if (sourceWidth <= 0 || sourceHeight <= 0) return
val density = imageView.resources.displayMetrics.density
val maxWidth = getMaxImageWidth(density).toFloat()
val maxHeight = (200 * density).toFloat()
val scale = minOf(
1f,
maxWidth / sourceWidth.toFloat(),
maxHeight / sourceHeight.toFloat()
)
imageView.layoutParams = imageView.layoutParams.apply {
width = (sourceWidth * scale).toInt().coerceAtLeast(1)
height = (sourceHeight * scale).toInt().coerceAtLeast(1)
}
}
private fun getMaxImageWidth(density: Float): Int {
val requestedWidth = (200 * density).toInt()
// Reserve the avatar, paddings, and the bubble's left inset.
val availableWidth = context.resources.displayMetrics.widthPixels - (155 * density).toInt()
return minOf(requestedWidth, availableWidth.coerceAtLeast(1))
}
override fun getItemCount(): Int {
return data.size
}
@@ -715,4 +943,9 @@ class CustomerActivity : CKBaseActivity() {
}
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1001
private const val REQUEST_PHOTO_PERMISSION = 1002
}
}

View File

@@ -0,0 +1,891 @@
package com.ck.ckcollar.app.ui.act.customer
import android.Manifest
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.provider.MediaStore
import android.text.Editable
import android.text.InputType
import android.text.TextUtils
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.action.ActionCode
import com.ck.ckcollar.app.databinding.ActivityCustomerBinding
import com.ck.ckcollar.app.databinding.LayoutItemAiCustomerMessageBinding
import com.ck.ckcollar.app.databinding.LayoutItemCustomerMessageBinding
import com.ck.ckcollar.app.listenner.OnItemClickListenner
import com.ck.ckcollar.app.model.AIMessageModel
import com.ck.ckcollar.app.model.CustomerMessageModel
import com.ck.ckcollar.app.model.UploadModel
import com.ck.ckcollar.app.ui.act.petmanager.ImageViewActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.cyclone.shadowsocks.utils.PreferencesUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.KeyboardUtils
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.TTUtils
import java.io.File
import java.util.UUID
class CustomerActivity_copy : CKBaseActivity() {
lateinit var binding: ActivityCustomerBinding
lateinit var customerMessageAdapter: CustomerMessageAdapter
var data = mutableListOf<CustomerMessageModel.MessageItemModel>()
var path = ""
private var isSending = false
private var isAiResponding = false
var url = ""
var lock = true
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityCustomerBinding.inflate(layoutInflater)
setContentView(binding.root)
super.onCreate(savedInstanceState)
setTitleText(getString(R.string.customer_service4))
setRightText(getString(R.string.clear))
}
override fun initView() {
binding.recyclerView.layoutManager =
LinearLayoutManager(this@CustomerActivity_copy, RecyclerView.VERTICAL, false)
binding.recyclerView.setHasFixedSize(true)
customerMessageAdapter =
CustomerMessageAdapter(this@CustomerActivity_copy, data, object : OnItemClickListenner {
override fun onClick(
pos: Int,
model: Any
) {
}
})
binding.recyclerView.adapter = customerMessageAdapter
binding.editMessage.setHorizontallyScrolling(false);
binding.btnSend.setOnClickListener {
if (isAiResponding) {
toast("请等待当前问题回答结束")
return@setOnClickListener
}
isSending = true
// Reset the action controls immediately. The send flow captures the
// submitted text and image path before clearing the visible draft.
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
binding.editMessage.onEditorAction(EditorInfo.IME_ACTION_SEND)
}
updateSendControls()
// Use the traditional multiline keyboard action; sending is handled by the
// activity's own message flow rather than the IME action button.
binding.editMessage.setSingleLine(false)
binding.editMessage.imeOptions = EditorInfo.IME_ACTION_NONE
binding.editMessage.setRawInputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE)
// setSingleLine(false) resets the XML maxLines value, so apply it last.
binding.editMessage.maxLines = 4
binding.editMessage.maxHeight = (70 * resources.displayMetrics.density).toInt()
binding.editMessage.isVerticalScrollBarEnabled = true
binding.editMessage.setOnEditorActionListener(object : TextView.OnEditorActionListener {
override fun onEditorAction(
p0: TextView?,
actionId: Int,
ketEvent: KeyEvent?
): Boolean {
if (actionId == EditorInfo.IME_ACTION_SEND) {
if (lock) {
// Whitespace-only input is not a message. A selected image may still be sent alone.
val text = binding.editMessage.text.toString().trim()
if (text.isNotEmpty() || path.isNotEmpty()) {
lock = false
KeyboardUtils.hideKeyboard(this@CustomerActivity_copy)
binding.editMessage.clearFocus()
handler.post {
if (!TextUtils.isEmpty(path)) {
uploadImage(text, path)
} else {
sendMessage(UUID.randomUUID().toString(), text, false, "")
}
clearInputViews()
}
}
}
}
return false
}
})
binding.editMessage.setOnClickListener {
binding.contentBottom.visibility = View.GONE
}
binding.editMessage.onFocusChangeListener = object : View.OnFocusChangeListener{
override fun onFocusChange(p0: View?, p1: Boolean) {
if (p1){
binding.contentBottom.visibility = View.GONE
}
}
}
binding.editMessage.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(
p0: CharSequence?,
p1: Int,
p2: Int,
p3: Int
) {
}
override fun onTextChanged(
p0: CharSequence?,
p1: Int,
p2: Int,
p3: Int
) {
if (p0?.toString()?.trim()?.isNotEmpty() == true) isSending = false
updateSendControls()
}
})
binding.btnMore.setOnClickListener {
if (binding.contentBottom.visibility == View.GONE) {
val inputMethodManager =
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0)
binding.editMessage.clearFocus()
binding.contentBottom.visibility = View.VISIBLE
} else {
binding.contentBottom.visibility = View.GONE
}
}
binding.btnSmile.setOnClickListener {
binding.contentBottom.visibility = View.GONE
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
}
binding.btnDeleteImage.setOnClickListener {
clearSelectedImage()
}
//拍照
binding.conetnt1.setOnClickListener {
if (TextUtils.isEmpty(path)) {
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
} else {
toast(getString(R.string.only_one_photo))
}
}
//相册
binding.conetnt2.setOnClickListener {
if (TextUtils.isEmpty(path)) {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
getPermissionRequst(
permission,
REQUEST_PHOTO_PERMISSION
)
} else {
toast(getString(R.string.only_one_photo))
}
}
Handler(Looper.getMainLooper()).post {
binding.recyclerView.scrollToPosition(this@CustomerActivity_copy.data.size - 1)
binding.recyclerView.post {
if (customerMessageAdapter.itemCount > 0) {
val lastView = binding.recyclerView.layoutManager?.findViewByPosition(customerMessageAdapter.itemCount - 1)
lastView?.let {
val bottomOffset = it.bottom - binding.recyclerView.height
binding.recyclerView.scrollBy(0, bottomOffset)
}
}
}
}
handleMessage()
binding.btnSmile.visibility = View.GONE;
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> takePhoto()
REQUEST_PHOTO_PERMISSION -> {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, 56)
}
}
}
override fun initData() {
//getList()
var dataStr = PreferencesUtils.getString(this, Const.MESSAGE,"[]")
var models = Gson().fromJson<List<CustomerMessageModel.MessageItemModel>>(dataStr,object : TypeToken<List<CustomerMessageModel.MessageItemModel>>(){}.type)
var data2 = models.stream().filter { it-> !TextUtils.isEmpty(it.url) || !TextUtils.isEmpty(it.content) }.toList()
data.addAll(data2)
}
fun handleMessage(){
if (data.size > 0){
var model = data.last()
var isResend = false
var sendConetnt = ""
if (model.senderId == CKApp.app.userInfo?.id){
isResend = true
if (!TextUtils.isEmpty(model.url)){
url = model.url.toString()
}else{
sendConetnt = model.content.toString()
}
}else{
if (model.content!!.indexOf("不能替代兽医诊断") < 0){
isResend = true
if (data.size - 2 >= 0){
if (!TextUtils.isEmpty(model.url)){
url = data.get(data.size - 2).url.toString()
}else{
sendConetnt = data.get(data.size - 2).content.toString()
}
}
data.removeAt(data.size -1)
}
}
if (isResend){
customerMessageAdapter.data =data
customerMessageAdapter.notifyDataSetChanged()
if (!TextUtils.isEmpty(sendConetnt) || !TextUtils.isEmpty(url)){
handler.post {
sendMessage2(UUID.randomUUID().toString(),sendConetnt,true, url)
}
}
}
}
}
var handler = object : Handler(Looper.getMainLooper()){
}
override fun rightButton1OnClick() {
CKAlertDialog(this,getString(R.string.alert),getString(R.string.clear_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
if (isConfirm){
lock = true
PreferencesUtils.putString(this@CustomerActivity_copy, Const.MESSAGE,"[]")
data.clear()
customerMessageAdapter.data = data
customerMessageAdapter.notifyDataSetChanged()
}
}
}).show()
}
fun sendMessage(uuid :String ,content: String,isResend : Boolean, imageUrl: String) {
isAiResponding = true
if (!TextUtils.isEmpty(imageUrl)) {
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = CKApp.app.userInfo?.id!!
this.content = content
this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = CKApp.app.userInfo?.custName ?: ""
this.url = imageUrl
})
}
if (!TextUtils.isEmpty(content) && !isResend) {
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = CKApp.app.userInfo?.id!!
this.content = content
this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = CKApp.app.userInfo?.custName ?: ""
})
}
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = -1
this.content = ""
this.senderName = getString(R.string.ai_doctor)
this.uuid = uuid
})
TTHttpClient.postJsonStream3(this, "发送会话消息stream", TTRequestParam().apply {
add(TTRequestData("imageUrl", imageUrl))
add(TTRequestData("question", content))
}, object : ICallback {
override fun onSuccess(data: String) {
// var model = Gson().fromJson<AIMessageModel>(data, AIMessageModel::class.java)
var flag = false
for (i in 0..this@CustomerActivity_copy.data.size -1){
if (this@CustomerActivity_copy.data.get(i).uuid.equals(uuid)){
flag = true
this@CustomerActivity_copy.data.get(i).content = CKUtils.subMessage(this@CustomerActivity_copy.data.get(i).content + data)
}
}
if (!flag){
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = -1
this.content =data
// this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = getString(R.string.ai_doctor)
this.uuid = uuid
})
}
customerMessageAdapter.data = this@CustomerActivity_copy.data
customerMessageAdapter.notifyDataSetChanged()
if (this@CustomerActivity_copy.data.last().content!!.indexOf("不能替代兽医诊断") > 0) {
binding.recyclerView.scrollToPosition(this@CustomerActivity_copy.data.size - 1)
binding.recyclerView.post {
if (customerMessageAdapter.itemCount > 0) {
val lastView = binding.recyclerView.layoutManager?.findViewByPosition(customerMessageAdapter.itemCount - 1)
lastView?.let {
val bottomOffset = it.bottom - binding.recyclerView.height
binding.recyclerView.scrollBy(0, bottomOffset)
}
}
}
}
}
override fun onFailed(code: String, msg: String) {
isAiResponding = false
lock = true
// toast(msg)
//TTLog.e(msg)
}
override fun onComplete() {
isAiResponding = false
lock = true
}
})
customerMessageAdapter.data = this@CustomerActivity_copy.data
customerMessageAdapter.notifyDataSetChanged()
binding.recyclerView.scrollToPosition(this@CustomerActivity_copy.data.size - 1)
}
fun sendMessage2(uuid :String ,content: String,isResend : Boolean, imageUrl: String) {
isAiResponding = true
lock = false
if (!TextUtils.isEmpty(content) && !isResend) {
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = CKApp.app.userInfo?.id!!
this.content = content
this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = CKApp.app.userInfo?.custName ?: ""
})
}
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = -1
this.content = ""
this.senderName = getString(R.string.ai_doctor)
this.uuid = uuid
})
TTHttpClient.postJsonStream3(this, "发送会话消息stream", TTRequestParam().apply {
add(TTRequestData("imageUrl", imageUrl))
add(TTRequestData("question", content))
}, object : ICallback {
override fun onSuccess(data: String) {
// var model = Gson().fromJson<AIMessageModel>(data, AIMessageModel::class.java)
var flag = false
for (i in 0..this@CustomerActivity_copy.data.size -1){
if (this@CustomerActivity_copy.data.get(i).uuid.equals(uuid)){
flag = true
this@CustomerActivity_copy.data.get(i).content = CKUtils.subMessage(this@CustomerActivity_copy.data.get(i).content + data)
}
}
if (!flag){
this@CustomerActivity_copy.data.add(CustomerMessageModel.MessageItemModel().apply {
senderId = -1
this.content =data
// this.senderAvatar = CKApp.app.userInfo?.avatar
this.senderName = getString(R.string.ai_doctor)
this.uuid = uuid
})
}
customerMessageAdapter.data = this@CustomerActivity_copy.data
customerMessageAdapter.notifyDataSetChanged()
if (this@CustomerActivity_copy.data.last().content!!.indexOf("不能替代兽医诊断") > 0) {
binding.recyclerView.scrollToPosition(this@CustomerActivity_copy.data.size - 1)
binding.recyclerView.post {
if (customerMessageAdapter.itemCount > 0) {
val lastView = binding.recyclerView.layoutManager?.findViewByPosition(customerMessageAdapter.itemCount - 1)
lastView?.let {
val bottomOffset = it.bottom - binding.recyclerView.height
binding.recyclerView.scrollBy(0, bottomOffset)
}
}
}
}
}
override fun onFailed(code: String, msg: String) {
isAiResponding = false
lock = true
// toast(msg)
//TTLog.e(msg)
}
override fun onComplete() {
isAiResponding = false
lock = true
}
})
customerMessageAdapter.data = this@CustomerActivity_copy.data
customerMessageAdapter.notifyDataSetChanged()
binding.recyclerView.scrollToPosition(this@CustomerActivity_copy.data.size - 1)
}
fun uploadImage(content: String, imagePath: String) {
var param = TTRequestParam()
param.files.add(File(imagePath))
showProgeassDialog()
TTHttpClient.postFile(this, "上传照片s", param, object : ICallback {
override fun onSuccess(data: String) {
hiddenProgeassDialog()
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
handler.post {
sendMessage(UUID.randomUUID().toString(), content, false, model.urls)
}
}
override fun onFailed(code: String, msg: String) {
hiddenProgeassDialog()
lock = true
if (path.isEmpty() && binding.editMessage.text.toString().isEmpty()) {
path = imagePath
showImagePreview(imagePath)
binding.editMessage.setText(content)
}
toast(msg)
}
})
}
fun getList() {
TTHttpClient.get(this, "会话消息列表", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
// var model = Gson().fromJson<CustomerMessageModel>(data,CustomerMessageModel::class.java)
// model.rows?.let {
// this@CustomerActivity.data.addAll(model.rows!!)
// }
var model = Gson().fromJson<List<CustomerMessageModel.MessageItemModel>>(
data,
object : TypeToken<List<CustomerMessageModel.MessageItemModel>>() {}.type
)
this@CustomerActivity_copy.data.addAll(model)
customerMessageAdapter.data = this@CustomerActivity_copy.data
customerMessageAdapter.notifyDataSetChanged()
}
override fun onFailed(code: String, msg: String) {
}
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 55 && resultCode == RESULT_OK) {
path = PreferencesUtils.getString(this@CustomerActivity_copy, Const.IMAGE_PATH, "")!!
prepareSelectedImage()
} else if (requestCode == 56 && resultCode == RESULT_OK) {
path = TTUtils.getImageUrl(this, data!!.getData());
prepareSelectedImage()
}
}
private fun prepareSelectedImage() {
if (TextUtils.isEmpty(path)) return
LubanCompressUtil.getCompressFile(
this,
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (isFinishing || isDestroyed) return
path = file.absolutePath
lock = true
binding.contentBottom.visibility = View.GONE
showImagePreview(path)
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
// Rebind the IME after selecting an image so its action button is
// available even though the text field itself is empty.
binding.editMessage.postDelayed({
val imm = getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.restartInput(binding.editMessage)
}, 150L)
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
lock = true
path = ""
toast(failMsg)
}
}
)
}
private fun showImagePreview(imagePath: String) {
isSending = false
binding.contentImagePreview.visibility = View.VISIBLE
updateSendControls()
binding.imagePreview.scaleType = ImageView.ScaleType.CENTER_CROP
Glide.with(this)
.load(imagePath)
.into(binding.imagePreview)
}
private fun clearSelectedImage() {
path = ""
url = ""
binding.imagePreview.setImageDrawable(null)
binding.contentImagePreview.visibility = View.GONE
updateSendControls()
}
private fun clearInputViews() {
binding.editMessage.setText("")
path = ""
url = ""
binding.imagePreview.setImageDrawable(null)
binding.contentImagePreview.visibility = View.GONE
// Sending has already been initiated; keep the controls in their idle state
// until the user starts composing the next draft.
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
}
private fun updateSendControls() {
val hasImage = path.isNotEmpty()
val hasText = binding.editMessage.text.toString().trim().isNotEmpty()
if (isSending) {
binding.btnSend.visibility = View.GONE
binding.btnMore.visibility = View.VISIBLE
return
}
binding.btnSend.visibility = if (hasImage || hasText) View.VISIBLE else View.GONE
binding.btnMore.visibility = if (hasImage) View.GONE else View.VISIBLE
}
override fun onDestroy() {
super.onDestroy()
PreferencesUtils.putString(this, Const.MESSAGE,Gson().toJson(data))
}
fun takePhoto() {
try {
val openCameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val vFile = File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES),
"${System.currentTimeMillis()}.png"
)
val parentDir = vFile.parentFile
if (parentDir != null && !parentDir.exists()) {
parentDir.mkdirs()
}
TTLog.e(vFile.absolutePath)
PreferencesUtils.putString(getApplicationContext(), Const.IMAGE_PATH, vFile.absolutePath)
val cameraUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
this@CustomerActivity_copy,
packageName + ".fileprovider",
vFile
)
} else {
Uri.fromFile(vFile)
}
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri)
startActivityForResult(openCameraIntent, 55)
}catch (e: Exception){
e.printStackTrace()
}
}
class CustomerMessageAdapter(
var context: TTBaseActivity,
var data: List<CustomerMessageModel.MessageItemModel>,
var onItemClickListenner: OnItemClickListenner
) : RecyclerView.Adapter<CustomerMessageAdapter.CustomerMessageHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CustomerMessageHolder {
return CustomerMessageHolder(
LayoutItemAiCustomerMessageBinding.inflate(
LayoutInflater.from(
context
), parent, false
)
)
}
override fun onBindViewHolder(
holder: CustomerMessageHolder,
position: Int
) {
// holder.binding.text.text = data.get(position).name
var model = data.get(position)
if (model.senderId != CKApp.app.userInfo?.id) {
holder.binding.contentTo.visibility = View.GONE
holder.binding.contentFrom.visibility = View.VISIBLE
holder.binding.textFrom.text = model.content
holder.binding.textFromNick.text = model.senderName
Glide.with(context)
.load(model.senderAvatar)
.placeholder(R.mipmap.image_ai_doctor)
.into(holder.binding.imageFrom)
holder.binding.imageContentTo.setOnClickListener(imageClick)
if (TextUtils.isEmpty(model.content)){
holder.binding.textFrom.visibility= View.GONE
holder.binding.progress.visibility= View.VISIBLE
}else{
holder.binding.textFrom.visibility= View.VISIBLE
holder.binding.progress.visibility= View.GONE
}
} else {
holder.binding.contentTo.visibility = View.VISIBLE
holder.binding.contentFrom.visibility = View.GONE
holder.binding.textTo.text = model.content
holder.binding.textToNick.text = model.senderName
Glide.with(context)
.load(model.senderAvatar)
.placeholder(R.mipmap.image_default)
.into(holder.binding.imageTo)
if (!TextUtils.isEmpty(model.url)) {
holder.binding.textTo.visibility = View.GONE
holder.binding.imageContentTo.visibility = View.VISIBLE
val imageView = holder.binding.imageContentTo
val imageUrl = model.url!!
imageView.setTag(imageUrl)
resetImageContentSize(imageView)
Glide.with(context)
.load(imageUrl)
.placeholder(R.mipmap.image_default)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean = false
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
if (imageView.tag == imageUrl) {
setImageContentSize(
imageView,
resource.intrinsicWidth,
resource.intrinsicHeight
)
}
return false
}
})
.into(imageView)
imageView.setOnClickListener(imageClick)
} else {
holder.binding.textTo.visibility = View.VISIBLE
holder.binding.imageContentTo.visibility = View.GONE
}
}
holder.binding.root.setTag(position)
holder.binding.root.setOnClickListener {
val position = it.tag as Int
onItemClickListenner.onClick(position, data.get(position))
}
}
private fun resetImageContentSize(imageView: View) {
val density = imageView.resources.displayMetrics.density
imageView.layoutParams = imageView.layoutParams.apply {
width = getMaxImageWidth(density)
height = (200 * density).toInt()
}
}
private fun setImageContentSize(imageView: View, sourceWidth: Int, sourceHeight: Int) {
if (sourceWidth <= 0 || sourceHeight <= 0) return
val density = imageView.resources.displayMetrics.density
val maxWidth = getMaxImageWidth(density).toFloat()
val maxHeight = (200 * density).toFloat()
val scale = minOf(
1f,
maxWidth / sourceWidth.toFloat(),
maxHeight / sourceHeight.toFloat()
)
imageView.layoutParams = imageView.layoutParams.apply {
width = (sourceWidth * scale).toInt().coerceAtLeast(1)
height = (sourceHeight * scale).toInt().coerceAtLeast(1)
}
}
private fun getMaxImageWidth(density: Float): Int {
val requestedWidth = (200 * density).toInt()
// Reserve the avatar, paddings, and the bubble's left inset.
val availableWidth = context.resources.displayMetrics.widthPixels - (155 * density).toInt()
return minOf(requestedWidth, availableWidth.coerceAtLeast(1))
}
override fun getItemCount(): Int {
return data.size
}
class CustomerMessageHolder(var binding: LayoutItemAiCustomerMessageBinding) :
RecyclerView.ViewHolder(binding.root) {
}
var imageClick = object : View.OnClickListener{
override fun onClick(v: View?) {
var url = v?.tag as String
var urls = mutableListOf<String>()
var n = 0
var pos = 0
for (m in data){
if (!TextUtils.isEmpty(m.url)){
urls.add(m.url!!)
if (url.equals(m.url)){
pos = n
}
n++
}
}
context.toActivity(ImageViewActivity::class.java, DataCollection().apply {
add(Data("data",Gson().toJson(urls)))
add(Data("pos",pos.toString()))
})
}
}
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1001
private const val REQUEST_PHOTO_PERMISSION = 1002
}
}

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.customer
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -36,6 +37,7 @@ import com.ck.ckcollar.app.model.CustomerMessageModel
import com.ck.ckcollar.app.model.UploadModel
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -64,6 +66,7 @@ class MineCustomerActivity : CKBaseActivity() {
var path = ""
var url = ""
private var imageSendDraft = ""
var lock = true
var webSocket: WebSocket? = null
@@ -98,6 +101,13 @@ class MineCustomerActivity : CKBaseActivity() {
binding.editMessage.setHorizontallyScrolling(false);
binding.editMessage.setOnClickListener {
binding.contentBottom.visibility = View.GONE
}
binding.editMessage.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
if (hasFocus) binding.contentBottom.visibility = View.GONE
}
binding.editMessage.setOnEditorActionListener(object : TextView.OnEditorActionListener {
override fun onEditorAction(
p0: TextView?,
@@ -120,6 +130,7 @@ class MineCustomerActivity : CKBaseActivity() {
sendMessage(text)
}
}
return true
}
return false
}
@@ -157,7 +168,9 @@ class MineCustomerActivity : CKBaseActivity() {
})
binding.btnMore.setOnClickListener {
if (binding.contentBottom.visibility == View.GONE) {
KeyboardUtils.hideKeyboard(this@MineCustomerActivity)
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as android.view.inputmethod.InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0)
binding.editMessage.clearFocus()
binding.contentBottom.visibility = View.VISIBLE
} else {
binding.contentBottom.visibility = View.GONE
@@ -167,13 +180,17 @@ class MineCustomerActivity : CKBaseActivity() {
}
binding.btnSmile.setOnClickListener {
binding.contentBottom.visibility = View.GONE
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
}
binding.conetnt1.setOnClickListener {
if (TextUtils.isEmpty(path)) {
takePhoto()
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -181,12 +198,12 @@ class MineCustomerActivity : CKBaseActivity() {
}
binding.conetnt2.setOnClickListener {
if (TextUtils.isEmpty(path)) {
//相册
val i = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
startActivityForResult(i, 56)
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
getPermissionRequst(permission, REQUEST_PHOTO_PERMISSION)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -195,6 +212,23 @@ class MineCustomerActivity : CKBaseActivity() {
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> takePhoto()
REQUEST_PHOTO_PERMISSION -> openPhotoAlbum()
}
}
private fun openPhotoAlbum() {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, 56)
}
override fun initData() {
//getList()
@@ -262,7 +296,7 @@ class MineCustomerActivity : CKBaseActivity() {
override fun onOpen(webSocket: WebSocket, response: Response) {
TTLog.e("onOpen")
runOnUiThread {
toast(getString(R.string.connect_customer_success))
// toast(getString(R.string.connect_customer_success))
}
}
})
@@ -280,7 +314,9 @@ class MineCustomerActivity : CKBaseActivity() {
fun sendMessage(content: String) {
showProgeassDialog(getString(R.string.send))
if (!TextUtils.isEmpty(url)) {
showProgeassDialog(getString(R.string.send))
}
TTHttpClient.postJson(this, "发送人工会话消息", TTRequestParam().apply {
add(TTRequestData("content", content))
add(TTRequestData("chatType", 2))
@@ -309,7 +345,8 @@ class MineCustomerActivity : CKBaseActivity() {
binding.editMessage.setText("")
binding.editMessage.setText(if (!TextUtils.isEmpty(url)) imageSendDraft else "")
imageSendDraft = ""
path = ""
url = ""
customerMessageAdapter.data = this@MineCustomerActivity.data
@@ -341,7 +378,7 @@ class MineCustomerActivity : CKBaseActivity() {
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
url = model.urls
imageSendDraft = binding.editMessage.text.toString().replace("$$$$$", "")
sendMessage(url)
}
@@ -428,17 +465,35 @@ class MineCustomerActivity : CKBaseActivity() {
if (requestCode == 55 && resultCode == RESULT_OK) {
path = PreferencesUtils.getString(this@MineCustomerActivity, Const.IMAGE_PATH, "")!!
//addImage(path!!)
uploadImage()
compressAndUploadImage()
} else if (requestCode == 56 && resultCode == RESULT_OK) {
path = TTUtils.getImageUrl(this, data!!.getData());
// addImage(path)
uploadImage()
compressAndUploadImage()
}
}
private fun compressAndUploadImage() {
if (TextUtils.isEmpty(path)) return
LubanCompressUtil.getCompressFile(
this,
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (isFinishing || isDestroyed) return
path = file.absolutePath
uploadImage()
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
path = ""
toast(failMsg)
}
}
)
}
override fun onDestroy() {
super.onDestroy()
@@ -476,6 +531,11 @@ class MineCustomerActivity : CKBaseActivity() {
startActivityForResult(openCameraIntent, 55)
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1201
private const val REQUEST_PHOTO_PERMISSION = 1202
}
class CustomerMessageAdapter(
var context: Context,
var data: List<CustomerMessageModel.MessageItemModel>,
@@ -528,10 +588,11 @@ class MineCustomerActivity : CKBaseActivity() {
holder.binding.textTo.visibility = View.GONE
holder.binding.imageContentTo.visibility = View.VISIBLE
Glide.with(context)
.load(ActionCode.BASE_URL + model.url)
.placeholder(R.mipmap.image_default)
.into(holder.binding.imageContentTo)
ChatImageLoader.load(
context,
holder.binding.imageContentTo,
ActionCode.BASE_URL + model.url
)
} else {
holder.binding.textTo.visibility = View.VISIBLE
holder.binding.imageContentTo.visibility = View.GONE

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.customer
import android.Manifest
import android.content.Context
import android.content.Intent
import android.graphics.Rect
@@ -45,6 +46,7 @@ import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -76,6 +78,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
var path = ""
var url = ""
private var imageSendDraft = ""
var lock = true
var webSocket: WebSocket? = null
@@ -154,6 +157,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
sendMessage(text)
}
}
return true
}
return false
}
@@ -191,7 +195,9 @@ class PeopleCustomerActivity : CKBaseActivity() {
})
binding.btnMore.setOnClickListener {
if (binding.contentBottom.visibility == View.GONE) {
KeyboardUtils.hideKeyboard(this@PeopleCustomerActivity)
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as android.view.inputmethod.InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0)
binding.editMessage.clearFocus()
binding.contentBottom.visibility = View.VISIBLE
} else {
binding.contentBottom.visibility = View.GONE
@@ -203,6 +209,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
}
binding.btnSmile.setOnClickListener {
binding.contentBottom.visibility = View.GONE
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
handler2.sendEmptyMessageDelayed(0,300)
@@ -211,16 +218,10 @@ class PeopleCustomerActivity : CKBaseActivity() {
binding.conetnt1.setOnClickListener {
if (TextUtils.isEmpty(path)) {
if (!checkCameraPermissions()){
CKAlertDialog(this@PeopleCustomerActivity,getString(R.string.alert),getString(R.string.camera_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
TODO("Not yet implemented")
}
}).show()
return@setOnClickListener
}
takePhoto()
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -228,12 +229,12 @@ class PeopleCustomerActivity : CKBaseActivity() {
}
binding.conetnt2.setOnClickListener {
if (TextUtils.isEmpty(path)) {
//相册
val i = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
startActivityForResult(i, 56)
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
getPermissionRequst(permission, REQUEST_PHOTO_PERMISSION)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -243,6 +244,23 @@ class PeopleCustomerActivity : CKBaseActivity() {
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> takePhoto()
REQUEST_PHOTO_PERMISSION -> openPhotoAlbum()
}
}
private fun openPhotoAlbum() {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, 56)
}
override fun initData() {
@@ -353,7 +371,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
override fun onOpen(webSocket: WebSocket, response: Response) {
TTLog.e("onOpen")
runOnUiThread {
toast(getString(R.string.connect_customer_success))
// toast(getString(R.string.connect_customer_success))
}
}
})
@@ -394,7 +412,9 @@ class PeopleCustomerActivity : CKBaseActivity() {
"<img src=\"$content\"/>"
}
showProgeassDialog(getString(R.string.send))
if (!TextUtils.isEmpty(url)) {
showProgeassDialog(getString(R.string.send))
}
TTHttpClient.postJson(this, "发送人工会话消息", TTRequestParam().apply {
add(TTRequestData("content", sendContent))
add(TTRequestData("chatType", chatType))
@@ -422,7 +442,8 @@ class PeopleCustomerActivity : CKBaseActivity() {
binding.editMessage.setText("")
binding.editMessage.setText(if (!TextUtils.isEmpty(url)) imageSendDraft else "")
imageSendDraft = ""
path = ""
url = ""
customerMessageAdapter.data = this@PeopleCustomerActivity.data
@@ -454,7 +475,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
url = model.urls
imageSendDraft = binding.editMessage.text.toString().replace("$$$$$", "")
sendMessage(url)
}
@@ -559,17 +580,35 @@ class PeopleCustomerActivity : CKBaseActivity() {
if (requestCode == 55 && resultCode == RESULT_OK) {
path = PreferencesUtils.getString(this@PeopleCustomerActivity, Const.IMAGE_PATH, "")!!
//addImage(path!!)
uploadImage()
compressAndUploadImage()
} else if (requestCode == 56 && resultCode == RESULT_OK) {
path = TTUtils.getImageUrl(this, data!!.getData());
//addImage(path)
uploadImage()
compressAndUploadImage()
}
}
private fun compressAndUploadImage() {
if (TextUtils.isEmpty(path)) return
LubanCompressUtil.getCompressFile(
this,
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (isFinishing || isDestroyed) return
path = file.absolutePath
uploadImage()
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
path = ""
toast(failMsg)
}
}
)
}
override fun onDestroy() {
super.onDestroy()
@@ -607,6 +646,11 @@ class PeopleCustomerActivity : CKBaseActivity() {
startActivityForResult(openCameraIntent, 55)
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1301
private const val REQUEST_PHOTO_PERMISSION = 1302
}
class CustomerMessageAdapter(
var context: TTBaseActivity,
var data: List<CustomerMessageModel.MessageItemModel>,
@@ -675,10 +719,7 @@ class PeopleCustomerActivity : CKBaseActivity() {
holder.binding.textTo.visibility = View.GONE
holder.binding.imageContentTo.visibility = View.VISIBLE
Glide.with(context)
.load( model.url)
.placeholder(R.mipmap.image_default)
.into(holder.binding.imageContentTo)
ChatImageLoader.load(context, holder.binding.imageContentTo, model.url!!)
holder.binding.imageContentTo.setTag(model.url)

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.customer
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -36,6 +37,7 @@ import com.ck.ckcollar.app.model.CustomerMessageModel
import com.ck.ckcollar.app.model.UploadModel
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -58,12 +60,15 @@ import java.util.concurrent.TimeUnit
class ShopCustomerActivity : CKBaseActivity() {
private val requestPhotoPermission = 1402
lateinit var binding: ActivityCustomerBinding
lateinit var customerMessageAdapter: CustomerMessageAdapter
var data = mutableListOf<CustomerMessageModel.MessageItemModel>()
var path = ""
var url = ""
private var imageSendDraft = ""
var lock = true
var webSocket: WebSocket? = null
@@ -97,6 +102,13 @@ class ShopCustomerActivity : CKBaseActivity() {
binding.editMessage.setHorizontallyScrolling(false);
binding.editMessage.setOnClickListener {
binding.contentBottom.visibility = View.GONE
}
binding.editMessage.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
if (hasFocus) binding.contentBottom.visibility = View.GONE
}
binding.editMessage.setOnEditorActionListener(object : TextView.OnEditorActionListener {
override fun onEditorAction(
p0: TextView?,
@@ -119,6 +131,7 @@ class ShopCustomerActivity : CKBaseActivity() {
sendMessage(text)
}
}
return true
}
return false
}
@@ -156,7 +169,9 @@ class ShopCustomerActivity : CKBaseActivity() {
})
binding.btnMore.setOnClickListener {
if (binding.contentBottom.visibility == View.GONE) {
KeyboardUtils.hideKeyboard(this@ShopCustomerActivity)
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as android.view.inputmethod.InputMethodManager
inputMethodManager.hideSoftInputFromWindow(binding.editMessage.windowToken, 0)
binding.editMessage.clearFocus()
binding.contentBottom.visibility = View.VISIBLE
} else {
binding.contentBottom.visibility = View.GONE
@@ -166,6 +181,7 @@ class ShopCustomerActivity : CKBaseActivity() {
}
binding.btnSmile.setOnClickListener {
binding.contentBottom.visibility = View.GONE
binding.editMessage.requestFocus()
KeyboardUtils.showKeyboard(binding.editMessage)
}
@@ -181,11 +197,12 @@ class ShopCustomerActivity : CKBaseActivity() {
binding.conetnt2.setOnClickListener {
if (TextUtils.isEmpty(path)) {
//相册
val i = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
startActivityForResult(i, 56)
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
getPermissionRequst(permission, requestPhotoPermission)
} else {
toast(getString(R.string.only_one_photo))
}
@@ -194,6 +211,22 @@ class ShopCustomerActivity : CKBaseActivity() {
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
if (requestCode == requestPhotoPermission) {
openPhotoAlbum()
}
}
private fun openPhotoAlbum() {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, 56)
}
override fun initData() {
//getList()
@@ -261,7 +294,7 @@ class ShopCustomerActivity : CKBaseActivity() {
override fun onOpen(webSocket: WebSocket, response: Response) {
TTLog.e("onOpen")
runOnUiThread {
toast(getString(R.string.connect_customer_success))
// toast(getString(R.string.connect_customer_success))
}
}
})
@@ -295,7 +328,9 @@ class ShopCustomerActivity : CKBaseActivity() {
// e.printStackTrace()
// }
showProgeassDialog(getString(R.string.send))
if (!TextUtils.isEmpty(url)) {
showProgeassDialog(getString(R.string.send))
}
TTHttpClient.postJson(this, "发送人工会话消息", TTRequestParam().apply {
add(TTRequestData("content", content))
add(TTRequestData("chatType", 1))
@@ -323,7 +358,8 @@ class ShopCustomerActivity : CKBaseActivity() {
binding.editMessage.setText("")
binding.editMessage.setText(if (!TextUtils.isEmpty(url)) imageSendDraft else "")
imageSendDraft = ""
path = ""
url = ""
customerMessageAdapter.data = this@ShopCustomerActivity.data
@@ -355,6 +391,7 @@ class ShopCustomerActivity : CKBaseActivity() {
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
url = model.urls
imageSendDraft = binding.editMessage.text.toString().replace("$$$$$", "")
sendMessage(url)
}
@@ -441,17 +478,35 @@ class ShopCustomerActivity : CKBaseActivity() {
if (requestCode == 55 && resultCode == RESULT_OK) {
path = PreferencesUtils.getString(this@ShopCustomerActivity, Const.IMAGE_PATH, "")!!
//addImage(path!!)
uploadImage()
compressAndUploadImage()
} else if (requestCode == 56 && resultCode == RESULT_OK) {
path = TTUtils.getImageUrl(this, data!!.getData());
//addImage(path)
uploadImage()
compressAndUploadImage()
}
}
private fun compressAndUploadImage() {
if (TextUtils.isEmpty(path)) return
LubanCompressUtil.getCompressFile(
this,
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (isFinishing || isDestroyed) return
path = file.absolutePath
uploadImage()
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
path = ""
toast(failMsg)
}
}
)
}
override fun onDestroy() {
super.onDestroy()
@@ -541,10 +596,7 @@ class ShopCustomerActivity : CKBaseActivity() {
holder.binding.textTo.visibility = View.GONE
holder.binding.imageContentTo.visibility = View.VISIBLE
Glide.with(context)
.load( model.url)
.placeholder(R.mipmap.image_default)
.into(holder.binding.imageContentTo)
ChatImageLoader.load(context, holder.binding.imageContentTo, model.url!!)
} else {
holder.binding.textTo.visibility = View.VISIBLE
holder.binding.imageContentTo.visibility = View.GONE

View File

@@ -1,40 +1,64 @@
package com.ck.ckcollar.app.ui.act.home
import android.graphics.Color
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.MenuItem
import android.os.SystemClock
import android.provider.Settings
import android.view.View
import android.view.WindowInsets
import android.widget.TextView
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityMainBinding
import com.ck.ckcollar.app.databinding.FragmentHomeBinding
import com.ck.ckcollar.app.model.CustomerStatusModel
import com.ck.ckcollar.app.model.UpdateModel
import com.ck.ckcollar.app.ui.act.home.adapter.MainFragmentAdapter
import com.ck.ckcollar.app.ui.act.home.fragment.HealthFragment
import com.ck.ckcollar.app.ui.act.home.fragment.HomeFragment
import com.ck.ckcollar.app.ui.act.home.fragment.MineFragment
import com.ck.ckcollar.app.ui.act.home.fragment.ShopFragment
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.base.CKBaseFragment
import com.ck.ckcollar.app.ui.base.CKLHBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKUpdateAlertDialog
import com.ck.ckcollar.app.utils.AppUpdateDownloader
import com.ck.ckcollar.app.utils.networkone.ICallback
import com.ck.ckcollar.app.utils.networkone.TTHttpClient
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.cyclone.shadowsocks.ui.frg.TTBaseFragment
import com.google.android.material.navigation.NavigationBarView
import com.google.gson.Gson
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.utils.TTUtils
import java.io.File
class MainActivity : CKLHBaseActivity(),View.OnClickListener {
private var lastBackPressedAt = 0L
private var pendingUpdate: UpdateModel? = null
private var updateDialog: CKUpdateAlertDialog? = null
private var pendingApk: File? = null
private val notificationPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
pendingUpdate?.let(::startUpdateDownload)
} else {
toast(getString(R.string.notification_permission_required))
}
}
private val installPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || packageManager.canRequestPackageInstalls()) {
pendingApk?.let(::installApk)
} else {
toast(getString(R.string.install_permission_required))
}
}
lateinit var mainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
@@ -43,6 +67,18 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
setContentView(mainBinding.root)
super.onCreate(savedInstanceState)
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val now = SystemClock.elapsedRealtime()
if (lastBackPressedAt != 0L && now - lastBackPressedAt <= EXIT_INTERVAL_MS) {
finish()
} else {
lastBackPressedAt = now
toast(getString(R.string.press_again_to_exit))
}
}
})
// if (Build.VERSION.SDK_INT >= 36){
//
@@ -100,6 +136,22 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
mainBinding.viewPager.setPagingEnabled(false)
if (intent.getBooleanExtra(EXTRA_SHOW_HOME, false)) {
showHomeTab()
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
if (intent.getBooleanExtra(EXTRA_SHOW_HOME, false)) {
showHomeTab()
}
}
private fun showHomeTab() {
mainBinding.content1.performClick()
}
fun resetItemIcon() {
@@ -170,19 +222,28 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
}
fun checkUpdate(){
if (hasCheckedUpdate) return
hasCheckedUpdate = true
TTHttpClient.get(this,"更新", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
var mode = Gson().fromJson<UpdateModel>(data,UpdateModel::class.java)
val model = Gson().fromJson<UpdateModel>(data, UpdateModel::class.java)
if (mode.androidVersion != TTUtils.getVersionCode(this@MainActivity)){
CKUpdateAlertDialog(this@MainActivity,mode
.content!!,mode.isForce,object : CKUpdateAlertDialog.AlertClickListener{
val currentVersionCode = TTUtils.getVersionCode(this@MainActivity)
if (model.versionCode > currentVersionCode) {
val updateContent = model.content.orEmpty().replace("\\n", "\n")
updateDialog = CKUpdateAlertDialog(this@MainActivity, updateContent,
model.isForce, object : CKUpdateAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
if (isConfirm) {
if (!model.isForce) {
toast("更新下载进度可在任务栏中查看")
}
requestUpdateDownload(model)
}
}
}).show()
}).also { it.show() }
}
}
@@ -192,4 +253,91 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
}
})
}
private fun requestUpdateDownload(model: UpdateModel) {
if (model.downloadUrl.isNullOrBlank()) {
toast(getString(R.string.update_url_invalid))
return
}
pendingUpdate = model
if (!model.isForce && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
PackageManager.PERMISSION_GRANTED
) {
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
return
}
startUpdateDownload(model)
}
private fun startUpdateDownload(model: UpdateModel) {
pendingUpdate = null
if (model.isForce) {
updateDialog?.showDownloadProgress()
} else {
updateDialog?.dismiss()
updateDialog = null
}
AppUpdateDownloader(applicationContext).download(
model.downloadUrl.orEmpty(),
showNotification = !model.isForce,
callback = object : AppUpdateDownloader.Callback {
override fun onProgress(percent: Int) {
runOnUiThread { updateDialog?.updateProgress(percent) }
}
override fun onComplete(apkFile: File) {
runOnUiThread {
if (model.isForce) {
updateDialog?.dismiss()
updateDialog = null
}
pendingApk = apkFile
installApk(apkFile)
}
}
override fun onError(message: String) {
runOnUiThread {
updateDialog?.showDownloadError()
toast(getString(R.string.update_download_failed, message))
}
}
}
)
}
private fun installApk(apkFile: File) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
!packageManager.canRequestPackageInstalls()
) {
toast(getString(R.string.install_permission_required))
installPermissionLauncher.launch(
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName")
)
)
return
}
pendingApk = null
val apkUri = FileProvider.getUriForFile(
this,
"$packageName.fileprovider",
apkFile
)
startActivity(
Intent(Intent.ACTION_VIEW).apply {
setDataAndType(apkUri, "application/vnd.android.package-archive")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
companion object {
const val EXTRA_SHOW_HOME = "extra_show_home"
private const val EXIT_INTERVAL_MS = 2_000L
private var hasCheckedUpdate = false
}
}

View File

@@ -8,6 +8,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.FragmentHealthBinding
import com.ck.ckcollar.app.databinding.LayoutItemNoteBinding
@@ -16,6 +17,7 @@ import com.ck.ckcollar.app.model.CustomerStatusModel
import com.ck.ckcollar.app.model.HealthNoteModel
import com.ck.ckcollar.app.ui.act.customer.CustomerActivity
import com.ck.ckcollar.app.ui.act.customer.PeopleCustomerActivity
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.act.note.NoteDetailsActivity
import com.ck.ckcollar.app.ui.base.CKBaseFragment
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
@@ -24,9 +26,6 @@ import com.ck.ckcollar.app.utils.networkone.TTHttpClient
import com.ck.ckcollar.app.utils.networkone.model.TTRequestData
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.google.gson.Gson
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
@@ -64,26 +63,37 @@ class HealthFragment: CKBaseFragment() {
binding.recyclerView.adapter = noteAdapter
binding.btnAIText.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(CustomerActivity::class.java, DataCollection())
}
binding.btnAIPhoto.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(CustomerActivity::class.java, DataCollection())
}
binding.contentCustomer.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
checkCustomerStatus()
}
binding.refreshLayout.setEnableLoadMore(false)
binding.refreshLayout.setOnRefreshListener {
page = 1
getData()
getCustomerTime()
if (CKApp.app.isUserLoggedIn) {
getCustomerTime()
}
}
}
private fun requireLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
toActivity(LoginActivity::class.java, DataCollection())
return false
}
override fun initData() {
@@ -93,6 +103,7 @@ class HealthFragment: CKBaseFragment() {
fun getCustomerTime(){
if (!CKApp.app.isUserLoggedIn) return
TTHttpClient.get(activity as TTBaseActivity,"客服在线状态", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
@@ -101,7 +112,19 @@ class HealthFragment: CKBaseFragment() {
binding.textDate.text = getString(R.string.pet_advisor2)
binding.textDate2.text = "("+customerStatusModel.serviceStartTime +","+customerStatusModel.serviceEndTime+")"
val serviceTimes = listOfNotNull(
customerStatusModel.serviceStartTime?.takeIf { it.isNotBlank() },
customerStatusModel.serviceEndTime?.takeIf { it.isNotBlank() }
)
binding.textDate2.text = when (serviceTimes.size) {
0 -> getString(R.string.pet_advisor_time_default)
1 -> serviceTimes.first()
else -> getString(
R.string.pet_advisor_time_format,
serviceTimes[0],
serviceTimes[1]
)
}
}
override fun onFailed(code: String, msg: String) {
@@ -116,6 +139,8 @@ class HealthFragment: CKBaseFragment() {
}
fun getData(){
// if (!CKApp.app.isUserLoggedIn) return
TTHttpClient.get(activity as TTBaseActivity,"宠物知识列表", TTRequestParam().apply {
add(
TTRequestData("pageNum", page)
@@ -144,7 +169,8 @@ class HealthFragment: CKBaseFragment() {
override fun onFailed(code: String, msg: String) {
try {
toast(msg)
binding.refreshLayout.finishLoadMore(true)
binding.refreshLayout.finishLoadMore(false)
binding.refreshLayout.finishRefresh(false)
}catch (e : Exception){
e.printStackTrace()

View File

@@ -1,26 +1,36 @@
package com.ck.ckcollar.app.ui.act.home.fragment
import android.Manifest
import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable.Orientation
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.provider.Settings
import android.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.FileProvider
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.FragmentHomeBinding
import com.ck.ckcollar.app.listenner.OnItemClickListenner
@@ -35,12 +45,14 @@ import com.ck.ckcollar.app.ui.act.petmanager.InputInviteCodeActivity
import com.ck.ckcollar.app.ui.act.petmanager.MapLocationActivity
import com.ck.ckcollar.app.ui.act.petmanager.PetManagerActivity
import com.ck.ckcollar.app.ui.act.petmanager.ViewPetFileActivity
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.base.CKBaseFragment
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.ui.popview.CKCollarTypePopView
import com.ck.ckcollar.app.ui.popview.CKPowerAlertView
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -67,6 +79,38 @@ import kotlin.random.Random
class HomeFragment : CKBaseFragment() {
private enum class CameraAction { SCAN, TAKE_PHOTO }
private var pendingCameraAction: CameraAction? = null
private var imageUploadInProgress = false
private var permissionTopDialog: AlertDialog? = null
private val cameraSettingsLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
val action = pendingCameraAction
pendingCameraAction = null
if (checkCameraPermissions()) {
performCameraAction(action)
}
}
private val cameraPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
hideCameraPermissionTopDialog()
val action = pendingCameraAction
if (granted) {
pendingCameraAction = null
performCameraAction(action)
} else if (!ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.CAMERA
)) {
showCameraSettingsDialog()
} else {
pendingCameraAction = null
}
}
lateinit var homeBinding: FragmentHomeBinding
var collarDatas = listOf<CollarTypeModel.TypeDataModel>()
@@ -75,6 +119,9 @@ class HomeFragment : CKBaseFragment() {
var dataModel: CollarTypeModel.TypeDataModel? = null
var homeModel: HomeModel? = null
var isFirstShow = false
private var waitingForLoginResult = false
private var loadedLoginToken: String? = null
private var shouldRefreshPetListOnResume = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -99,10 +146,13 @@ class HomeFragment : CKBaseFragment() {
homeBinding.textName?.setOnClickListener {
getPetList()
if (!requireLogin()) return@setOnClickListener
if (collarDatas.size <= 1) return@setOnClickListener
showPetSelector()
}
//扫一扫
homeBinding.btnScan?.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
if (dataModel == null) {
toast(getString(R.string.choose_pet_alert))
return@setOnClickListener
@@ -111,6 +161,11 @@ class HomeFragment : CKBaseFragment() {
if (!checkCameraPermissions()){
CKAlertDialog(activity!!,getString(R.string.alert),getString(R.string.camera_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
if(isConfirm){
requestCameraPermission(CameraAction.SCAN)
}else{
}
}
}).show()
@@ -120,13 +175,14 @@ class HomeFragment : CKBaseFragment() {
startScan()
}
homeBinding.btnCamera?.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
try {
if (!checkCameraPermissions()){
CKAlertDialog(activity!!,getString(R.string.alert),getString(R.string.camera_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
if (isConfirm) requestCameraPermission(CameraAction.TAKE_PHOTO)
}
}).show()
return@setOnClickListener
@@ -139,9 +195,11 @@ class HomeFragment : CKBaseFragment() {
}
}
homeBinding.btnManager?.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(PetManagerActivity::class.java, DataCollection())
}
homeBinding.btnAddPet?.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(AddPetActivity::class.java, DataCollection())
}
@@ -167,6 +225,11 @@ class HomeFragment : CKBaseFragment() {
}
homeBinding.btnViewPostion?.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
if(homeModel?.equipment?.collNum == null){
toast("请为您的爱宠绑定项圈")
return@setOnClickListener
}
getData(dataModel?.id.toString())
// toActivity(MapLocationActivity::class.java, DataCollection().apply {
@@ -185,6 +248,10 @@ class HomeFragment : CKBaseFragment() {
})
}
homeBinding.refreshLayout?.setOnRefreshListener {
if (!CKApp.app.isUserLoggedIn) {
homeBinding.refreshLayout?.finishRefresh()
return@setOnRefreshListener
}
if (dataModel == null) {
getData("")
@@ -199,10 +266,75 @@ class HomeFragment : CKBaseFragment() {
homeBinding.btnTraining?.setOnClickListener {
toast(getString(R.string.waitting_open_area))
}
LiveDataBus.get().with("FIRST", Int::class.java)
.observe(viewLifecycleOwner, object : Observer<Int?> {
override fun onChanged(result: Int?) {
shouldRefreshPetListOnResume = true
}
})
}
private fun requireLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
waitingForLoginResult = true
toActivity(LoginActivity::class.java, DataCollection())
return false
}
private fun requestCameraPermission(action: CameraAction) {
pendingCameraAction = action
showCameraPermissionTopDialog()
permissionTopDialog?.window?.decorView?.post {
if (isAdded && pendingCameraAction == action) {
cameraPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
private fun showCameraPermissionTopDialog() {
permissionTopDialog?.dismiss()
val view = layoutInflater.inflate(R.layout.permission_top_dialog, null)
view.findViewById<TextView>(R.id.permissionTopTitle).text = getString(R.string.camera)
view.findViewById<TextView>(R.id.permissionTopContent).text = getString(R.string.camera_alert)
permissionTopDialog = AlertDialog.Builder(requireContext())
.setView(view)
.create()
.also { dialog ->
dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.show()
dialog.window?.apply {
setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT))
setGravity(Gravity.TOP)
setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
}
}
}
private fun hideCameraPermissionTopDialog() {
permissionTopDialog?.dismiss()
permissionTopDialog = null
}
override fun onDestroyView() {
hideCameraPermissionTopDialog()
pendingCameraAction = null
super.onDestroyView()
}
override fun initData() {
if (!CKApp.app.isUserLoggedIn) {
resetLoggedOutState()
return
}
loadedLoginToken = CKApp.app.token
var data = PreferencesUtils.getString(activity!!, Const.PET_DATA, "")
if (!TextUtils.isEmpty(data)) {
@@ -222,18 +354,9 @@ class HomeFragment : CKBaseFragment() {
}
if (isFirstShow) {
getPetList()
}
getPetList()
LiveDataBus.get().with("FIRST", Int::class.java)
.observe(viewLifecycleOwner, object : Observer<Int?> {
override fun onChanged(result: Int?) {
getPetList2()
}
})
}
fun takePhoto() {
@@ -275,22 +398,15 @@ class HomeFragment : CKBaseFragment() {
}
fun getData(id: String) {
if (!CKApp.app.isUserLoggedIn) return
TTHttpClient.get(activity as TTBaseActivity, "首页", TTRequestParam().apply {
add(TTRequestData("patId", id))
}, object : ICallback {
override fun onSuccess(data: String) {
if (!CKApp.app.isUserLoggedIn) return
homeModel = Gson().fromJson<HomeModel>(data, HomeModel::class.java)
bannarData = homeModel?.banners!!
imageAdapter = ImageAdapter(activity!!, bannarData, object : OnItemClickListenner {
override fun onClick(pos: Int, model: Any) {
}
})
homeBinding.banner.addBannerLifecycleObserver(this@HomeFragment)//添加生命周期观察者
.setAdapter(imageAdapter)
.setIndicator(CircleIndicator(activity));
setBannerData(homeModel?.banners.orEmpty())
}
@@ -299,9 +415,9 @@ class HomeFragment : CKBaseFragment() {
}
})
TTHttpClient.get(activity as TTBaseActivity, "设备信息", TTRequestParam().apply { add(TTRequestData("patId",id)) },object : ICallback{
override fun onSuccess(data: String) {
if (!CKApp.app.isUserLoggedIn) return
var model = Gson().fromJson<HomeModel.EquipmentModel>(data, HomeModel.EquipmentModel::class.java)
homeModel?.equipment = model
@@ -395,7 +511,53 @@ class HomeFragment : CKBaseFragment() {
})
}
private fun performCameraAction(action: CameraAction?) {
when (action) {
CameraAction.SCAN -> startScan()
CameraAction.TAKE_PHOTO -> takePhoto()
null -> Unit
}
}
private fun showCameraSettingsDialog() {
CKAlertDialog(
requireActivity(),
getString(R.string.settting_permissions),
getString(R.string.camera_permission_settings_alert),
object : CKAlertDialog.AlertClickListener {
override fun onBack(isConfirm: Boolean) {
if (isConfirm) {
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:${requireContext().packageName}")
)
cameraSettingsLauncher.launch(intent)
} else {
pendingCameraAction = null
}
}
}
).show()
}
private fun setBannerData(data: List<BannarModel>) {
bannarData = data
if (::imageAdapter.isInitialized) {
imageAdapter.mDatas = bannarData
homeBinding.banner.setDatas(bannarData)
return
}
imageAdapter = ImageAdapter(activity!!, bannarData, object : OnItemClickListenner {
override fun onClick(pos: Int, model: Any) = Unit
})
homeBinding.banner.addBannerLifecycleObserver(this@HomeFragment)
.setAdapter(imageAdapter)
.setIndicator(CircleIndicator(activity))
}
fun switchPet() {
if (!CKApp.app.isUserLoggedIn) return
LiveDataBus.get().with("BOUND" + dataModel!!.id, Int::class.java)
@@ -431,6 +593,7 @@ class HomeFragment : CKBaseFragment() {
}
fun getPetInfo(id: String) {
if (!CKApp.app.isUserLoggedIn) return
TTHttpClient.get(activity as TTBaseActivity, "宠物档案", id, object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<CollarTypeModel.TypeDataModel>(
@@ -517,6 +680,7 @@ class HomeFragment : CKBaseFragment() {
fun getPetList() {
if (!CKApp.app.isUserLoggedIn) return
showProgeassDialog(getString(R.string.search))
TTHttpClient.get(activity as TTBaseActivity, "宠物列表", TTRequestParam().apply {
add(
@@ -529,43 +693,31 @@ class HomeFragment : CKBaseFragment() {
override fun onSuccess(data: String) {
hiddenProgeassDialog()
if (!CKApp.app.isUserLoggedIn) return
var model = Gson().fromJson<CollarTypeModel>(data, CollarTypeModel::class.java)
collarDatas = model.rows
if (!isFirstShow) {
collarDatas = model.rows
homeBinding.btnRotate?.rotateDownward()
CKCollarTypePopView(
activity!!,
homeBinding.textName!!,
collarDatas,
object : PopupWindow.OnDismissListener {
override fun onDismiss() {
homeBinding.btnRotate?.rotateUpwards()
}
}).show(object : CKCollarTypePopView.OnItemDataClickListenner {
override fun onClick(
pos: Int,
model: CollarTypeModel.TypeDataModel
) {
dataModel = model
homeBinding.textName?.setText(model.patName)
switchPet()
dataModel?.let {
PreferencesUtils.putString(
activity!!,
Const.PET_DATA,
Gson().toJson(dataModel)
)
}
}
})
} else {
if (model.rows.isEmpty()) {
isFirstShow = false
if (model.rows.size > 0) {
dataModel = model.rows.get(0)
homeBinding.textName?.setText(dataModel?.patName)
dataModel = null
homeBinding.textName?.text = getString(R.string.choost_pet)
PreferencesUtils.putString(activity!!, Const.PET_DATA, "")
return
}
if (isFirstShow) {
isFirstShow = false
dataModel = model.rows.first()
homeBinding.textName?.text = dataModel?.patName
switchPet()
} else {
val currentPet = model.rows.firstOrNull { it.id == dataModel?.id }
if (currentPet != null) {
dataModel = currentPet
homeBinding.textName?.text = currentPet.patName
} else if (model.rows.isNotEmpty()) {
dataModel = model.rows.first()
homeBinding.textName?.text = dataModel?.patName
switchPet()
}
}
@@ -579,8 +731,36 @@ class HomeFragment : CKBaseFragment() {
})
}
private fun showPetSelector() {
homeBinding.btnRotate?.rotateDownward()
CKCollarTypePopView(
activity!!,
homeBinding.textName!!,
collarDatas,
object : PopupWindow.OnDismissListener {
override fun onDismiss() {
homeBinding.btnRotate?.rotateUpwards()
}
}).show(object : CKCollarTypePopView.OnItemDataClickListenner {
override fun onClick(
pos: Int,
model: CollarTypeModel.TypeDataModel
) {
dataModel = model
homeBinding.textName?.text = model.patName
switchPet()
PreferencesUtils.putString(
activity!!,
Const.PET_DATA,
Gson().toJson(dataModel)
)
}
})
}
fun getPetList2() {
if (!CKApp.app.isUserLoggedIn) return
showProgeassDialog(getString(R.string.search))
TTHttpClient.get(activity as TTBaseActivity, "宠物列表", TTRequestParam().apply {
add(
@@ -593,6 +773,7 @@ class HomeFragment : CKBaseFragment() {
override fun onSuccess(data: String) {
hiddenProgeassDialog()
if (!CKApp.app.isUserLoggedIn) return
var model = Gson().fromJson<CollarTypeModel>(data, CollarTypeModel::class.java)
if (model.rows.size == 1) {
@@ -615,12 +796,31 @@ class HomeFragment : CKBaseFragment() {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 55 && resultCode == RESULT_OK) {
var path = PreferencesUtils.getString(activity!!, Const.IMAGE_PATH, "")
uploadImage(path!!)
val path = PreferencesUtils.getString(activity!!, Const.IMAGE_PATH, "")
if (!path.isNullOrBlank()) {
imageUploadInProgress = true
LubanCompressUtil.getCompressFile(
requireContext(),
File(path),
object : LubanCompressUtil.CompressCallback {
override fun success(file: File) {
if (!isAdded) return
uploadImage(file.absolutePath)
}
override fun fail(failMsg: String) {
if (!isAdded) return
imageUploadInProgress = false
toast(failMsg)
}
}
)
}
}
}
fun uploadImage(path: String) {
if (!CKApp.app.isUserLoggedIn) return
var param = TTRequestParam()
param.files.add(File(path))
@@ -628,6 +828,11 @@ class HomeFragment : CKBaseFragment() {
showProgeassDialog(getString(R.string.see))
TTHttpClient.postFile(activity as TTBaseActivity, "上传照片s", param, object : ICallback {
override fun onSuccess(data: String) {
if (!CKApp.app.isUserLoggedIn) {
imageUploadInProgress = false
hiddenProgeassDialog()
return
}
var model = Gson().fromJson<UploadModel>(data, UploadModel::class.java)
//aiPhoto(ActionCode.BASE_URL + "/" + model.fileNames)
@@ -635,6 +840,7 @@ class HomeFragment : CKBaseFragment() {
}
override fun onFailed(code: String, msg: String) {
imageUploadInProgress = false
hiddenProgeassDialog()
toast(msg)
}
@@ -642,12 +848,15 @@ class HomeFragment : CKBaseFragment() {
}
fun aiPhoto(imageUrl: String) {
if (!CKApp.app.isUserLoggedIn) return
TTHttpClient.postJson(activity as TTBaseActivity, "AI拍照识别", TTRequestParam().apply {
add(TTRequestData("imageUrl", imageUrl))
}, object : ICallback {
override fun onSuccess(data: String) {
imageUploadInProgress = false
hiddenProgeassDialog()
if (!CKApp.app.isUserLoggedIn) return
var model = Gson().fromJson<AIPhotoModel>(data, AIPhotoModel::class.java)
if ("".equals(model.species) || "".equals(model.species)) {
@@ -665,6 +874,7 @@ class HomeFragment : CKBaseFragment() {
}
override fun onFailed(code: String, msg: String) {
imageUploadInProgress = false
hiddenProgeassDialog()
toast(msg)
}
@@ -705,11 +915,62 @@ class HomeFragment : CKBaseFragment() {
override fun onStart() {
super.onStart()
homeBinding.banner.start();
if (!CKApp.app.isUserLoggedIn) {
waitingForLoginResult = false
resetLoggedOutState()
return
}
if (waitingForLoginResult) {
waitingForLoginResult = false
if (CKApp.app.isUserLoggedIn) {
getPetList()
}
}
dataModel?.let {
getData(dataModel!!.id.toString())
}
}
override fun onResume() {
super.onResume()
if (CKApp.app.isUserLoggedIn && loadedLoginToken != CKApp.app.token) {
initData()
} else if (CKApp.app.isUserLoggedIn && shouldRefreshPetListOnResume && !imageUploadInProgress) {
getPetList()
}
shouldRefreshPetListOnResume = false
}
override fun onPause() {
super.onPause()
shouldRefreshPetListOnResume = true
}
private fun resetLoggedOutState() {
loadedLoginToken = null
collarDatas = emptyList()
bannarData = emptyList()
dataModel = null
homeModel = null
isFirstShow = false
homeBinding.textName?.text = getString(R.string.choost_pet)
homeBinding.btnBound?.text = getString(R.string.unbound)
homeBinding.textHealthPoints?.text = "--"
homeBinding.points?.visibility = View.GONE
homeBinding.textStep?.text = "-"
homeBinding.textAddress?.text = getString(R.string.no_position)
homeBinding.textDistance?.text = "-"
homeBinding.textPowerValue?.text = "%"
homeBinding.imagePower?.setImageResource(R.mipmap.image_power_0)
homeBinding.contentPower?.visibility = View.GONE
setNoDeviceUI()
setBannerData(listOf(BannarModel().apply {
localImageRes = R.mipmap.index_banner1
}))
homeBinding.refreshLayout?.finishRefresh()
}
override fun onStop() {
super.onStop()
homeBinding.banner.stop();
@@ -749,8 +1010,11 @@ class HomeFragment : CKBaseFragment() {
position: Int,
size: Int
) {
val imageSource: Any? = data?.localImageRes
?.takeIf { it != 0 }
?: data?.bannerUrl
Glide.with(context)
.load(data?.bannerUrl)
.load(imageSource)
.apply(RequestOptions.bitmapTransform(RoundedCorners(12)))
.placeholder(R.mipmap.image_bannar_item)
.into(holder!!.imageView)

View File

@@ -17,6 +17,7 @@ import com.ck.ckcollar.app.ui.act.deposit.DepositActivity
import com.ck.ckcollar.app.ui.act.deposit.StartDepositActivity
import com.ck.ckcollar.app.ui.act.member.MemberActivity
import com.ck.ckcollar.app.ui.act.message.MessageCenterActivity
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.act.mine.FeedBackActivity
import com.ck.ckcollar.app.ui.act.mine.ReturnDeviceActivity
import com.ck.ckcollar.app.ui.act.points.PointsActivity
@@ -51,19 +52,24 @@ class MineFragment : CKBaseFragment() {
override fun initView() {
binding.contentBalance.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(BalanceActivity::class.java, DataCollection())
}
binding.conetntPoints.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(PointsActivity::class.java, DataCollection())
}
binding.imageUser.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(UserInfoActivity::class.java, DataCollection())
}
binding.btnUserInfo.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(UserInfoActivity::class.java, DataCollection())
}
binding.btnMessage.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MessageCenterActivity::class.java, DataCollection())
}
@@ -71,19 +77,24 @@ class MineFragment : CKBaseFragment() {
toActivity(SettingActivity::class.java, DataCollection())
}
binding.btnOpen.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MemberActivity::class.java, DataCollection())
}
binding.btnReturnDevice.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(ReturnDeviceActivity::class.java, DataCollection())
}
binding.btnFeedback.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(FeedBackActivity::class.java, DataCollection())
}
binding.btnCustomerService.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(PeopleCustomerActivity::class.java, DataCollection().apply { add(Data("chatType","2")) })
}
binding.textDeposit.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
if ("0".equals(CKApp.app.userInfo!!.depositStatus)){
toActivity(DepositActivity::class.java, DataCollection())
}else{
@@ -92,18 +103,23 @@ class MineFragment : CKBaseFragment() {
}
binding.btnAllOrder.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","0")) })
}
binding.btnOrderPayment.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","1")) })
}
binding.btnOrderDelivery.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","2")) })
}
binding.btnOrderReceipt.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","3")) })
}
binding.btnOrderAfterSales.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","4")) })
}
@@ -111,20 +127,39 @@ class MineFragment : CKBaseFragment() {
}
private fun requireLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
toActivity(LoginActivity::class.java, DataCollection())
return false
}
override fun initData() {
if (!CKApp.app.isUserLoggedIn) {
renderLoggedOutState()
binding.textNick.text = getString(R.string.guest)
binding.textPhone.text = "用户未登录"
binding.textPhone.visibility = View.VISIBLE
binding.textBalance.text = "0"
binding.textPoints.text = "0"
binding.textCount.visibility = View.INVISIBLE
binding.imageUser.setImageResource(R.mipmap.icon_unlogin_defaul)
return
}
binding.textNick.text = CKApp.app.userInfo?.custName
binding.textPhone.text = CKApp.app.userInfo?.phonenumber
binding.textPhone.visibility = View.VISIBLE
if (!TextUtils.isEmpty(CKApp.app.userInfo?.avatar)){
Glide.with(this@MineFragment)
.load( CKApp.app.userInfo?.avatar)
.placeholder(R.mipmap.image_user_default)
.placeholder(R.mipmap.icon_unlogin_defaul)
.into(binding.imageUser)
}
binding.textBalance.text = CKApp.app.userInfo?.balance.toString()
binding.textPoints.text = CKApp.app.userInfo?.points.toString()
binding.textBalance.text = CKApp.app.userInfo?.balance?.toString() ?: "0"
binding.textPoints.text = CKApp.app.userInfo?.points?.toString() ?: "0"
if (CKApp.app.userInfo?.isMember == true){
binding.btnOpen.setText(getString(R.string.auto_renewal2))
@@ -135,6 +170,7 @@ class MineFragment : CKBaseFragment() {
}else{
binding.btnOpen.setText(getString(R.string.open_member))
binding.textMemberTitle.text = getString(R.string.pet_member)
binding.textMemberDate.text = getString(R.string.pet_member_rewards)
}
@@ -147,11 +183,36 @@ class MineFragment : CKBaseFragment() {
override fun onResume() {
super.onResume()
getUserInfo()
initData()
if (CKApp.app.isUserLoggedIn) {
getUserInfo()
}
}
fun getUserInfo(){
private fun renderLoggedOutState() {
binding.textNick.text = getString(R.string.guest)
binding.textPhone.text = getString(R.string.user_not_logged_in)
binding.textPhone.visibility = View.VISIBLE
binding.textBalance.text = "0"
binding.textPoints.text = "0"
binding.textCount.text = "0"
binding.textCount.visibility = View.INVISIBLE
binding.btnOpen.text = getString(R.string.open_member)
binding.textMemberTitle.text = getString(R.string.pet_member)
binding.textMemberDate.text = getString(R.string.pet_member_rewards)
Glide.with(this).clear(binding.imageUser)
binding.imageUser.setImageResource(R.mipmap.icon_unlogin_defaul)
}
private fun getUserInfo(){
if (!CKApp.app.isUserLoggedIn) {
binding.textCount.visibility = View.INVISIBLE
return
}
TTHttpClient.get(activity as TTBaseActivity,"个人资料", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
if (!CKApp.app.isUserLoggedIn) return
CKApp.app.userInfo = Gson().fromJson<UserInfoModel>(data, UserInfoModel::class.java)
PreferencesUtils.putString(activity!!, Const.USER_DATA,data)
@@ -168,6 +229,7 @@ class MineFragment : CKBaseFragment() {
add(TTRequestData("accept","true"))
},object : ICallback{
override fun onSuccess(data: String) {
if (!CKApp.app.isUserLoggedIn) return
var model = Gson().fromJson<UnReadModel>(data, UnReadModel::class.java)
binding.textCount.text = model.unreadCount.toString()

View File

@@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.action.ActionCode
import com.ck.ckcollar.app.databinding.FragmentShopBinding
@@ -134,10 +135,12 @@ class ShopFragment : CKBaseFragment() {
}
binding.btnShopCar.setOnClickListener {
if (!isUserLogin()) return@setOnClickListener
toActivity(ShopCarActivity::class.java, DataCollection())
}
binding.btnShopOrder.setOnClickListener {
if (!isUserLogin()) return@setOnClickListener
toActivity(MyOrderActivity::class.java, DataCollection().apply { add(Data("pos","0")) })
}
@@ -154,7 +157,7 @@ class ShopFragment : CKBaseFragment() {
getHot()
page = 1
getData()
getBannar()
// getBannar()
}
override fun onLoadMore(refreshLayout: RefreshLayout) {
@@ -299,6 +302,11 @@ class ShopFragment : CKBaseFragment() {
}
fun getCarCount() {
if (!CKApp.app.isUserLoggedIn) {
binding.textCount.visibility = View.GONE
return
}
TTHttpClient.get(
activity as TTBaseActivity,
"购物车数量和合计",

View File

@@ -91,11 +91,7 @@ class BoundPhoneActivity : CKBaseActivity() {
override fun onSuccess(data: String) {
var token = JSONObject(data).getString("token")
PreferencesUtils.putString(
this@BoundPhoneActivity,
Const.TOKEN, token
)
CKApp.app.token = token
CKApp.app.setLoginToken(token)

View File

@@ -12,10 +12,12 @@ import android.text.style.ClickableSpan
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.ActivityLoginBinding
import com.ck.ckcollar.app.model.LoginModel
import com.ck.ckcollar.app.model.UserInfoModel
import com.ck.ckcollar.app.ui.act.home.MainActivity
import com.ck.ckcollar.app.ui.act.setting.SettingWebViewActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.networkone.ICallback
@@ -32,7 +34,7 @@ import com.tt.kit.model.DataCollection
import com.tt.kit.utils.TTUtils
/**
* 登录
* 登录..
*/
class LoginActivity : CKBaseActivity() {
@@ -51,7 +53,7 @@ class LoginActivity : CKBaseActivity() {
// api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, false);
setStatusBarColor(R.attr.mainBackground)
//检测权限
checkAppPermissions()
// checkAppPermissions()
}
@@ -67,6 +69,9 @@ class LoginActivity : CKBaseActivity() {
binding.btnLogin.setOnClickListener {
login()
}
binding.btnQuickLogin.setOnClickListener {
quickLogin()
}
binding.btnCodeLogin.setOnClickListener {
binding.btnCodeLogin.setTextColor(getColor2(R.attr.appTextColor))
@@ -124,8 +129,11 @@ class LoginActivity : CKBaseActivity() {
var clickableSpan2 = object : ClickableSpan() {
override fun onClick(v: View) {
toActivity(
AgreementActivity::class.java,
DataCollection().apply { add(Data("title", "隐私政策")) })
SettingWebViewActivity::class.java,
DataCollection().apply {
add(Data("url", Config.PRIVACY_POLICY))
add(Data("title", getString(R.string.privacy_agreement)))
})
}
override fun updateDrawState(ds: TextPaint) {
@@ -146,7 +154,6 @@ class LoginActivity : CKBaseActivity() {
binding.btnCheck.setOnClickListener {
isCheck = !isCheck
setCheckUI()
PreferencesUtils.putBoolean(this@LoginActivity, Const.AGREEMENT_CHECK, isCheck)
}
binding.btnSendCode.setOnClickListener {
@@ -154,6 +161,10 @@ class LoginActivity : CKBaseActivity() {
sendCode()
}
binding.btnWX.setOnClickListener {
if (!isCheck) {
toast(getString(R.string.alert_agreement))
return@setOnClickListener
}
val req = SendAuth.Req()
req.scope = "snsapi_userinfo" // 只能填 snsapi_userinfo
req.state = "wechat_sdk_demo_test"
@@ -232,11 +243,7 @@ class LoginActivity : CKBaseActivity() {
try {
var model = Gson().fromJson<LoginModel>(data, LoginModel::class.java)
PreferencesUtils.putString(
this@LoginActivity,
Const.TOKEN, model.token.toString()
)
CKApp.app.token = model.token.toString()
CKApp.app.setLoginToken(model.token.orEmpty())
getUserInfo()
} catch (e: Exception) {
@@ -262,11 +269,7 @@ class LoginActivity : CKBaseActivity() {
try {
var model = Gson().fromJson<LoginModel>(data, LoginModel::class.java)
PreferencesUtils.putString(
this@LoginActivity,
Const.TOKEN, model.token.toString()
)
CKApp.app.token = model.token.toString()
CKApp.app.setLoginToken(model.token.orEmpty())
getUserInfo()
} catch (e: Exception) {
@@ -285,15 +288,44 @@ class LoginActivity : CKBaseActivity() {
}
private fun quickLogin() {
showProgeassDialog(getString(R.string.login))
TTHttpClient.postJson(this, "登录", TTRequestParam().apply {
add(TTRequestData("username", "13305376054"))
add(TTRequestData("password", "000000"))
add(TTRequestData("code", ""))
add(TTRequestData("uuid", TTUtils.getDeviceId(this@LoginActivity).toString()))
}, object : ICallback {
override fun onSuccess(data: String) {
try {
val model = Gson().fromJson<LoginModel>(data, LoginModel::class.java)
CKApp.app.setLoginToken(model.token.orEmpty())
getUserInfo()
} catch (e: Exception) {
hiddenProgeassDialog()
toast(e.message.orEmpty())
}
}
override fun onFailed(code: String, msg: String) {
hiddenProgeassDialog()
toast(msg)
}
})
}
fun getUserInfo(){
TTHttpClient.get(this,"个人资料", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
CKApp.app.userInfo = Gson().fromJson<UserInfoModel>(data, UserInfoModel::class.java)
PreferencesUtils.putString(this@LoginActivity, Const.USER_DATA,data)
toActivity(MainActivity::class.java, DataCollection())
startActivity(
Intent(this@LoginActivity, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
putExtra(MainActivity.EXTRA_SHOW_HOME, true)
}
)
finish()
}
@@ -308,7 +340,9 @@ class LoginActivity : CKBaseActivity() {
binding.editLoginName.setText(PreferencesUtils.getString(this, Const.LOGIN_NAME, ""))
isCheck = PreferencesUtils.getBolean(this, Const.AGREEMENT_CHECK, false) as Boolean
// Agreement consent is intentionally session-local: users must confirm it on every login.
isCheck = false
setCheckUI()
}
@@ -324,6 +358,12 @@ class LoginActivity : CKBaseActivity() {
}
fun wxLogin(code : String){
if (!isCheck) {
toast(getString(R.string.alert_agreement))
return
}
showProgeassDialog(getString(R.string.login))
TTHttpClient.postJson(this,"微信登录", TTRequestParam().apply { add(TTRequestData("code",code)) },object : ICallback{
override fun onSuccess(data: String) {
@@ -338,11 +378,7 @@ class LoginActivity : CKBaseActivity() {
finish()
}else{
PreferencesUtils.putString(
this@LoginActivity,
Const.TOKEN, model.token.toString()
)
CKApp.app.token = model.token.toString()
CKApp.app.setLoginToken(model.token.orEmpty())
getUserInfo()
}

View File

@@ -122,11 +122,7 @@ class RegisterActivity : CKBaseActivity() {
try {
var model = Gson().fromJson<LoginModel>(data, LoginModel::class.java)
PreferencesUtils.putString(
this@RegisterActivity,
Const.TOKEN, model.token.toString()
)
CKApp.app.token = model.token.toString()
CKApp.app.setLoginToken(model.token.orEmpty())
getUserInfo()
} catch (e: Exception) {

View File

@@ -8,45 +8,42 @@ import android.widget.ImageButton
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityMemberBinding
import com.ck.ckcollar.app.model.MemberModel
import com.ck.ckcollar.app.model.MemberPriceModel
import com.ck.ckcollar.app.model.PointsModel
import com.ck.ckcollar.app.model.RecordModel
import com.ck.ckcollar.app.model.UserInfoModel
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.MemberPriceCache
import com.ck.ckcollar.app.utils.networkone.ICallback
import com.ck.ckcollar.app.utils.networkone.TTHttpClient
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.cyclone.shadowsocks.utils.PreferencesUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.gyf.immersionbar.ImmersionBar
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.model.DataCollection
class MemberActivity : CKBaseActivity() {
override val useDarkStatusBarIcons: Boolean = false
lateinit var binding: ActivityMemberBinding
private var priceRequestInFlight = false
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityMemberBinding.inflate(layoutInflater)
setContentView(binding.root)
// ImmersionBar.with(this)
// .statusBarDarkFont(true)
// .init()
super.onCreate(savedInstanceState)
setActionBarColor(resources.getColor(R.color.color_252132))
setStatusBarColor2(resources.getColor(R.color.color_252132))
findViewById<ImageButton>(R.id.btnLeft).imageTintList = ColorStateList.valueOf(Color.WHITE)
findViewById<ImageButton>(R.id.btnLeft).imageTintList =
ColorStateList.valueOf(Color.WHITE)
setTitleTextColor(resources.getColor(R.color.white))
setTitleText(getString(R.string.member))
}
override fun initView() {
binding.btnOpen.setOnClickListener {
toActivity(OpenMemberActivity::class.java, DataCollection())
}
@@ -56,8 +53,7 @@ class MemberActivity : CKBaseActivity() {
}
override fun initData() {
renderMemberInfo(CKApp.app.userInfo)
}
override fun onStart() {
@@ -65,45 +61,78 @@ class MemberActivity : CKBaseActivity() {
getMemberInfo()
}
fun getInfo() {
TTHttpClient.get(this, "会员套餐列表", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<List<MemberPriceModel>>(data, object : TypeToken<List<MemberPriceModel>>(){}.type)
private fun loadMemberPrice() {
val cachedPrice = MemberPriceCache.getPrice(this)
if (cachedPrice != null) {
showMemberPrice(cachedPrice)
}
if (model.size > 0){
binding.textMoney.text = model.get(0).actualPrice.toString()
}
}
override fun onFailed(code: String, msg: String) {
toast(msg)
}
})
if (cachedPrice == null || MemberPriceCache.isExpired(this)) {
getInfo(cachedPrice != null)
}
}
fun getMemberInfo(){
TTHttpClient.get(this,"个人资料", TTRequestParam(),object : ICallback{
private fun getInfo(hasCachedPrice: Boolean) {
if (priceRequestInFlight) return
priceRequestInFlight = true
TTHttpClient.get(this, "会员套餐列表", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<UserInfoModel>(data, UserInfoModel::class.java)
priceRequestInFlight = false
val model = Gson().fromJson<List<MemberPriceModel>>(
data,
object : TypeToken<List<MemberPriceModel>>() {}.type
)
if (model.isMember == true){
binding.textMemberDate.text = getString(R.string.deadline_date, CKUtils.dataToFormat(model.deadlineTime,
CKUtils.YYYYMMDD_HHMMSS, CKUtils.YYYYMMDD))
binding.conetnt1.visibility = View.GONE
binding.conetnt2.visibility = View.VISIBLE
}else{
binding.conetnt1.visibility = View.VISIBLE
binding.conetnt2.visibility = View.GONE
getInfo()
if (model.isNotEmpty()) {
val price = model.first().actualPrice
MemberPriceCache.save(this@MemberActivity, price)
showMemberPrice(price)
}
}
override fun onFailed(code: String, msg: String) {
priceRequestInFlight = false
if (!hasCachedPrice) toast(msg)
}
})
}
private fun showMemberPrice(price: Double) {
binding.textMoney.text = price.toString()
binding.priceContent.visibility = View.VISIBLE
}
private fun getMemberInfo() {
TTHttpClient.get(this, "个人资料", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
val model = Gson().fromJson<UserInfoModel>(data, UserInfoModel::class.java)
CKApp.app.userInfo = model
PreferencesUtils.putString(this@MemberActivity, Const.USER_DATA, data)
renderMemberInfo(model)
}
override fun onFailed(code: String, msg: String) {
toast(msg)
}
})
}
private fun renderMemberInfo(model: UserInfoModel?) {
if (model?.isMember == true) {
binding.textMemberDate.text = getString(
R.string.deadline_date,
CKUtils.dataToFormat(
model.deadlineTime,
CKUtils.YYYYMMDD_HHMMSS,
CKUtils.YYYYMMDD
)
)
binding.conetnt1.visibility = View.GONE
binding.conetnt2.visibility = View.VISIBLE
} else {
binding.conetnt1.visibility = View.VISIBLE
binding.conetnt2.visibility = View.GONE
loadMemberPrice()
}
}
}

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.member
import android.content.pm.ApplicationInfo
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
@@ -8,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.alipay.sdk.app.EnvUtils
import com.alipay.sdk.app.PayTask
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityMemberBinding
@@ -19,14 +21,19 @@ import com.ck.ckcollar.app.model.PayResult
import com.ck.ckcollar.app.ui.act.shop.PayPasswordBottomFragment
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.base.CKLHBaseActionBarActivity
import com.ck.ckcollar.app.ui.act.balance.RechargeActivity
import com.ck.ckcollar.app.utils.Const
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
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.gyf.immersionbar.ImmersionBar
import com.tt.kit.utils.TTUtils
import com.tt.kit.utils.LiveDataBus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -38,7 +45,7 @@ class OpenMemberActivity : CKLHBaseActionBarActivity() {
var data = listOf<MemberPriceModel>()
var pos = -1
var isAuto = false
var payType = "1"
var payType = "0"
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityOpenMemberBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -53,6 +60,17 @@ class OpenMemberActivity : CKLHBaseActionBarActivity() {
override fun initView() {
LiveDataBus.get().with(RechargeActivity.WX_PAY_RESULT, Int::class.java).observe(this) { result ->
when (result) {
0 -> {
toast(getString(R.string.payment_success))
finish()
}
-2 -> toast("已取消微信支付")
else -> toast(getString(R.string.payment_faild))
}
}
//setPriceUI()
binding.btnAli.setOnClickListener {
@@ -134,6 +152,8 @@ class OpenMemberActivity : CKLHBaseActionBarActivity() {
var model = Gson().fromJson<MemberModel>(data, MemberModel::class.java)
if ("1".equals(payType)){
aliPay(model.orderStr!!)
}else if ("0".equals(payType)) {
wxPay(model)
}else{
toast(getString(R.string.payment_success))
finish()
@@ -220,7 +240,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)
@@ -239,4 +262,49 @@ class OpenMemberActivity : CKLHBaseActionBarActivity() {
}
}
}
private fun wxPay(model: MemberModel) {
val requiredValues = listOf(
model.appId,
model.partnerId,
model.prepayId,
model.packageVal,
model.nonceStr,
model.timeStamp,
model.paySign
)
if (requiredValues.any { it.isNullOrBlank() }) {
hiddenProgeassDialog()
toast("微信支付参数不完整,请检查商户号等支付参数")
return
}
if (model.appId != Const.WX_APP_ID) {
hiddenProgeassDialog()
toast("微信支付 AppID 不匹配")
return
}
val api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, true)
api.registerApp(Const.WX_APP_ID)
if (!api.isWXAppInstalled) {
hiddenProgeassDialog()
toast("请先安装微信")
return
}
val request = PayReq().apply {
appId = model.appId
partnerId = model.partnerId
prepayId = model.prepayId
packageValue = model.packageVal
nonceStr = model.nonceStr
timeStamp = model.timeStamp
sign = model.paySign
extData = model.orderNo
}
hiddenProgeassDialog()
if (!api.sendReq(request)) {
toast("微信支付拉起失败")
}
}
}

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.mine
import android.Manifest
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -10,24 +11,23 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import android.widget.AdapterView
import android.widget.BaseAdapter
import android.widget.ImageView
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityFeedBackBinding
import com.ck.ckcollar.app.databinding.LayoutItemImage2Binding
import com.ck.ckcollar.app.databinding.LayoutItemImageBinding
import com.ck.ckcollar.app.model.ImageModel
import com.ck.ckcollar.app.model.UploadModel
import com.ck.ckcollar.app.ui.act.petmanager.AddPetActivity
import com.ck.ckcollar.app.ui.act.petmanager.AddPetActivity.ImageAdapter
import com.ck.ckcollar.app.ui.act.petmanager.ImageViewActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.ui.dialog.CKPhotoDialog
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -36,8 +36,10 @@ import com.cyclone.shadowsocks.utils.PreferencesUtils
import com.google.gson.Gson
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.TTUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class FeedBackActivity : CKBaseActivity() {
@@ -50,7 +52,7 @@ class FeedBackActivity : CKBaseActivity() {
binding = ActivityFeedBackBinding.inflate(layoutInflater)
setContentView(binding.root)
super.onCreate(savedInstanceState)
checkAppPermissions()
// checkAppPermissions()
setTitleText(getString(R.string.feedback))
}
@@ -84,13 +86,30 @@ class FeedBackActivity : CKBaseActivity() {
})
return
}
//相册
val i = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
i.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");
startActivityForResult(i, 56)
CKPhotoDialog(
this@FeedBackActivity,
object : CKPhotoDialog.PhotoClickListener {
override fun onBack(mark: Int, position: Int) {
try {
if (mark == 0) {
if (!checkCameraPermissions()) {
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
return
}
takePhoto()
} else {
openAlbum()
}
} catch (e: Exception) {
e.printStackTrace()
toast(getString(R.string.read_photo_failed))
}
}
}
).show()
}
}
@@ -105,6 +124,12 @@ class FeedBackActivity : CKBaseActivity() {
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
takePhoto()
}
}
fun upload(){
if (binding.editConten.isEmpty()){
@@ -162,11 +187,120 @@ class FeedBackActivity : CKBaseActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 56 && resultCode == RESULT_OK) {
var path = TTUtils.getImageUrl(this, data!!.getData());
this.data.add(this.data.size - 1,ImageModel().apply { this.path = path })
imageAdapter.data = this.data
imageAdapter.notifyDataSetChanged()
if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
val path = PreferencesUtils.getString(this, Const.IMAGE_PATH, "").orEmpty()
compressAndAddImage(path)
} else if (requestCode == REQUEST_ALBUM && resultCode == RESULT_OK) {
data?.data?.let(::handleSelectedImage)
?: toast(getString(R.string.read_photo_failed))
}
}
private fun openAlbum() {
val intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).apply {
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
}
startActivityForResult(intent, REQUEST_ALBUM)
}
private fun handleSelectedImage(uri: Uri) {
lifecycleScope.launch {
val path = withContext(Dispatchers.IO) {
copySelectedImageToCache(uri)
}
if (path == null) {
toast(getString(R.string.read_photo_failed))
return@launch
}
compressAndAddImage(path)
}
}
private fun compressAndAddImage(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
data.add(data.size - 1, ImageModel().apply {
this.path = file.absolutePath
})
imageAdapter.data = data
imageAdapter.notifyDataSetChanged()
}
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)
?.takeIf { it.isNotBlank() }
?: "jpg"
val imageDir = File(cacheDir, "feedback_images")
if (!imageDir.exists() && !imageDir.mkdirs()) return null
val target = File(imageDir, System.currentTimeMillis().toString() + "." + extension)
return try {
val input = contentResolver.openInputStream(uri) ?: return null
input.use { source ->
target.outputStream().use { output -> source.copyTo(output) }
}
target.absolutePath.takeIf { target.length() > 0L }
?: run {
target.delete()
null
}
} catch (e: Exception) {
target.delete()
e.printStackTrace()
null
}
}
private fun takePhoto() {
try {
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val imageFile = File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES),
System.currentTimeMillis().toString() + ".png"
)
imageFile.parentFile?.let { parent ->
if (!parent.exists()) parent.mkdirs()
}
PreferencesUtils.putString(this, Const.IMAGE_PATH, imageFile.absolutePath)
val cameraUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
this,
packageName + ".fileprovider",
imageFile
)
} else {
Uri.fromFile(imageFile)
}
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri)
cameraIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
startActivityForResult(cameraIntent, REQUEST_CAMERA)
} catch (e: Exception) {
e.printStackTrace()
toast(getString(R.string.read_photo_failed))
}
}
@@ -234,4 +368,10 @@ class FeedBackActivity : CKBaseActivity() {
}
}
companion object {
private const val REQUEST_CAMERA = 55
private const val REQUEST_ALBUM = 56
private const val REQUEST_CAMERA_PERMISSION = 1301
}
}

View File

@@ -23,7 +23,7 @@ class ReturnDeviceActivity : CKBaseActivity() {
binding = ActivityReturnDeviceBinding.inflate(layoutInflater)
setContentView(binding.root)
super.onCreate(savedInstanceState)
checkAppPermissions()
// checkAppPermissions()
setTitleText(getString(R.string.device_return))
setActionBarColor(getColor(R.color.white))
@@ -97,6 +97,12 @@ class ReturnDeviceActivity : CKBaseActivity() {
return
}
val phone = binding.textPhone.text.toString().trim()
if (!PHONE_PATTERN.matches(phone)) {
toast(getString(R.string.invalid_phone_format))
return
}
if (binding.textReason.isEmpty()){
toast(getString(R.string.input_return_reason))
return
@@ -124,4 +130,8 @@ class ReturnDeviceActivity : CKBaseActivity() {
}
})
}
companion object {
private val PHONE_PATTERN = Regex("^1[3-9]\\d{9}$")
}
}

View File

@@ -137,8 +137,8 @@ class LogisticsInfoActivity : CKBaseActivity() {
model = Gson().fromJson<OrderModel.Order>(getReceiveParam().get("data")!!.stringValue.toString(),
OrderModel.Order::class.java)
binding.textReceiveName.text = model.receiverName + " " + model.receiverPhone
getData()
binding.textReceiveName.text = model.receiverAddress + "\n" + model.receiverName + " " + model.receiverPhone
getData()
}

View File

@@ -160,6 +160,7 @@ class MyOrderActivity : CKBaseActivity() , View.OnClickListener{
fun getData() {
showProgeassDialog()
TTHttpClient.get(this, "我的订单", TTRequestParam().apply {
add(
TTRequestData("pageNum", page)
@@ -172,10 +173,10 @@ class MyOrderActivity : CKBaseActivity() , View.OnClickListener{
override fun onSuccess(data: String) {
var model = Gson().fromJson<OrderModel>(data, OrderModel::class.java)
if (page == 1) {
this@MyOrderActivity.data.clear()
}
model.rows?.let{
if (page ==1){
this@MyOrderActivity.data.clear()
}
if (this@MyOrderActivity.data.size != model.total){
this@MyOrderActivity.data.addAll(model.rows!!)
}
@@ -184,11 +185,17 @@ class MyOrderActivity : CKBaseActivity() , View.OnClickListener{
orderAdapter.data = this@MyOrderActivity.data
orderAdapter.notifyDataSetChanged()
val isEmpty = this@MyOrderActivity.data.isEmpty()
binding.relNoresult.visibility = if (isEmpty) View.VISIBLE else View.GONE
binding.recyclerView.visibility = if (isEmpty) View.GONE else View.VISIBLE
binding.refreshLayout.finishRefresh()
binding.refreshLayout.finishLoadMore(true)
}
override fun onFailed(code: String, msg: String) {
binding.refreshLayout.finishRefresh()
binding.refreshLayout.finishLoadMore(false)
toast(msg)
}
})

View File

@@ -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
@@ -20,27 +22,34 @@ import com.ck.ckcollar.app.model.AddressModel
import com.ck.ckcollar.app.model.ConfirmOrderModel
import com.ck.ckcollar.app.model.MemberModel
import com.ck.ckcollar.app.model.OrderModel
import com.ck.ckcollar.app.model.OrderPaymentModel
import com.ck.ckcollar.app.model.PayOrderModel
import com.ck.ckcollar.app.model.PayPasswordStatus
import com.ck.ckcollar.app.model.PayResult
import com.ck.ckcollar.app.model.UserInfoModel
import com.ck.ckcollar.app.ui.act.setting.PayPsswordActivity
import com.ck.ckcollar.app.ui.act.balance.RechargeActivity
import com.ck.ckcollar.app.ui.act.shop.OrderSuccessActivity
import com.ck.ckcollar.app.ui.act.shop.PayPasswordBottomFragment
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
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
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.google.gson.Gson
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.LiveDataBus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.math.BigDecimal
import java.math.RoundingMode
class PayOrderActivity : CKBaseActivity() {
@@ -54,12 +63,15 @@ class PayOrderActivity : CKBaseActivity() {
var memberModel: UserInfoModel? = null
var payType = "1"
var payType = "0"
var selectBalance = false
var selectPoints = false
var confirmOrderModel : ConfirmOrderModel? = null
lateinit var orderSource: String
private var isCancellingOrder = false
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityPayOrderBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -68,6 +80,14 @@ class PayOrderActivity : CKBaseActivity() {
}
override fun initView() {
LiveDataBus.get().with(RechargeActivity.WX_PAY_RESULT, Int::class.java).observe(this) { result ->
if (result == 0) {
handleWeChatPaymentSuccess()
} else {
handleWeChatPaymentFailed(result == -2)
}
}
productAdapter =
ProductAdapter(this@PayOrderActivity, productDatas, object : OnItemClickListenner {
override fun onClick(pos: Int, model: Any) {
@@ -82,13 +102,14 @@ class PayOrderActivity : CKBaseActivity() {
binding.btnAli.setOnClickListener(payClick)
binding.btnWX.setOnClickListener(payClick)
binding.btnBalance.setOnClickListener(payClick)
binding.btnPoints.setOnClickListener(payClick)
binding.btnBalance.setOnClickListener {
if (memberModel?.balance == 0.0){
if (clearBalanceIfPointsSufficient()) {
toast("积分足够无需使用余额")
return@setOnClickListener
}
if ((memberModel?.balance ?: 0.0) <= 0.0){
toast(getString(R.string.balance_over))
return@setOnClickListener
}
@@ -99,21 +120,28 @@ class PayOrderActivity : CKBaseActivity() {
}else{
binding.image3.setImageResource(R.mipmap.image_address_check_narmal)
}
clearExternalPaymentIfNotNeeded()
}
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)
}else{
binding.image4.setImageResource(R.mipmap.image_address_check_narmal)
}
clearBalanceIfPointsSufficient()
clearExternalPaymentIfNotNeeded()
}
binding.btnBuyNow.setOnClickListener {
if (selectBalance){
if (selectBalance && !canPointsCoverPayAmount()){
getPayPassword()
}else{
order("")
@@ -170,7 +198,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
}
@@ -204,17 +232,18 @@ class PayOrderActivity : CKBaseActivity() {
fun showPayBottomView(){
var showBalance = model.payAmount
var showBalance = BigDecimal.valueOf(model.payAmount).setScale(2, RoundingMode.HALF_UP)
if (selectPoints){
CKApp.app.userInfo?.intoAmounts?.let {
showBalance = showBalance - BigDecimal(memberModel?.points!!).multiply(
BigDecimal(memberModel!!.intoAmounts!!)).setScale(2).toDouble()
if (showBalance < 0.0){
showBalance = 0.00
}
calculatePointsDeduction()?.let { pointsDeduction ->
showBalance = showBalance.subtract(pointsDeduction)
.max(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP))
}
}
val userBalance = BigDecimal.valueOf(memberModel?.balance ?: 0.0)
.setScale(2, RoundingMode.HALF_UP)
showBalance = showBalance.min(userBalance)
var passwordBottomFragment = PayPasswordBottomFragment(showBalance.toString(), "余额",object : PayPasswordBottomFragment.PasswordCallBack{
override fun callBack(password: String) {
@@ -229,6 +258,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{
@@ -236,6 +270,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{
@@ -271,16 +310,25 @@ class PayOrderActivity : CKBaseActivity() {
)
}, object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<PayOrderModel>(data, PayOrderModel::class.java)
val paymentModel = Gson().fromJson(data, OrderPaymentModel::class.java)
if ("0".equals(this@PayOrderActivity.payType)) {
//微信
val weChatPayParams = paymentModel.wxMap
if (weChatPayParams == null || !weChatPayParams.isJsonObject) {
hiddenProgeassDialog()
toast("微信支付参数格式错误")
return
}
wxPay(Gson().fromJson(weChatPayParams, PayOrderModel::class.java))
}else if ("1".equals(this@PayOrderActivity.payType)) {
if (TextUtils.isEmpty(model.orderStr)){
val aliPayOrderStr = paymentModel.orderStr
?.takeIf { it.isJsonPrimitive && it.asJsonPrimitive.isString }
?.asString
if (TextUtils.isEmpty(aliPayOrderStr)){
toActivity(OrderSuccessActivity::class.java, DataCollection())
finish(DataCollection(),1002)
}else{
aliPay(model.orderStr!!)
aliPay(aliPayOrderStr!!)
}
}else{
toast(getString(R.string.order_success))
@@ -299,9 +347,143 @@ class PayOrderActivity : CKBaseActivity() {
})
}
fun aliPay(orderStr: String) {
GlobalScope.launch(Dispatchers.IO) {
private fun wxPay(model: PayOrderModel) {
val requiredValues = listOf(
model.appId,
model.partnerId,
model.prepayId,
model.packageVal,
model.nonceStr,
model.timeStamp,
model.paySign
)
if (requiredValues.any { it.isNullOrBlank() }) {
hiddenProgeassDialog()
toast("微信支付参数不完整,请检查商户号等支付参数")
return
}
if (model.appId != Const.WX_APP_ID) {
hiddenProgeassDialog()
toast("微信支付 AppID 不匹配")
return
}
val api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, true)
api.registerApp(Const.WX_APP_ID)
if (!api.isWXAppInstalled) {
hiddenProgeassDialog()
toast("请先安装微信")
return
}
val request = PayReq().apply {
appId = model.appId
partnerId = model.partnerId
prepayId = model.prepayId
packageValue = model.packageVal
nonceStr = model.nonceStr
timeStamp = model.timeStamp
sign = model.paySign
extData = model.orderNo
}
hiddenProgeassDialog()
if (!api.sendReq(request)) {
toast("微信支付拉起失败")
}
}
private fun handleWeChatPaymentSuccess() {
toast(getString(R.string.payment_success))
finish(DataCollection(), 1002)
}
private fun handleWeChatPaymentFailed(cancelled: Boolean) {
if (cancelled) {
cancelPendingOrder()
} else {
toast(getString(R.string.payment_faild))
}
}
private fun cancelPendingOrder() {
if (isCancellingOrder) return
isCancellingOrder = true
showProgeassDialog(getString(R.string.cancel))
TTHttpClient.post(this, "取消待支付订单", TTRequestParam().apply {
add(TTRequestData("orderId", model.id))
}, object : ICallback {
override fun onSuccess(data: String) {
hiddenProgeassDialog()
toast(getString(R.string.cancel_pay_cussess))
finish(DataCollection(), 1002)
}
override fun onFailed(code: String, msg: String) {
isCancellingOrder = false
hiddenProgeassDialog()
toast(msg)
}
})
}
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 clearBalanceIfPointsSufficient(): Boolean {
if (!canPointsCoverPayAmount()) return false
selectBalance = false
binding.image3.setImageResource(R.mipmap.image_address_check_narmal)
return true
}
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)
}
private fun isInternalPaymentSufficient(): Boolean {
var internalAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
if (selectBalance) {
internalAmount = internalAmount.add(
BigDecimal.valueOf(memberModel?.balance ?: 0.0)
.setScale(2, RoundingMode.HALF_UP)
)
}
if (selectPoints) {
internalAmount = internalAmount.add(
calculatePointsDeduction() ?: BigDecimal.ZERO
)
}
val payAmount = BigDecimal.valueOf(model.payAmount)
.setScale(2, RoundingMode.HALF_UP)
return internalAmount >= payAmount
}
private fun clearExternalPaymentIfNotNeeded() {
if (!isInternalPaymentSufficient()) return
payType = ""
binding.image1.setImageResource(R.mipmap.image_address_check_narmal)
binding.image2.setImageResource(R.mipmap.image_address_check_narmal)
}
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)
@@ -314,6 +496,8 @@ class PayOrderActivity : CKBaseActivity() {
// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
toast(getString(R.string.payment_success))
finish(DataCollection(),1002)
} else if (TextUtils.equals(resultStatus, "6001")) {
cancelPendingOrder()
} else {
toast(getString(R.string.payment_faild))
}
@@ -342,13 +526,19 @@ class PayOrderActivity : CKBaseActivity() {
}
override fun getResult(data: DataCollection, resultCode: Int) {
if (resultCode == 10001){
if (resultCode == 10001){
showPayBottomView()
}
}
var payClick = object : View.OnClickListener {
override fun onClick(v: View?) {
if (isInternalPaymentSufficient()) {
clearExternalPaymentIfNotNeeded()
toast("当前支付方式无需使用三方支付")
return
}
binding.image1.setImageResource(R.mipmap.image_address_check_narmal)
binding.image2.setImageResource(R.mipmap.image_address_check_narmal)
when (v?.id) {

View File

@@ -251,10 +251,12 @@ class RefundActivity: CKBaseActivity() {
showProgeassDialog(getString(R.string.submit))
var commodityState = if(model.orderStatus >= 3){
"1"
var commodityState = -1;
if ("0".equals(refundType)){
commodityState = 0
}else{
"0"
commodityState = 1
}
var param = TTRequestParam().apply {
add(TTRequestData("orderId",model.id))

View File

@@ -1,7 +1,7 @@
package com.ck.ckcollar.app.ui.act.petmanager
import android.Manifest
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -11,10 +11,12 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import android.widget.AdapterView
import android.widget.BaseAdapter
import android.widget.ImageView
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityAddPetBinding
@@ -28,6 +30,7 @@ import com.ck.ckcollar.app.ui.dialog.CKPhotoDialog
import com.ck.ckcollar.app.ui.dialog.CKPickViewDialog
import com.ck.ckcollar.app.utils.CKUtils
import com.ck.ckcollar.app.utils.Const
import com.ck.ckcollar.app.utils.LubanCompressUtil
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
@@ -40,6 +43,9 @@ import com.tt.kit.model.DataCollection
import com.tt.kit.utils.LiveDataBus
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.TTUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class AddPetActivity : CKBaseActivity() {
@@ -97,12 +103,10 @@ class AddPetActivity : CKBaseActivity() {
try {
if (mark == 0){
if (!checkCameraPermissions()){
CKAlertDialog(this@AddPetActivity,getString(R.string.alert),getString(R.string.camera_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
TODO("Not yet implemented")
}
}).show()
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
return
}
takePhoto()
@@ -189,11 +193,25 @@ class AddPetActivity : CKBaseActivity() {
}
binding.tvAiRecognize.setOnClickListener {
if (!checkCameraPermissions()) {
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_AI_CAMERA_PERMISSION
)
return@setOnClickListener
}
takePhoto2()
}
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> takePhoto()
REQUEST_AI_CAMERA_PERMISSION -> takePhoto2()
}
}
override fun initData() {
var data = getReceiveParam().get("data")
data?.let {
@@ -271,6 +289,7 @@ class AddPetActivity : CKBaseActivity() {
}
}
showProgeassDialog()
//先上传照片
TTHttpClient.postFile(this,"上传照片s", param,object : ICallback{
override fun onSuccess(data: String) {
var model = Gson().fromJson<UploadModel>(data,UploadModel::class.java)
@@ -317,21 +336,109 @@ class AddPetActivity : CKBaseActivity() {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 55 && resultCode == RESULT_OK) {
var path = PreferencesUtils.getString(this@AddPetActivity, Const.IMAGE_PATH,"")
this.data.add(this.data.size - 1,ImageModel().apply { this.path = path })
imageAdapter.data = this.data
imageAdapter.notifyDataSetChanged()
val path = PreferencesUtils.getString(this@AddPetActivity, Const.IMAGE_PATH, "")
compressAndAddImage(path.orEmpty())
}else if (requestCode == 56 && resultCode == RESULT_OK) {
var path = TTUtils.getImageUrl(this, data!!.getData());
this.data.add(this.data.size - 1,ImageModel().apply { this.path = path })
imageAdapter.data = this.data
imageAdapter.notifyDataSetChanged()
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")
}
private fun handleSelectedImage(uri: Uri) {
lifecycleScope.launch {
val path = withContext(Dispatchers.IO) {
copySelectedImageToCache(uri)
}
if (path == null) {
toast(getString(R.string.read_photo_failed))
return@launch
}
compressAndAddImage(path)
}
}
private fun compressAndAddImage(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
data.add(data.size - 1, ImageModel().apply {
this.path = file.absolutePath
})
imageAdapter.data = data
imageAdapter.notifyDataSetChanged()
binding.textPhotoNum.text = "${data.size - 1}/3"
}
override fun fail(failMsg: String) {
if (isFinishing || isDestroyed) return
toast(failMsg)
}
}
)
}
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)
?.takeIf { it.isNotBlank() }
?: "jpg"
val imageDir = File(cacheDir, "selected_images")
if (!imageDir.exists() && !imageDir.mkdirs()) {
return null
}
val target = File(imageDir, "${System.currentTimeMillis()}.$extension")
return try {
val input = contentResolver.openInputStream(uri) ?: return null
input.use { source ->
target.outputStream().use { output -> source.copyTo(output) }
}
target.absolutePath.takeIf { target.length() > 0L }
?: run {
target.delete()
null
}
} catch (e: Exception) {
target.delete()
e.printStackTrace()
null
}
}
fun takePhoto() {
try {
val openCameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
@@ -408,6 +515,11 @@ class AddPetActivity : CKBaseActivity() {
}).show()
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1201
private const val REQUEST_AI_CAMERA_PERMISSION = 1202
}
fun uploadImage(path: String) {
@@ -491,13 +603,10 @@ class AddPetActivity : CKBaseActivity() {
layoutItemImageBinding.imageDelete.visibility = View.GONE
} else {
layoutItemImageBinding.image.scaleType = ImageView.ScaleType.CENTER_CROP
layoutItemImageBinding.image.setImageBitmap(
TTUtils.getBitmap(
model.path,
200,
200
)
)
Glide.with(context)
.load(model.path?.let(::File))
.centerCrop()
.into(layoutItemImageBinding.image)
layoutItemImageBinding.imageDelete.visibility = View.VISIBLE
layoutItemImageBinding.imageDelete.setOnClickListener {

View File

@@ -3,27 +3,19 @@ package com.ck.ckcollar.app.ui.act.petmanager
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.viewpager.widget.PagerAdapter
import com.bumptech.glide.Glide
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityImageViewBinding
import com.ck.ckcollar.app.databinding.ActivityWelcomeBinding
import com.ck.ckcollar.app.databinding.LayoutItemImageViewBinding
import com.ck.ckcollar.app.databinding.LayoutItemWelcomeBinding
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.act.welcome.PermisstionActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
import com.cyclone.shadowsocks.utils.PreferencesUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tt.kit.model.DataCollection
class ImageViewActivity : CKBaseActivity() {
override val useDarkStatusBarIcons: Boolean = false
lateinit var binding: ActivityImageViewBinding
var views = mutableListOf<View>()
var data : List<String>? = null
var pos = 0
override fun onCreate(savedInstanceState: Bundle?) {
@@ -36,17 +28,9 @@ class ImageViewActivity : CKBaseActivity() {
}
override fun initView() {
for (i in data!!) {
var view = LayoutItemImageViewBinding.inflate(layoutInflater);
Glide.with(this)
.load(i)
.placeholder(R.mipmap.image_default)
.into(view.imageView)
views.add(view.root)
}
binding.viewPager.adapter = object : PagerAdapter() {
override fun getCount(): Int {
return views.size
return data.orEmpty().size
}
override fun isViewFromObject(
@@ -57,12 +41,21 @@ class ImageViewActivity : CKBaseActivity() {
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(views.get(position))
container.removeView(`object` as View)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
container.addView(views.get(position))
return views.get(position)
val itemBinding = LayoutItemImageViewBinding.inflate(
layoutInflater,
container,
false
)
Glide.with(this@ImageViewActivity)
.load(data.orEmpty()[position])
.placeholder(R.mipmap.image_default)
.into(itemBinding.imageView)
container.addView(itemBinding.root)
return itemBinding.root
}
}

View File

@@ -2,6 +2,7 @@ package com.ck.ckcollar.app.ui.act.petmanager
import android.net.wifi.ScanResult
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow
import androidx.recyclerview.widget.LinearLayoutManager
@@ -64,7 +65,7 @@ class PetManagerActivity : CKBaseActivity() {
}
fun getData() {
//showProgeassDialog(getString(R.string.search))
showProgeassDialog(getString(R.string.search))
TTHttpClient.get(this, "宠物列表", TTRequestParam().apply {
add(
TTRequestData("pageNum", 1)
@@ -81,6 +82,10 @@ class PetManagerActivity : CKBaseActivity() {
petAdapter.data = this@PetManagerActivity.data
petAdapter.notifyDataSetChanged()
val isEmpty = this@PetManagerActivity.data.isEmpty()
binding.relNoresult.visibility = if (isEmpty) View.VISIBLE else View.GONE
binding.recyclerView.visibility = if (isEmpty) View.GONE else View.VISIBLE
}
override fun onFailed(code: String, msg: String) {

View File

@@ -77,7 +77,10 @@ class ViewPetFileActivity: CKBaseActivity() {
}
binding.btnViewPostion.setOnClickListener {
if(homeModel?.collNum == null){
toast("请为您的爱宠绑定项圈")
return@setOnClickListener
}
getData2(model?.id.toString())
}
binding.btnAdreessDetails.setOnClickListener {

View File

@@ -33,6 +33,7 @@ import java.text.SimpleDateFormat
import java.util.Date
class PointsActivity : CKLHBaseActionBarActivity() {
override val shouldRequestPermissionsOnCreate: Boolean = false
lateinit var binding: ActivityPointsBinding
var data = listOf<PointsModel.PointsRecordModel>()

View File

@@ -109,6 +109,8 @@ class AccountCancellationActivity: CKBaseActivity() {
},object : ICallback{
override fun onSuccess(data: String) {
PreferencesUtils.clear(this@AccountCancellationActivity)
PreferencesUtils.putBoolean(this@AccountCancellationActivity, Const.FIRST, false)
CKApp.app.clearLoginState()
toActivityNewTaskClear(LoginActivity::class.java, DataCollection())
finish()
}

View File

@@ -168,7 +168,7 @@ class AddAddressActivity : CKBaseActivity() {
add(TTRequestData("provinceName",provinceName))
add(TTRequestData("cityName",cityName))
add(TTRequestData("areaName",areaName))
add(TTRequestData("detailAddress",binding.textDetailsAddress.text.toString()))
add(TTRequestData("detailAddress",binding.textDetailsAddress.text.toString().trim()))
add(TTRequestData("postalCode",""))
add(TTRequestData("isDefault",check))
add(TTRequestData("addressTag",tag))

View File

@@ -195,12 +195,12 @@ class EditAddressActivity : CKBaseActivity() {
}
var param = TTRequestParam().apply {
add(TTRequestData("id", model?.id.toString()))
add(TTRequestData("receiverName", binding.textName.text.toString()))
add(TTRequestData("receiverPhone", binding.textPhone.text.toString()))
add(TTRequestData("receiverName", binding.textName.text.toString().trim()))
add(TTRequestData("receiverPhone", binding.textPhone.text.toString().trim()))
add(TTRequestData("provinceName", provinceName))
add(TTRequestData("cityName", cityName))
add(TTRequestData("areaName", areaName))
add(TTRequestData("detailAddress", binding.textDetailsAddress.text.toString()))
add(TTRequestData("detailAddress", binding.textDetailsAddress.text.toString().trim()))
add(TTRequestData("postalCode", ""))
add(TTRequestData("isDefault", check))
add(TTRequestData("addressTag", tag))

View File

@@ -3,81 +3,74 @@ package com.ck.ckcollar.app.ui.act.setting
import android.content.Intent
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.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.utils.Const
import com.cyclone.shadowsocks.utils.PreferencesUtils
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.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)
binding.btnLogout.visibility = if (CKApp.app.isUserLoggedIn) View.VISIBLE else View.GONE
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->{
toActivity(PasswordManagerActivity::class.java, DataCollection())
}
R.id.contentPaymentPassword->{
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->{
toActivity(AddressActivity::class.java, DataCollection().apply { add(Data("flag","1"))
})
}
R.id.contentCancellation->{
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){
PreferencesUtils.clear(this@SettingActivity)
toActivityNewTaskClear(LoginActivity::class.java, DataCollection())
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) {
PreferencesUtils.putString(this@SettingActivity, Const.MESSAGE,"[]")
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())
return false
}
}

View File

@@ -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()
}
}

View File

@@ -1,6 +1,7 @@
package com.ck.ckcollar.app.ui.act.shop
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
@@ -21,6 +23,7 @@ import com.ck.ckcollar.app.model.AddressModel
import com.ck.ckcollar.app.model.ConfirmOrderModel
import com.ck.ckcollar.app.model.CreateOrderModel
import com.ck.ckcollar.app.model.MemberModel
import com.ck.ckcollar.app.model.OrderPaymentModel
import com.ck.ckcollar.app.model.PayOrderModel
import com.ck.ckcollar.app.model.PayPasswordStatus
import com.ck.ckcollar.app.model.PayResult
@@ -28,20 +31,25 @@ import com.ck.ckcollar.app.model.ShopCarModel
import com.ck.ckcollar.app.model.ShopProductModel
import com.ck.ckcollar.app.model.UserInfoModel
import com.ck.ckcollar.app.ui.act.order.MyOrderActivity
import com.ck.ckcollar.app.ui.act.balance.RechargeActivity
import com.ck.ckcollar.app.ui.act.setting.AddAddressActivity
import com.ck.ckcollar.app.ui.act.setting.AddressActivity
import com.ck.ckcollar.app.ui.act.setting.PayPsswordActivity
import com.ck.ckcollar.app.ui.act.shop.ShopCarActivity.ShopCarAdapter
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.utils.Const
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
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.model.Data
import com.tt.kit.model.DataCollection
import com.tt.kit.utils.LiveDataBus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -49,6 +57,7 @@ import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONObject
import java.math.BigDecimal
import java.math.RoundingMode
class OrderActivity : CKBaseActivity() {
@@ -62,7 +71,7 @@ class OrderActivity : CKBaseActivity() {
var memberModel: UserInfoModel? = null
var payType = "1"
var payType = "0"
var selectBalance = false
var selectPoints = false
@@ -70,6 +79,10 @@ class OrderActivity : CKBaseActivity() {
lateinit var orderSource: String
var flag = 0
private var currentOrderId = 0
private var isCancellingOrder = false
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityOrderBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -78,7 +91,20 @@ class OrderActivity : CKBaseActivity() {
setTitleText(getString(R.string.confirm_order))
}
override fun onResume() {
super.onResume()
var abc = 0;
}
override fun initView() {
LiveDataBus.get().with(RechargeActivity.WX_PAY_RESULT, Int::class.java).observe(this) { result ->
if (result == 0) {
handlePaymentSuccess()
} else {
handlePaymentFailed(result == -2)
}
}
productAdapter =
ProductAdapter(this@OrderActivity, productDatas, object : OnItemClickListenner {
override fun onClick(pos: Int, model: Any) {
@@ -95,7 +121,11 @@ class OrderActivity : CKBaseActivity() {
binding.btnWX.setOnClickListener(payClick)
binding.btnBalance.setOnClickListener {
if (memberModel?.balance == 0.0){
if (clearBalanceIfPointsSufficient()) {
toast("积分足够无需使用余额")
return@setOnClickListener
}
if ((memberModel?.balance ?: 0.0) <= 0.0){
toast(getString(R.string.balance_over))
return@setOnClickListener
}
@@ -106,14 +136,22 @@ class OrderActivity : CKBaseActivity() {
}else{
binding.image3.setImageResource(R.mipmap.image_address_check_narmal)
}
clearExternalPaymentIfNotNeeded()
}
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)
}else{
binding.image4.setImageResource(R.mipmap.image_address_check_narmal)
}
clearBalanceIfPointsSufficient()
clearExternalPaymentIfNotNeeded()
}
binding.contentAddress.setOnClickListener {
@@ -123,7 +161,7 @@ class OrderActivity : CKBaseActivity() {
}
binding.btnBuyNow.setOnClickListener {
if (selectBalance){
if (selectBalance && !canPointsCoverPayAmount()){
getPayPassword()
}else{
order("")
@@ -287,38 +325,54 @@ class OrderActivity : CKBaseActivity() {
param.put("buyerNote", "")
param.put("items", items)
param.put("payPassword", payPassword)
param.put("orderId", model.orderId)
showProgeassDialog(getString(R.string.order))
TTHttpClient.postJson(this, "下单", param.toString(), object : ICallback {
override fun onSuccess(data: String) {
var model = Gson().fromJson<PayOrderModel>(data, PayOrderModel::class.java)
val paymentModel = Gson().fromJson(data, OrderPaymentModel::class.java)
currentOrderId = paymentModel.orderId
if ("0".equals(this@OrderActivity.payType)) {
//微信
val weChatPayParams = paymentModel.wxMap
if (weChatPayParams == null || !weChatPayParams.isJsonObject) {
hiddenProgeassDialog()
toast("微信支付参数格式错误")
return
}
wxPay(Gson().fromJson(weChatPayParams, PayOrderModel::class.java))
}else if ("1".equals(this@OrderActivity.payType)) {
if (TextUtils.isEmpty(model.orderStr)){
val aliPayOrderStr = paymentModel.orderStr
?.takeIf { it.isJsonPrimitive && it.asJsonPrimitive.isString }
?.asString
if (TextUtils.isEmpty(aliPayOrderStr)){
toActivity(OrderSuccessActivity::class.java, DataCollection())
finish()
}else{
aliPay(model.orderStr!!)
aliPay(aliPayOrderStr!!)
}
}else{
toast(getString(R.string.order_success))
toActivity(OrderSuccessActivity::class.java, DataCollection())
finish()
}
}
override fun onFailed(code: String, msg: String) {
toast(msg)
if (msg.indexOf("不足")>=0){
toActivity(
MyOrderActivity::class.java,
DataCollection().apply { add(Data("pos", "1")) })
finish()
}
override fun onFailed(code: String, msg: String, data: String) {
onFailed(code, msg)
//原有【不足】的字符串判断条件
if (code == "502") {
val orderId = JSONObject(data).optInt("orderId")
if (orderId > 0) {
model.orderId = orderId
}
}
}
})
@@ -327,6 +381,10 @@ class OrderActivity : CKBaseActivity() {
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@OrderActivity)
val result = alipay.payV2(orderStr, true)
@@ -337,20 +395,111 @@ class OrderActivity : CKBaseActivity() {
// 判断resultStatus 为9000则代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
// 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
toast(getString(R.string.payment_success))
toActivity(OrderSuccessActivity::class.java, DataCollection())
finish()
handlePaymentSuccess()
} else {
toast(getString(R.string.payment_faild))
toActivity(
MyOrderActivity::class.java,
DataCollection().apply { add(Data("pos", "1")) })
finish()
handlePaymentFailed(TextUtils.equals(resultStatus, "6001"))
}
}
}
}
private fun wxPay(model: PayOrderModel) {
val requiredValues = listOf(
model.appId,
model.partnerId,
model.prepayId,
model.packageVal,
model.nonceStr,
model.timeStamp,
model.paySign
)
if (requiredValues.any { it.isNullOrBlank() }) {
hiddenProgeassDialog()
toast("微信支付参数不完整,请检查商户号等支付参数")
return
}
if (model.appId != Const.WX_APP_ID) {
hiddenProgeassDialog()
toast("微信支付 AppID 不匹配")
return
}
val api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, true)
api.registerApp(Const.WX_APP_ID)
if (!api.isWXAppInstalled) {
hiddenProgeassDialog()
toast("请先安装微信")
return
}
val request = PayReq().apply {
appId = model.appId
partnerId = model.partnerId
prepayId = model.prepayId
packageValue = model.packageVal
nonceStr = model.nonceStr
timeStamp = model.timeStamp
sign = model.paySign
extData = model.orderNo
}
hiddenProgeassDialog()
if (!api.sendReq(request)) {
toast("微信支付拉起失败")
}
}
private fun handlePaymentSuccess() {
toast(getString(R.string.payment_success))
toActivity(OrderSuccessActivity::class.java, DataCollection())
finish()
}
private fun handlePaymentFailed(cancelled: Boolean) {
if (cancelled) {
cancelPendingOrder()
return
}
toast(getString(R.string.payment_faild))
openPendingOrderList()
}
private fun cancelPendingOrder() {
if (isCancellingOrder) return
if (currentOrderId <= 0) {
toast(getString(R.string.payment_faild))
openPendingOrderList()
return
}
isCancellingOrder = true
showProgeassDialog(getString(R.string.cancel))
TTHttpClient.post(this, "取消待支付订单", TTRequestParam().apply {
add(TTRequestData("orderId", currentOrderId))
}, object : ICallback {
override fun onSuccess(data: String) {
hiddenProgeassDialog()
toast(getString(R.string.cancel_pay_cussess))
openPendingOrderList()
}
override fun onFailed(code: String, msg: String) {
hiddenProgeassDialog()
toast(msg)
openPendingOrderList()
}
})
}
private fun openPendingOrderList() {
toActivity(
MyOrderActivity::class.java,
DataCollection().apply { add(Data("pos", "1")) }
)
finish()
}
fun getMemberInfo() {
TTHttpClient.get(this, "个人资料", TTRequestParam(), object : ICallback {
override fun onSuccess(data: String) {
@@ -393,16 +542,18 @@ class OrderActivity : CKBaseActivity() {
fun showPayBottomView(){
var showBalance = model.payAmount
var showBalance = BigDecimal.valueOf(model.payAmount).setScale(2, RoundingMode.HALF_UP)
if (selectPoints){
CKApp.app.userInfo?.intoAmounts?.let {
showBalance = showBalance - BigDecimal(memberModel?.points!!).multiply(
BigDecimal(memberModel!!.intoAmounts!!)).setScale(2).toDouble()
if (showBalance < 0.0){
showBalance = 0.00
}
calculatePointsDeduction()?.let { pointsDeduction ->
showBalance = showBalance.subtract(pointsDeduction)
.max(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP))
}
}
val userBalance = BigDecimal.valueOf(memberModel?.balance ?: 0.0)
.setScale(2, RoundingMode.HALF_UP)
showBalance = showBalance.min(userBalance)
var passwordBottomFragment = PayPasswordBottomFragment(showBalance.toString(), "余额",object : PayPasswordBottomFragment.PasswordCallBack{
override fun callBack(password: String) {
@@ -413,8 +564,64 @@ class OrderActivity : CKBaseActivity() {
passwordBottomFragment.show(supportFragmentManager, "PayPasswordBottomFragment")
}
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 clearBalanceIfPointsSufficient(): Boolean {
if (!canPointsCoverPayAmount()) return false
selectBalance = false
binding.image3.setImageResource(R.mipmap.image_address_check_narmal)
return true
}
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)
}
private fun isInternalPaymentSufficient(): Boolean {
var internalAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
if (selectBalance) {
internalAmount = internalAmount.add(
BigDecimal.valueOf(memberModel?.balance ?: 0.0)
.setScale(2, RoundingMode.HALF_UP)
)
}
if (selectPoints) {
internalAmount = internalAmount.add(
calculatePointsDeduction() ?: BigDecimal.ZERO
)
}
val payAmount = BigDecimal.valueOf(model.payAmount)
.setScale(2, RoundingMode.HALF_UP)
return internalAmount >= payAmount
}
private fun clearExternalPaymentIfNotNeeded() {
if (!isInternalPaymentSufficient()) return
payType = ""
binding.image1.setImageResource(R.mipmap.image_address_check_narmal)
binding.image2.setImageResource(R.mipmap.image_address_check_narmal)
}
var payClick = object : View.OnClickListener {
override fun onClick(v: View?) {
if (isInternalPaymentSufficient()) {
clearExternalPaymentIfNotNeeded()
toast("当前支付方式无需使用三方支付")
return
}
binding.image1.setImageResource(R.mipmap.image_address_check_narmal)
binding.image2.setImageResource(R.mipmap.image_address_check_narmal)
when (v?.id) {

View File

@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.action.ActionCode
import com.ck.ckcollar.app.databinding.ActivityProductDetailsBinding
@@ -23,6 +24,7 @@ import com.ck.ckcollar.app.ui.act.customer.PeopleCustomerActivity
import com.ck.ckcollar.app.ui.act.customer.ShopCustomerActivity
import com.ck.ckcollar.app.ui.act.home.fragment.HomeFragment
import com.ck.ckcollar.app.ui.act.home.fragment.HomeFragment.ImageAdapter
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.act.petmanager.ImageViewActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.base.CKLHBaseActionBarActivity
@@ -63,20 +65,30 @@ class ProductDetailsActivity : CKBaseActivity() {
setupWebView(binding.textContent)
binding.btnAddCar.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
showProductTypeFragment()
}
binding.btnBuyNow.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
showProductTypeFragment()
}
binding.contentShopCar.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(ShopCarActivity::class.java, DataCollection())
}
binding.contentCustomer.setOnClickListener {
if (!requireLogin()) return@setOnClickListener
toActivity(PeopleCustomerActivity::class.java, DataCollection().apply { add(Data("chatType","1")) })
}
}
private fun requireLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
toActivity(LoginActivity::class.java, DataCollection())
return false
}
override fun initData() {
model = Gson().fromJson<ShopProductModel.ProductModel>(getReceiveParam().get("data")!!.stringValue,
ShopProductModel.ProductModel::class.java)
@@ -148,7 +160,7 @@ class ProductDetailsActivity : CKBaseActivity() {
ViewGroup.LayoutParams.MATCH_PARENT
)
)
imageView.setScaleType(ImageView.ScaleType.FIT_XY)
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP)
return BannerViewHolder(imageView)
}

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
@@ -91,6 +92,10 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
binding.checkbox.setOnClickListener {
if (productDatas.isEmpty()) {
toast(getString(R.string.shop_car_empty))
return@setOnClickListener
}
selectAll = !selectAll
if (selectAll){
@@ -154,6 +159,11 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
override fun rightButton1OnClick() {
if (productDatas.isEmpty()) {
toast(getString(R.string.shop_car_empty))
return
}
isShowDelete = !isShowDelete
if (isShowDelete){
@@ -214,6 +224,7 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
})
}
fun getData(){
showProgeassDialog()
TTHttpClient.get(this,"购物车列表", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
this@ShopCarActivity.productDatas = Gson().fromJson<List<ShopCarModel>>(data, object : TypeToken<List<ShopCarModel>>(){}.type)
@@ -221,6 +232,10 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
shopCarAdapter.data = this@ShopCarActivity.productDatas
shopCarAdapter.notifyDataSetChanged()
val isEmpty = this@ShopCarActivity.productDatas.isEmpty()
binding.relNoresult.visibility = if (isEmpty) View.VISIBLE else View.GONE
binding.recyclerView.visibility = if (isEmpty) View.GONE else View.VISIBLE
var flag = true
for (model in productDatas){
if ("0".equals(model.selected)){
@@ -242,7 +257,7 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
}
override fun onFailed(code: String, msg: String) {
toast(msg)
}
})
}
@@ -296,14 +311,15 @@ class ShopCarActivity : CKLHBaseActionBarActivity() {
}
fun sendSelectAll(select : String){
showProgeassDialog()
TTHttpClient.putJson(this,"购物车全部选中", TTRequestParam().apply { add(TTRequestData("selected",select)) },object : ICallback{
override fun onSuccess(data: String) {
hiddenProgeassDialog()
getData()
}
override fun onFailed(code: String, msg: String) {
hiddenProgeassDialog()
toast(msg)
getData()
}

View File

@@ -3,6 +3,7 @@ package com.ck.ckcollar.app.ui.act.shop
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
@@ -33,6 +34,8 @@ import com.tt.kit.model.DataCollection
import com.tt.kit.utils.KeyboardUtils
class TypeActivity : CKLHBaseActionBarActivity() {
override val shouldRequestPermissionsOnCreate: Boolean = false
lateinit var binding: ActivityShopTypeBinding
lateinit var productAdapter: ProductAdapter
@@ -86,6 +89,7 @@ class TypeActivity : CKLHBaseActionBarActivity() {
fun getData() {
showProgeassDialog()
TTHttpClient.get(this, "商品列表", TTRequestParam().apply {
add(
TTRequestData("pageNum", page)
@@ -107,12 +111,18 @@ class TypeActivity : CKLHBaseActionBarActivity() {
productAdapter.data = productDatas
productAdapter.notifyDataSetChanged()
val isEmpty = productDatas.isEmpty()
binding.relNoresult.visibility = if (isEmpty) View.VISIBLE else View.GONE
binding.recyclerView.visibility = if (isEmpty) View.GONE else View.VISIBLE
binding.refreshLayout.finishLoadMore(true)
binding.refreshLayout.finishRefresh()
}
override fun onFailed(code: String, msg: String) {
binding.refreshLayout.finishLoadMore(true)
binding.refreshLayout.finishLoadMore(false)
binding.refreshLayout.finishRefresh()
toast(msg)
}
})

View File

@@ -1,5 +1,6 @@
package com.ck.ckcollar.app.ui.act.user
import android.Manifest
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -20,12 +21,12 @@ import com.ck.ckcollar.app.model.UserInfoModel
import com.ck.ckcollar.app.ui.act.home.MainActivity
import com.ck.ckcollar.app.ui.act.login.BoundPhoneActivity
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.ck.ckcollar.app.ui.dialog.CKEditDialog
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
@@ -55,7 +56,7 @@ class UserInfoActivity: CKBaseActivity() {
api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID)
CKApp.app.wxJump = 1
checkAppPermissions()
// checkAppPermissions()
setStatusBarColor(resources.getColor(R.color.white))
setActionBarColor(resources.getColor(R.color.white))
@@ -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()
@@ -111,16 +117,10 @@ class UserInfoActivity: CKBaseActivity() {
override fun onBack(mark: Int, position: Int) {
try {
if (mark == 0){
if (!checkCameraPermissions()){
CKAlertDialog(this@UserInfoActivity,getString(R.string.alert),getString(R.string.camera_alert),object : CKAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
TODO("Not yet implemented")
}
}).show()
return
}
takePhoto()
getPermissionRequst(
Manifest.permission.CAMERA,
REQUEST_CAMERA_PERMISSION
)
}else{
//相册
val i = Intent(
@@ -148,6 +148,12 @@ class UserInfoActivity: CKBaseActivity() {
}
}
override fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
takePhoto()
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@@ -242,6 +248,10 @@ class UserInfoActivity: CKBaseActivity() {
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 1101
}
override fun initData() {
binding.textNick.text= CKApp.app.userInfo?.custName

View File

@@ -4,11 +4,27 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.TextUtils
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.LoginActivity
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
@@ -25,19 +41,89 @@ class IndexActivity : CKBaseActivity() {
}
var handler = object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
if (TextUtils.isEmpty(CKApp.app.token)){
var isFirst = PreferencesUtils.getBolean(this@IndexActivity, Const.FIRST,true) as Boolean
if (isFirst){
toActivity(WelcomeActivity::class.java, DataCollection())
}else{
toActivity(LoginActivity::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)
}
}

View File

@@ -9,7 +9,7 @@ import androidx.viewpager.widget.PagerAdapter
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityWelcomeBinding
import com.ck.ckcollar.app.databinding.LayoutItemWelcomeBinding
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.utils.Const
import com.cyclone.shadowsocks.utils.PreferencesUtils
@@ -59,11 +59,12 @@ class WelcomeActivity : CKBaseActivity() {
view.btnCome.setOnClickListener {
val toPermisstionActivity = PreferencesUtils.getBolean(this@WelcomeActivity,
Const.PERMISSIONS_FIRST,false) as Boolean
if (!toPermisstionActivity){
toActivity(PermisstionActivity::class.java, DataCollection())
}else{
toActivity(LoginActivity::class.java, DataCollection())
}
// if (!toPermisstionActivity){
// toActivity(PermisstionActivity::class.java, DataCollection())
// }else{
// toActivity(LoginActivity::class.java, DataCollection())
// }
toActivity(MainActivity::class.java, DataCollection())
finish()
}

View File

@@ -19,8 +19,8 @@ import pub.devrel.easypermissions.EasyPermissions
open class CKBaseActionBarActivity : TTBaseActivity(){
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE)
lateinit var rootView : View
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -1,23 +1,33 @@
package com.ck.ckcollar.app.ui.base
import android.Manifest
import android.content.pm.PackageManager
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.TypedValue
import android.view.View
import android.view.Gravity
import android.view.WindowManager
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.ui.dialog.CKAlertDialog
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.TTUtils
@@ -26,8 +36,47 @@ import pub.devrel.easypermissions.EasyPermissions
open class CKBaseActivity : TTBaseActivity(){
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
protected open val useDarkStatusBarIcons: Boolean = true
private var permissionDialog: AlertDialog? = null
private var pendingPermissionRequestCode: Int? = null
private var pendingPermissions: Array<String> = emptyArray()
private var settingsPermissionRequestCode: Int? = null
private var settingsPermissions: Array<String> = emptyArray()
private val permissionSettingsLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
val requestCode = settingsPermissionRequestCode
val permissions = settingsPermissions
settingsPermissionRequestCode = null
settingsPermissions = emptyArray()
if (requestCode != null && hasRecordPermission(permissions)) {
onPermissionsGranted(requestCode, permissions)
}
}
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
hidePermissionDialog()
val requestCode = pendingPermissionRequestCode ?: return@registerForActivityResult
val requestedPermissions = pendingPermissions
pendingPermissionRequestCode = null
pendingPermissions = emptyArray()
val denied = result.filterValues { !it }.keys.toTypedArray()
if (denied.isEmpty()) {
onPermissionsGranted(requestCode, requestedPermissions)
} else {
val permanentlyDenied = denied.any {
!ActivityCompat.shouldShowRequestPermissionRationale(this, it)
}
onPermissionsDenied(requestCode, denied, permanentlyDenied)
}
}
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_carmra = listOf<String>(Manifest.permission.CAMERA)
@@ -53,10 +102,15 @@ open class CKBaseActivity : TTBaseActivity(){
CKApp.app.activitys.add(this)
setStatusBarColor(R.attr.actionBarBackground)
setStatusBarTextDark(useDarkStatusBarIcons)
}
fun setStatusBarTextDark(isDark: Boolean = true) {
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightStatusBars = isDark
}
fun checkAppPermissions(){
if (!checkPermissions()){
requstPermissions()
@@ -208,10 +262,139 @@ open class CKBaseActivity : TTBaseActivity(){
}
fun checkCameraPermissions() : Boolean{
var a = 0;
return EasyPermissions.hasPermissions(this, *perms_carmra.toTypedArray())
}
fun hasRecordPermission(permission: String): Boolean {
return hasRecordPermission(arrayOf(permission))
}
fun hasRecordPermission(permissions: Array<String>): Boolean {
return permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
}
fun getPermissionRequst(
permission: String,
requestCode: Int
) {
val isCameraPermission = permission == Manifest.permission.CAMERA
getPermissionRequst(
permission,
getString(if (isCameraPermission) R.string.camera else R.string.photo),
getString(if (isCameraPermission) R.string.camera_alert else R.string.photo_permission_alert),
requestCode
)
}
fun getPermissionRequst(
permission: String,
title: String,
content: String,
requestCode: Int
) {
getMorePermissionRequst(arrayOf(permission), title, content, requestCode)
}
fun getMorePermissionRequst(
permissions: Array<String>,
title: String,
content: String,
requestCode: Int
) {
if (hasRecordPermission(permissions)) {
onPermissionsGranted(requestCode, permissions)
return
}
CKAlertDialog(this, getString(R.string.alert), content, object : CKAlertDialog.AlertClickListener {
override fun onBack(isConfirm: Boolean) {
if (isConfirm) {
requestSystemPermissions(permissions, title, content, requestCode)
} else {
onPermissionsDenied(requestCode, permissions, false)
}
}
}).show()
}
private fun requestSystemPermissions(
permissions: Array<String>,
title: String,
content: String,
requestCode: Int
) {
pendingPermissionRequestCode = requestCode
pendingPermissions = permissions
showPermissionDialog(title, content)
permissionDialog?.window?.decorView?.post {
if (!isFinishing && pendingPermissionRequestCode == requestCode) {
permissionLauncher.launch(permissions)
}
}
}
private fun showPermissionDialog(title: String, content: String) {
hidePermissionDialog()
val view = layoutInflater.inflate(R.layout.permission_top_dialog, null)
view.findViewById<TextView>(R.id.permissionTopTitle).text = title
view.findViewById<TextView>(R.id.permissionTopContent).text = content
permissionDialog = AlertDialog.Builder(this)
.setView(view)
.create()
.also { dialog ->
dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.show()
dialog.window?.apply {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setGravity(Gravity.TOP)
setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
}
}
}
private fun hidePermissionDialog() {
permissionDialog?.dismiss()
permissionDialog = null
}
protected open fun onPermissionsGranted(requestCode: Int, permissions: Array<String>) {
}
protected open fun onPermissionsDenied(
requestCode: Int,
permissions: Array<String>,
permanentlyDenied: Boolean
) {
if (!permanentlyDenied) return
CKAlertDialog(
this,
getString(R.string.settting_permissions),
getString(R.string.permission_settings_alert),
object : CKAlertDialog.AlertClickListener {
override fun onBack(isConfirm: Boolean) {
if (isConfirm) {
settingsPermissionRequestCode = requestCode
settingsPermissions = permissions
permissionSettingsLauncher.launch(
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:$packageName")
)
)
}
}
}
).show()
}
fun requstPermissions(){
var perms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
perms_
@@ -228,6 +411,11 @@ open class CKBaseActivity : TTBaseActivity(){
ActivityCompat.requestPermissions(this, perms.toTypedArray(), 1001)
}
override fun onDestroy() {
hidePermissionDialog()
pendingPermissionRequestCode = null
pendingPermissions = emptyArray()
settingsPermissionRequestCode = null
settingsPermissions = emptyArray()
super.onDestroy()
CKApp.app.activitys.remove(this)
}

View File

@@ -1,14 +1,21 @@
package com.ck.ckcollar.app.ui.base
import android.Manifest
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.cyclone.shadowsocks.ui.frg.TTBaseFragment
import com.tt.kit.model.DataCollection
import pub.devrel.easypermissions.EasyPermissions
open class CKBaseFragment : TTBaseFragment() {
var perms_carmra = listOf<String>(Manifest.permission.CAMERA)
fun isUserLogin(): Boolean {
if (CKApp.app.isUserLoggedIn) return true
toActivity(LoginActivity::class.java, DataCollection())
return false
}
fun checkCameraPermissions() : Boolean{

View File

@@ -28,8 +28,10 @@ import pub.devrel.easypermissions.EasyPermissions
open class CKLHBaseActionBarActivity : TTBaseActivity(){
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
protected open val shouldRequestPermissionsOnCreate: Boolean = false
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE)
lateinit var rootView : View
@@ -83,7 +85,7 @@ open class CKLHBaseActionBarActivity : TTBaseActivity(){
// insets
// }
if (!checkPermissions()){
if (shouldRequestPermissionsOnCreate && !checkPermissions()){
requstPermissions()
}

View File

@@ -28,8 +28,8 @@ import pub.devrel.easypermissions.EasyPermissions
open class CKLHBaseActivity : TTBaseActivity(){
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_= listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES,Manifest.permission.READ_EXTERNAL_STORAGE)
var perms_tiramisu = listOf<String>(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE)
override fun onCreate(savedInstanceState: Bundle?) {
// AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
@@ -74,9 +74,9 @@ open class CKLHBaseActivity : TTBaseActivity(){
// getWindow().setStatusBarColor(Color.TRANSPARENT);
if (!checkPermissions()){
requstPermissions()
}
// if (!checkPermissions()){
// requstPermissions()
// }
}

View File

@@ -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())
}

View File

@@ -35,6 +35,10 @@ class CKPhotoDialog(
}
})
dialogMakePhotoBinding.cancelButton.setOnClickListener {
cancel()
}
setContentView(dialogMakePhotoBinding.root,ViewGroup.LayoutParams(TTUtils.width, ViewGroup.LayoutParams.WRAP_CONTENT))
} catch (e: Exception) {

View File

@@ -19,10 +19,13 @@ class CKUpdateAlertDialog(
var alertClickListener: AlertClickListener
) : TTBaseDialog(mContext){
private var binding: DialogUpdateAlertBinding? = null
override fun show() {
try {
var dialogAlertBinding =
val dialogAlertBinding =
DialogUpdateAlertBinding.inflate(LayoutInflater.from(mContext))
binding = dialogAlertBinding
dialogAlertBinding.textAlert.text = alertText
if (isForce){
@@ -39,8 +42,6 @@ class CKUpdateAlertDialog(
//dialogView.findViewById(R.id.photo).setVisibility(View.GONE);
dialogAlertBinding.btnConfirm.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
// TODO Auto-generated method stub
cancel()
alertClickListener.onBack(true)
}
})
@@ -55,6 +56,34 @@ class CKUpdateAlertDialog(
setCancelable(false)
}
fun showDownloadProgress() {
binding?.apply {
progressContainer.visibility = View.VISIBLE
btnConfirm.visibility = View.GONE
btnCenlel.visibility = View.GONE
textAlert.visibility = View.GONE
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_downloading, 0)
}
}
fun updateProgress(progress: Int) {
binding?.apply {
updateProgress.progress = progress
progressText.text = "$progress%"
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_downloading, progress)
}
}
fun showDownloadError() {
binding?.apply {
progressContainer.visibility = View.GONE
btnConfirm.visibility = View.VISIBLE
btnCenlel.visibility = if (isForce) View.GONE else View.VISIBLE
textAlert.visibility = View.VISIBLE
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_content)
}
}
interface AlertClickListener {
fun onBack(isConfirm : Boolean)

View File

@@ -12,6 +12,9 @@ object Const {
const val LOGIN_NAME = "login_name"
const val LOGIN_PWD = "login_pwd"
const val TOKEN = "token"
const val MEMBER_PRICE = "member_price"
const val MEMBER_PRICE_CACHE_TIME = "member_price_cache_time"
//微信app-id
const val WX_APP_ID = "wx7681e74df7d88ddc"
}

View File

@@ -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
}

View File

@@ -0,0 +1,53 @@
package com.ck.ckcollar.app.utils
import android.content.Context
import java.io.File
import java.util.Locale
import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener
object LubanCompressUtil {
private const val DEFAULT_IGNORE_SIZE_KB = 100
interface CompressCallback {
fun success(file: File)
fun fail(failMsg: String)
}
fun getInstance(): LubanCompressUtil {
return this
}
fun getCompressFile(
context: Context,
oldFile: File,
callback: CompressCallback?
) {
if (!oldFile.exists() || !oldFile.isFile) {
callback?.fail("图片文件不存在")
return
}
Luban.with(context.applicationContext)
.load(oldFile)
.ignoreBy(DEFAULT_IGNORE_SIZE_KB)
.filter { path ->
path.isNotBlank() && !path.lowercase(Locale.ROOT).endsWith(".gif")
}
.setCompressListener(object : OnCompressListener {
override fun onStart() {
}
override fun onSuccess(file: File) {
callback?.success(file)
}
override fun onError(error: Throwable) {
callback?.fail(error.message ?: "图片压缩失败")
}
})
.launch()
}
}

View File

@@ -0,0 +1,31 @@
package com.ck.ckcollar.app.utils
import android.content.Context
import com.cyclone.shadowsocks.utils.PreferencesUtils
object MemberPriceCache {
private const val CACHE_VALID_DURATION = 24 * 60 * 60 * 1000L
fun getPrice(context: Context): Double? {
return PreferencesUtils.getString(context, Const.MEMBER_PRICE, "")
.toString()
.toDoubleOrNull()
}
fun isExpired(context: Context): Boolean {
val cacheTime = PreferencesUtils.getString(context, Const.MEMBER_PRICE_CACHE_TIME, "0")
.toString()
.toLongOrNull() ?: 0L
return System.currentTimeMillis() - cacheTime >= CACHE_VALID_DURATION
}
fun save(context: Context, price: Double) {
PreferencesUtils.putString(context, Const.MEMBER_PRICE, price.toString())
PreferencesUtils.putString(
context,
Const.MEMBER_PRICE_CACHE_TIME,
System.currentTimeMillis().toString()
)
}
}

View File

@@ -2,4 +2,8 @@ package com.ck.ckcollar.app.utils.networkone
interface ICallback {
fun onSuccess(data: String)
fun onFailed(code: String, msg: String)
fun onFailed(code: String, msg: String, data: String) {
onFailed(code, msg)
}
fun onComplete() {}
}

View File

@@ -78,7 +78,9 @@ object TTHttpClient {
val string = response.body?.string()
string?.let { TTLog.e("response", actionCode + "====" + it) }
var model = JSONObject(string)
if (isSusscee(model)) {
// A withdrawal code of 201 means the transfer bill was created
// and the client must continue with WeChat confirmation.
if (isSusscee(model) || (actionCode == "提现" && getCode(model) == "201")) {
activity.runOnUiThread {
activity.hiddenProgeassDialog()
iCallback.onSuccess(getData(model))
@@ -137,7 +139,10 @@ object TTHttpClient {
val string = response.body?.string()
string?.let { TTLog.e("response", actionCode + "====" + it) }
var model = JSONObject(string)
if (isSusscee(model)) {
// code 201 means the withdrawal bill was created and the
// client must continue with WeChat confirmation. Keep the
// response data available to the withdrawal screen.
if (isSusscee(model) || (actionCode == "提现" && getCode(model) == "201")) {
activity.runOnUiThread {
activity.hiddenProgeassDialog()
iCallback.onSuccess(getData(model))
@@ -429,10 +434,12 @@ object TTHttpClient {
}
withContext(Dispatchers.Main) {
iCallback.onFailed("500", "网络连接失败,请检查网络")
iCallback.onComplete()
}
}catch (e: Exception){
withContext(Dispatchers.Main) {
iCallback.onFailed("500", e.message.orEmpty())
}
}
}
@@ -484,7 +491,7 @@ fun postJson(
} else {
activity.runOnUiThread {
activity.hiddenProgeassDialog()
iCallback.onFailed(getCode(model), getMessage(model))
iCallback.onFailed(getCode(model), getMessage(model), model.toString())
}
}
}

View File

@@ -2,6 +2,7 @@ package com.ck.ckcollar.app.utils.networkone.interceptor
import android.content.Context
import android.content.Intent
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.google.gson.Gson
import com.tt.kit.networktwo.model.TTResposeData
@@ -22,6 +23,7 @@ class ResponseInterceptor(val context : Context) : Interceptor{
TTLog.i("ResponseInterceptor", "intercept model:" + body)
// token失效统一处理
if (model.code == 401){
CKApp.app.clearLoginState()
var intent = Intent(context, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
context.startActivity(intent)

View File

@@ -3,24 +3,63 @@ package com.ck.ckcollar.app.widget
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatImageView
import androidx.viewpager.widget.ViewPager
class CKViewPager : ViewPager{
private var isMultiTouchGesture = false
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
when (ev.actionMasked) {
MotionEvent.ACTION_DOWN -> isMultiTouchGesture = false
MotionEvent.ACTION_POINTER_DOWN -> {
if (ev.pointerCount > 1) {
isMultiTouchGesture = true
}
}
}
return try {
super.dispatchTouchEvent(ev)
} finally {
// dispatchTouchEvent always sees the terminal event, even when
// PhotoView has disabled parent interception during scaling.
if (ev.actionMasked == MotionEvent.ACTION_UP ||
ev.actionMasked == MotionEvent.ACTION_CANCEL
) {
isMultiTouchGesture = false
}
}
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
if (ev == null) return false
// Keep the complete pinch sequence in PhotoView. If ViewPager intercepts
// one MOVE event, the child receives CANCEL and scaling stops entirely.
if (isMultiTouchGesture || ev.pointerCount > 1) {
return false
}
try {
return super.onInterceptTouchEvent(ev)
}catch (e : Exception){
} catch (e: IllegalArgumentException) {
e.printStackTrace()
}
return false
return false
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
if (ev == null) return false
return try {
super.onTouchEvent(ev)
} catch (e: IllegalArgumentException) {
e.printStackTrace()
false
}
}
}

View File

@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import com.ck.ckcollar.app.CKApp
import com.ck.ckcollar.app.ui.act.balance.WithDrawActivity
import com.ck.ckcollar.app.ui.act.login.LoginActivity
import com.ck.ckcollar.app.ui.act.user.UserInfoActivity
import com.ck.ckcollar.app.utils.Const
@@ -15,6 +16,7 @@ import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tt.kit.utils.TTLog
import com.tt.kit.utils.LiveDataBus
class WXEntryActivity : Activity(), IWXAPIEventHandler {
@@ -43,6 +45,13 @@ class WXEntryActivity : Activity(), IWXAPIEventHandler {
}
override fun onResp(resp: BaseResp?) {
if (resp?.type == ConstantsAPI.COMMAND_OPEN_BUSINESS_VIEW) {
LiveDataBus.get()
.with(WithDrawActivity.WX_WITHDRAW_RESULT)
.value = resp.errCode
finish()
return
}
if (resp!!.getType() === ConstantsAPI.COMMAND_SENDAUTH) {
val authResp = resp as SendAuth.Resp
val code = authResp.code

View File

@@ -0,0 +1,51 @@
package com.ck.ckcollar.app.wxapi
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import com.ck.ckcollar.app.ui.act.balance.RechargeActivity
import com.ck.ckcollar.app.utils.Const
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.constants.ConstantsAPI
import com.ck.ckcollar.app.ui.act.balance.WithDrawActivity
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tt.kit.utils.LiveDataBus
class WXPayEntryActivity : Activity(), IWXAPIEventHandler {
private lateinit var api: IWXAPI
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
api = WXAPIFactory.createWXAPI(this, Const.WX_APP_ID, false)
api.handleIntent(intent, this)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
api.handleIntent(intent, this)
}
override fun onReq(req: BaseReq?) = Unit
override fun onResp(resp: BaseResp?) {
val resultCode = resp?.errCode ?: -1
if (resp?.type == ConstantsAPI.COMMAND_OPEN_BUSINESS_VIEW) {
LiveDataBus.get().with(WithDrawActivity.WX_WITHDRAW_RESULT).value = resultCode
finish()
return
}
val message = when (resultCode) {
BaseResp.ErrCode.ERR_OK -> "微信支付成功"
BaseResp.ErrCode.ERR_USER_CANCEL -> "已取消微信支付"
else -> "微信支付失败"
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
LiveDataBus.get().with(RechargeActivity.WX_PAY_RESULT).value = resultCode
finish()
}
}

View File

@@ -88,6 +88,7 @@
android:textColorHint="@color/textSecondColor"
android:maxLength="11"
android:maxLines="1"
android:singleLine="true"
android:inputType="number"
android:textSize="14sp"></com.ck.ckcollar.app.widget.CKEditView>
@@ -165,7 +166,8 @@
android:textColorHint="@color/textSecondColor"
android:maxLength="50"
android:maxLines="3"
android:textSize="14sp"></com.ck.ckcollar.app.widget.CKEditView>
android:textSize="14sp"
android:singleLine="true"></com.ck.ckcollar.app.widget.CKEditView>
</com.ck.ckcollar.app.widget.CKLinearLayout>

View File

@@ -102,7 +102,7 @@
android:text="@string/no_reason_text"
android:background="@drawable/bg_cz_button"
android:paddingHorizontal="5dp"
></com.ck.ckcollar.app.widget.CKTextView>
android:visibility="gone"/>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout

View File

@@ -35,8 +35,8 @@
<com.ck.ckcollar.app.widget.CKTextView
android:id="@+id/textBalance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0.00"
android:layout_height="50dp"
android:gravity="center_vertical"
android:textColor="@color/textColor"
android:textSize="38sp"
android:textStyle="bold"

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.ck.ckcollar.app.widget.CKLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@@ -7,12 +8,12 @@
android:background="@color/backgroundColor"
>
<include layout="@layout/action_bar"></include>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
></androidx.recyclerview.widget.RecyclerView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
></androidx.recyclerview.widget.RecyclerView>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:layout_width="match_parent"
@@ -20,7 +21,6 @@
android:orientation="horizontal"
android:background="@color/white"
android:gravity="center_vertical"
android:paddingHorizontal="20dp"
>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnKeyboard"
@@ -30,39 +30,89 @@
android:visibility="gone"
></com.ck.ckcollar.app.widget.CKImageView>
<com.ck.ckcollar.app.widget.CKEditView
android:id="@+id/editMessage"
android:layout_width="match_parent"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="34dp"
android:layout_weight="1"
android:textColor="@color/textColor"
android:textSize="14sp"
android:textColorHint="@color/textColor"
android:hint="@string/please_input_message"
android:paddingHorizontal="16dp"
android:paddingVertical="5dp"
android:layout_marginVertical="10dp"
android:layout_marginHorizontal="12dp"
android:background="@drawable/bg_input_message"
android:imeOptions="actionSend"
android:inputType="text"
android:maxLength="100"
></com.ck.ckcollar.app.widget.CKEditView>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnSmile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_smile"
></com.ck.ckcollar.app.widget.CKImageView>
android:orientation="vertical">
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnMore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_more"
android:layout_marginLeft="12dp"
></com.ck.ckcollar.app.widget.CKImageView>
<FrameLayout
android:id="@+id/contentImagePreview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:visibility="gone">
<com.ck.ckcollar.app.widget.CKRoundImageView
android:id="@+id/imagePreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:roundRadius="6dp" />
<ImageView
android:id="@+id/btnDeleteImage"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="end|top"
android:layout_margin="4dp"
android:contentDescription="@null"
android:src="@mipmap/icon_img_del_red" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<com.ck.ckcollar.app.widget.CKEditView
android:id="@+id/editMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="34dp"
android:textColor="@color/textColor"
android:textSize="14sp"
android:textColorHint="@color/textColor"
android:hint="@string/please_input_message"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:layout_marginVertical="10dp"
android:layout_marginHorizontal="12dp"
android:background="@drawable/bg_input_message"
android:imeOptions="actionSend"
android:inputType="text"
android:maxHeight="70dp"
android:maxLines="4"
android:scrollbars="vertical"
android:maxLength="100" />
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnSmile"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/image_smile"
android:visibility="gone"/>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnMore"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/image_more"
android:layout_marginLeft="12dp"
android:layout_marginRight="15dp"/>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/btnSend"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_send_yellow"
android:layout_marginLeft="12dp"
android:layout_marginRight="15dp"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout

View File

@@ -165,6 +165,16 @@
android:background="@drawable/bg_login_button"
></com.ck.ckcollar.app.widget.CKButton>
<com.ck.ckcollar.app.widget.CKButton
android:id="@+id/btnQuickLogin"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="10dp"
android:background="@drawable/bg_login_button"
android:text="临时快捷登录"
android:textColor="@color/buttonTextColor"
android:visibility="gone"/>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"

View File

@@ -36,7 +36,7 @@
<com.ck.ckcollar.app.widget.CKLinearLayout
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_height="60dp"
android:background="?attr/mainBackground"
android:orientation="horizontal"
>

View File

@@ -33,16 +33,21 @@
android:orientation="vertical"
>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:id="@+id/priceContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="58dp"
android:baselineAligned="true"
android:baselineAlignedChildIndex="1"
android:gravity="bottom"
android:orientation="horizontal"
android:visibility="invisible">
<com.ck.ckcollar.app.widget.CKTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="¥"
android:textColor="@color/color_7B4B12"
android:textSize="22sp"></com.ck.ckcollar.app.widget.CKTextView>
android:textSize="22sp"/>
<com.ck.ckcollar.app.widget.CKTextView
android:id="@+id/textMoney"
@@ -51,14 +56,15 @@
android:text=""
android:textColor="@color/color_7B4B12"
android:textSize="48sp"
android:textStyle="bold"></com.ck.ckcollar.app.widget.CKTextView>
android:textStyle="bold"/>
<com.ck.ckcollar.app.widget.CKTextView
android:id="@+id/tv_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/月"
android:textColor="@color/color_7B4B12"
android:textSize="22sp"></com.ck.ckcollar.app.widget.CKTextView>
android:textSize="22sp"/>
</com.ck.ckcollar.app.widget.CKLinearLayout>
@@ -71,7 +77,7 @@
android:gravity="center"
android:text="@string/pet_member_open_now"
android:textColor="@color/color_CACACA"
android:textSize="16sp"></com.ck.ckcollar.app.widget.CKTextView>
android:textSize="16sp"/>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout
@@ -93,7 +99,7 @@
android:textColor="@color/color_7B4B12"
android:textSize="22sp"
android:textStyle="bold"
></com.ck.ckcollar.app.widget.CKTextView>
/>
<com.ck.ckcollar.app.widget.CKTextView
android:id="@+id/textMemberDate"
@@ -101,7 +107,7 @@
android:layout_height="wrap_content"
android:text="@string/deadline_date"
android:textColor="@color/color_7B4B12"
android:textSize="16sp"></com.ck.ckcollar.app.widget.CKTextView>
android:textSize="16sp"/>
</com.ck.ckcollar.app.widget.CKLinearLayout>
@@ -260,9 +266,9 @@
android:id="@+id/textNode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textSize="14sp"
android:textColor="@color/color_CACACA"
android:text=""
android:text="商城商品享受折扣,开通会员赠送积分"
android:layout_marginTop="20dp"
></com.ck.ckcollar.app.widget.CKTextView>

View File

@@ -80,18 +80,34 @@
android:layout_height="match_parent">
<com.scwang.smartrefresh.layout.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.scwang.smartrefresh.layout.header.ClassicsHeader>
android:layout_height="wrap_content"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="20dp"
android:layout_marginVertical="12dp"
></androidx.recyclerview.widget.RecyclerView>
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="20dp"
android:layout_marginVertical="12dp" />
<RelativeLayout
android:id="@+id/rel_noresult"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<include
android:id="@+id/include_noresult"
layout="@layout/include_no_result_layout" />
</RelativeLayout>
</FrameLayout>
<com.scwang.smartrefresh.layout.footer.ClassicsFooter
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.scwang.smartrefresh.layout.footer.ClassicsFooter>
android:layout_height="wrap_content"/>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
</com.ck.ckcollar.app.widget.CKLinearLayout>

View File

@@ -49,38 +49,6 @@
android:layout_marginBottom="10dp"
></com.ck.ckcollar.app.widget.CKTextView>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:id="@+id/btnAli"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:paddingHorizontal="20dp"
android:background="@drawable/bg_view_gray_radius_12_stroke_1"
android:gravity="center_vertical"
>
<com.ck.ckcollar.app.widget.CKImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_ali"
></com.ck.ckcollar.app.widget.CKImageView>
<com.ck.ckcollar.app.widget.CKTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="14sp"
android:textColor="@color/textColor"
android:text="@string/alipay"
android:paddingLeft="8dp"
></com.ck.ckcollar.app.widget.CKTextView>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_address_check_select"
></com.ck.ckcollar.app.widget.CKImageView>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:id="@+id/btnWX"
@@ -91,7 +59,7 @@
android:background="@drawable/bg_view_gray_radius_12_stroke_1"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
android:visibility="gone"
android:visibility="visible"
>
<com.ck.ckcollar.app.widget.CKImageView
@@ -113,10 +81,46 @@
android:id="@+id/image2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_address_check_select"
></com.ck.ckcollar.app.widget.CKImageView>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:id="@+id/btnAli"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:paddingHorizontal="20dp"
android:background="@drawable/bg_view_gray_radius_12_stroke_1"
android:gravity="center_vertical"
android:layout_marginTop="10dp">
<com.ck.ckcollar.app.widget.CKImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_ali"
></com.ck.ckcollar.app.widget.CKImageView>
<com.ck.ckcollar.app.widget.CKTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="14sp"
android:textColor="@color/textColor"
android:text="@string/alipay"
android:paddingLeft="8dp"
></com.ck.ckcollar.app.widget.CKTextView>
<com.ck.ckcollar.app.widget.CKImageView
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_address_check_narmal"
></com.ck.ckcollar.app.widget.CKImageView>
</com.ck.ckcollar.app.widget.CKLinearLayout>
<com.ck.ckcollar.app.widget.CKLinearLayout
android:id="@+id/btnBalance"
android:layout_width="match_parent"

View File

@@ -263,7 +263,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
android:visibility="gone"
android:visibility="visible"
>
<com.ck.ckcollar.app.widget.CKImageView
@@ -295,7 +295,6 @@
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
>
<com.ck.ckcollar.app.widget.CKImageView
@@ -317,7 +316,7 @@
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_address_check_select"
android:src="@mipmap/image_address_check_narmal"
></com.ck.ckcollar.app.widget.CKImageView>
</com.ck.ckcollar.app.widget.CKLinearLayout>

View File

@@ -232,7 +232,7 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
android:visibility="gone"
android:visibility="visible"
>
<com.ck.ckcollar.app.widget.CKImageView
@@ -264,7 +264,6 @@
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="10dp"
>
<com.ck.ckcollar.app.widget.CKImageView
@@ -286,7 +285,7 @@
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/image_address_check_select"
android:src="@mipmap/image_address_check_narmal"
></com.ck.ckcollar.app.widget.CKImageView>
</com.ck.ckcollar.app.widget.CKLinearLayout>

View File

@@ -14,12 +14,27 @@
android:padding="20dp"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
></androidx.recyclerview.widget.RecyclerView>
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:id="@+id/rel_noresult"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<include
android:id="@+id/include_noresult"
layout="@layout/include_no_result_layout" />
</RelativeLayout>
</FrameLayout>
<com.ck.ckcollar.app.widget.CKButton

Some files were not shown because too many files have changed in this diff Show More