@@ -6,40 +6,43 @@ import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Bundle
import android.os.Environment
import android.text.TextPaint
import android.view.LayoutInflater
import android.view.View
import android.widget.RadioButton
import android.widget.RadioGroup
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialog
import androidx.core.content.ContextCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.databinding.DialogExpireTimeBinding
import com.example.defenseapplication.databinding.InvitationCodeActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.util.Hashtable
//邀请码页面
class InvitationCodeActivity : AppCompatActivity ( ) {
private lateinit var binding : InvitationCodeActivityBinding
private var expireTimeInMillis : Long = 24 * 60 * 60 * 1000 // 默认24小时
private var expireTimeInMillis : Long = EXPIRE _24H // 默认24小时
private var selectedExpireOption : Int = 0 // 0: 24h, 1: 7d, 默认选中24h
private var generatedInviteCode : String = " " // 生成的邀请码
private var expireTimestamp : Long = 0 // 过期时间戳
private var qrCodeContent : String = " " // 生成的二维码内容
private var lastClickTime : Long = 0 // 上次点击时间,用于防止多次点击
private var expireTimeDialog : BottomSheetDialog ? = null // 时效选择弹窗
private var isLoading = false // 是否正在加载
companion object {
const val EXPIRE _24H = 24 * 60 * 60 * 1000L
@@ -61,6 +64,9 @@ class InvitationCodeActivity : AppCompatActivity() {
binding . tvExpireTime . text = getString ( R . string . expire _24h )
// 默认显示灰色占位区域,二维码不可见
hideQrCode ( )
// 页面加载时查询推荐码状态
queryRecommendationCodeStatus ( )
}
private fun initListener ( ) {
@@ -73,7 +79,7 @@ class InvitationCodeActivity : AppCompatActivity() {
}
binding . llWechatShare . setOnClickListener {
// 防止多次点击(间隔1 秒)
// 防止多次点击(间隔3 秒)
val currentTime = System . currentTimeMillis ( )
if ( currentTime - lastClickTime < 3000 ) {
return @setOnClickListener
@@ -82,6 +88,119 @@ class InvitationCodeActivity : AppCompatActivity() {
// 微信分享功能
shareToWeChat ( )
}
// 撤销邀请按钮
binding . btnCancelInvite . setOnClickListener {
cancelInvitation ( )
}
// 重新生成按钮
binding . btnRegenerate . setOnClickListener {
regenerateInvitation ( )
}
}
/**
* 查询推荐码状态
*/
private fun queryRecommendationCodeStatus ( ) {
isLoading = true
CoroutineScope ( Dispatchers . IO ) . launch {
try {
// 注意: 接口需要传入code参数, 但我们可能不知道当前的推荐码
// 这里使用空字符串或默认值进行查询
val response = RetrofitNetwork . getNetworkService ( ) . selectRecommendationCode ( " " )
withContext ( Dispatchers . Main ) {
isLoading = false
if ( response . code == 200 ) {
// 有推荐码,解析返回的数据
handleRecommendationCodeResponse ( response . data )
} else {
// 没有推荐码或查询失败, 生成默认24小时的推荐码
generateDefaultRecommendationCode ( )
}
}
} catch ( e : Exception ) {
e . printStackTrace ( )
withContext ( Dispatchers . Main ) {
isLoading = false
// 网络异常, 生成默认24小时的推荐码
generateDefaultRecommendationCode ( )
}
}
}
}
/**
* 处理推荐码查询响应
*/
private fun handleRecommendationCodeResponse ( data : Any ? ) {
// 解析返回的数据,获取推荐码和过期时间
// 这里需要根据实际接口返回格式进行解析
if ( data is Map < * , * > ) {
generatedInviteCode = data [ " code " ] ?. toString ( ) ?: " "
expireTimestamp = data [ " expireTime " ] ?. toString ( ) ?. toLongOrNull ( ) ?: 0L
// 检查推荐码是否过期
if ( expireTimestamp > 0 && System . currentTimeMillis ( ) < expireTimestamp ) {
// 推荐码有效,显示二维码
qrCodeContent = " { \" type \" : \" invite \" , \" code \" : \" $generatedInviteCode \" , \" expire \" : $expireTimestamp } "
showQrCode ( )
generateQRCode ( )
// 根据过期时间设置显示的时效
val remainingTime = expireTimestamp - System . currentTimeMillis ( )
if ( remainingTime > EXPIRE _24H ) {
// 超过24小时, 认为是7天的
expireTimeInMillis = EXPIRE _7D
selectedExpireOption = 1
binding . tvExpireTime . text = getString ( R . string . expire _7d )
} else {
expireTimeInMillis = EXPIRE _24H
selectedExpireOption = 0
binding . tvExpireTime . text = getString ( R . string . expire _24h )
}
} else {
// 推荐码已过期, 生成新的默认24小时推荐码
generateDefaultRecommendationCode ( )
}
} else {
// 数据格式不对,生成新的推荐码
generateDefaultRecommendationCode ( )
}
}
/**
* 生成默认24小时的推荐码
*/
private fun generateDefaultRecommendationCode ( ) {
expireTimeInMillis = EXPIRE _24H
selectedExpireOption = 0
binding . tvExpireTime . text = getString ( R . string . expire _24h )
// 生成推荐码( 本地生成, 实际应该调用API)
generateQRCode ( )
showQrCode ( )
}
/**
* 处理生成推荐码成功
*/
private fun handleGenerateSuccess ( data : Any ? , expireDays : Int ) {
// 解析返回的数据
if ( data is Map < * , * > ) {
generatedInviteCode = data [ " code " ] ?. toString ( ) ?: " "
expireTimestamp = data [ " expireTime " ] ?. toString ( ) ?. toLongOrNull ( ) ?: ( System . currentTimeMillis ( ) + expireTimeInMillis )
qrCodeContent = " { \" type \" : \" invite \" , \" code \" : \" $generatedInviteCode \" , \" expire \" : $expireTimestamp } "
showQrCode ( )
generateQRCode ( )
} else {
// 如果接口没有返回推荐码,本地生成一个
generateInviteCode ( )
showQrCode ( )
generateQRCode ( )
}
}
private fun showExpireTimeDialog ( ) {
@@ -108,6 +227,7 @@ class InvitationCodeActivity : AppCompatActivity() {
}
dialogBinding . tvConfirm . setOnClickListener {
// 直接设置新的时效并生成二维码
when ( selectedExpireOption ) {
0 -> {
expireTimeInMillis = EXPIRE _24H
@@ -118,7 +238,7 @@ class InvitationCodeActivity : AppCompatActivity() {
binding . tvExpireTime . text = getString ( R . string . expire _7d )
}
}
// 选择后 显示二维码并生成
// 显示二维码并生成
showQrCode ( )
generateQRCode ( )
expireTimeDialog ?. dismiss ( )
@@ -128,31 +248,31 @@ class InvitationCodeActivity : AppCompatActivity() {
}
private fun hideQrCode ( ) {
// 隐藏二维码,显示灰色占位区域
binding . ivQrCode . visibility = View . GONE
binding . ivQrLogo . visibility = View . GONE
binding . vQrPlaceholder . visibility = View . VISIBLE
}
private fun showQrCode ( ) {
// 显示二维码,隐藏灰色占位区域
binding . vQrPlaceholder . visibility = View . GONE
binding . ivQrCode . visibility = View . VISIBLE
binding . ivQrLogo . visibility = View . VISIBLE
}
private fun generateQRCode ( ) {
val content = generateInviteCode ( )
val content = if ( qrCodeContent . isNotEmpty ( ) ) qrCodeContent else generateInviteCode ( )
val qrBitmap = createQRCodeWithLogo ( content , 400 , 400 )
binding . ivQrCode . setImageBitmap ( qrBitmap )
}
private fun generateInviteCode ( ) : String {
// 生成包含邀请信息的JSON字符串
val timestamp = System . currentTimeMillis ( )
expireTimestamp = timestamp + expireTimeInMillis
generatedInviteCode = " INV ${timestamp} "
qrCodeContent = " { \" type \" : \" invite \" , \" code \" : \" $generatedInviteCode \" , \" expire \" : $expireTimestamp } "
// 输出日志,方便调试验证
android . util . Log . d ( " InvitationCode " , " 生成的二维码内容: $qrCodeContent " )
android . util . Log . d ( " InvitationCode " , " 推荐码: $generatedInviteCode , 过期时间戳: $expireTimestamp " )
return qrCodeContent
}
@@ -161,7 +281,6 @@ class InvitationCodeActivity : AppCompatActivity() {
val hints = Hashtable < EncodeHintType , Any > ( )
hints [ EncodeHintType . CHARACTER _SET ] = " UTF-8 "
hints [ EncodeHintType . ERROR _CORRECTION ] = ErrorCorrectionLevel . H
// 设置二维码边距为最小( 1)
hints [ EncodeHintType . MARGIN ] = 1
val writer = QRCodeWriter ( )
@@ -170,10 +289,8 @@ class InvitationCodeActivity : AppCompatActivity() {
val qrBitmap = Bitmap . createBitmap ( width , height , Bitmap . Config . ARGB _8888 )
val canvas = Canvas ( qrBitmap )
// 绘制白色背景
canvas . drawColor ( Color . WHITE )
// 绘制二维码
val paint = Paint ( Paint . ANTI _ALIAS _FLAG )
paint . color = Color . BLACK
@@ -194,10 +311,8 @@ class InvitationCodeActivity : AppCompatActivity() {
}
}
// 在二维码中间绘制logo
val logoDrawable = ContextCompat . getDrawable ( this , R . mipmap . loge )
if ( logoDrawable != null ) {
// logo大小为二维码的1/5
val logoSize = width / 5
val logoX = ( width - logoSize ) / 2
val logoY = ( height - logoSize ) / 2
@@ -218,51 +333,42 @@ class InvitationCodeActivity : AppCompatActivity() {
}
private fun shareToWeChat ( ) {
// 检查二维码是否过期
if ( expireTimestamp == 0L || System . currentTimeMillis ( ) > expireTimestamp ) {
ToastUtils . showError ( this , getString ( R . string . qr _code _expired ) )
return
}
// 生成分享海报
val shareBitmap = generateSharePoster ( )
if ( shareBitmap == null ) {
ToastUtils . showError ( this , getString ( R . string . share _failed ) )
return
}
// 保存图片到本地
val imagePath = saveBitmapToFile ( shareBitmap )
if ( imagePath . isEmpty ( ) ) {
ToastUtils . showError ( this , getString ( R . string . share _failed ) )
return
}
// 使用系统分享功能
shareImage ( imagePath )
}
private fun generateSharePoster ( ) : Bitmap ? {
return try {
// 获取空海报背景图
val posterBackground = ContextCompat . getDrawable ( this , R . drawable . share _poster _empty )
?: return null
// 创建画布
val width = posterBackground . intrinsicWidth
val height = posterBackground . intrinsicHeight
val bitmap = Bitmap . createBitmap ( width , height , Bitmap . Config . ARGB _8888 )
val canvas = Canvas ( bitmap )
// 绘制背景图
posterBackground . setBounds ( 0 , 0 , width , height )
posterBackground . draw ( canvas )
// 使用已生成的二维码内容生成二维码
val qrCode = createQRCodeWithLogo ( qrCodeContent , 300 , 300 )
?: return bitmap
// 绘制二维码到海报上
val qrSize = ( width * 0.38f ) . toInt ( )
val qrX = ( width - qrSize ) / 2
val qrY = ( height * 0.60f ) . toInt ( )
@@ -270,14 +376,12 @@ class InvitationCodeActivity : AppCompatActivity() {
val scaledQr = Bitmap . createScaledBitmap ( qrCode , qrSize , qrSize , true )
canvas . drawBitmap ( scaledQr , qrX . toFloat ( ) , qrY . toFloat ( ) , null )
// 获取时效文本
val expireText = if ( expireTimeInMillis == EXPIRE _24H ) {
getString ( R . string . expire _24h )
} else {
getString ( R . string . expire _7d )
}
// 绘制底部提示文字
val tipsText = getString ( R . string . share _poster _tips , expireText )
val tipsPaint = android . text . TextPaint ( ) . apply {
color = Color . parseColor ( " #0475F3 " )
@@ -297,12 +401,10 @@ class InvitationCodeActivity : AppCompatActivity() {
. setLineSpacing ( 0f , 1.4f )
. build ( )
// 计算文字位置:距离二维码底部有固定间距
val qrBottom = qrY + qrSize
val marginBelowQr = ( height * 0.04f ) . toInt ( ) // 二维码下方间距
val marginBelowQr = ( height * 0.04f ) . toInt ( )
val textY = qrBottom + marginBelowQr
// 向右偏移
val rightOffset = ( width * 0.35f ) . toInt ( )
canvas . save ( )
@@ -310,7 +412,6 @@ class InvitationCodeActivity : AppCompatActivity() {
staticLayout . draw ( canvas )
canvas . restore ( )
bitmap
} catch ( e : Exception ) {
e . printStackTrace ( )
@@ -342,7 +443,6 @@ class InvitationCodeActivity : AppCompatActivity() {
return
}
// 使用 FileProvider 获取内容 URI
val authority = " ${packageName} .fileprovider "
val contentUri = androidx . core . content . FileProvider . getUriForFile ( this , authority , file )
@@ -350,11 +450,9 @@ class InvitationCodeActivity : AppCompatActivity() {
type = " image/* "
putExtra ( Intent . EXTRA _STREAM , contentUri )
putExtra ( Intent . EXTRA _TEXT , getString ( R . string . invite _friend ) )
// 授予读取权限
flags = Intent . FLAG _GRANT _READ _URI _PERMISSION
}
// 为所有匹配的应用授予权限
val resInfoList = packageManager . queryIntentActivities ( intent , PackageManager . MATCH _DEFAULT _ONLY )
for ( resolveInfo in resInfoList ) {
val packageName = resolveInfo . activityInfo . packageName
@@ -369,9 +467,31 @@ class InvitationCodeActivity : AppCompatActivity() {
}
}
/**
* 撤销邀请
*/
private fun cancelInvitation ( ) {
// 撤销邀请逻辑
hideQrCode ( )
generatedInviteCode = " "
qrCodeContent = " "
expireTimestamp = 0
// 显示提示
ToastUtils . showSuccess ( this , getString ( R . string . operation _success ) )
}
/**
* 重新生成邀请码
*/
private fun regenerateInvitation ( ) {
// 根据当前选择的时效重新生成邀请码
generateQRCode ( )
showQrCode ( )
ToastUtils . showSuccess ( this , getString ( R . string . operation _success ) )
}
override fun onDestroy ( ) {
super . onDestroy ( )
// 关闭弹窗,防止内存泄漏
expireTimeDialog ?. dismiss ( )
}
}
}