优化时间弹窗

This commit is contained in:
2026-05-26 11:07:14 +08:00
parent d5be6a9b5f
commit 5b7d20d38c
14 changed files with 661 additions and 43 deletions

View File

@@ -31,7 +31,7 @@ import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.util.Hashtable
//邀请二维码
class InvitationCodeActivity : AppCompatActivity() {
private lateinit var binding: InvitationCodeActivityBinding

View File

@@ -1,20 +1,34 @@
package com.example.defenseapplication.personal
import android.Manifest
import android.content.ContentResolver
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.widget.Toast
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.UpdateUserInfo
import com.example.defenseapplication.bean.UserInfoBean
import com.example.defenseapplication.databinding.PersonalProfileActivityBinding
import com.example.defenseapplication.utils.OssUtil
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.AvatarSelectDialog
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.example.defenseapplication.view.PhoneModifyDialogFragment
import com.gyf.immersionbar.ImmersionBar
@@ -22,11 +36,78 @@ 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.io.IOException
import java.io.InputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class PersonalProfileActivity : AppCompatActivity() {
private lateinit var binding: PersonalProfileActivityBinding
private var userInfo: UserInfoBean? = null
// 拍照返回的文件
private var photoFile: File? = null
// 请求权限回调
private val requestPermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
} else {
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
}
val writeStorageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
true
} else {
permissions[Manifest.permission.WRITE_EXTERNAL_STORAGE] ?: false
}
if (!cameraGranted) {
Toast.makeText(this, getString(R.string.camera_permission_denied), Toast.LENGTH_SHORT).show()
}
if (!storageGranted) {
Toast.makeText(this, getString(R.string.storage_permission_denied), Toast.LENGTH_SHORT).show()
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && !writeStorageGranted) {
Toast.makeText(this, getString(R.string.write_storage_permission_denied), Toast.LENGTH_SHORT).show()
}
}
// 拍照结果回调
private val takePhotoLauncher = registerForActivityResult(
ActivityResultContracts.TakePicture()
) { success ->
if (success && photoFile != null) {
Log.d("PersonalProfile", "拍照成功,文件路径: ${photoFile?.absolutePath}")
uploadAvatar(photoFile!!.absolutePath)
} else {
Toast.makeText(this, getString(R.string.take_photo_failed), Toast.LENGTH_SHORT).show()
}
}
// 相册选择结果回调
private val selectAlbumLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK && result.data != null) {
val uri = result.data?.data
uri?.let {
val filePath = getFilePathFromUri(it)
if (filePath != null) {
Log.d("PersonalProfile", "相册选择成功,文件路径: $filePath")
uploadAvatar(filePath)
} else {
Toast.makeText(this, getString(R.string.get_file_failed), Toast.LENGTH_SHORT).show()
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PersonalProfileActivityBinding.inflate(layoutInflater)
@@ -37,6 +118,8 @@ class PersonalProfileActivity : AppCompatActivity() {
.init()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
userInfo = intent.getParcelableExtra("userInfo", UserInfoBean::class.java)
} else {
userInfo = intent.getParcelableExtra("userInfo")
}
initView()
}
@@ -49,11 +132,11 @@ class PersonalProfileActivity : AppCompatActivity() {
userInfo?.let { info ->
Log.d("头像", "头像 URL = ${info.avatar}")
val requestOptions = RequestOptions()
.placeholder(R.drawable.img) // 加载中显示
.error(R.drawable.img) // 失败时显示
.placeholder(R.drawable.img)
.error(R.drawable.img)
Glide.with(this)
.load(info.avatar) // 如果 avatar 为 nullGlide 会直接回调 error
.load(info.avatar)
.apply(requestOptions)
.circleCrop()
.into(binding.ivAvatar)
@@ -61,6 +144,10 @@ class PersonalProfileActivity : AppCompatActivity() {
binding.tvPhone.text = info.phonenumber ?: ""
}
binding.ivAvatar.setOnClickListener {
showAvatarSelectDialog()
}
binding.itemNickname.setOnClickListener {
showNicknameDialog()
}
@@ -151,4 +238,188 @@ class PersonalProfileActivity : AppCompatActivity() {
}
}
}
}
private fun showAvatarSelectDialog() {
AvatarSelectDialog(this)
.setOnTakePhotoListener {
checkCameraPermission { granted ->
if (granted) {
takePhoto()
}
}
}
.setOnSelectAlbumListener {
checkStoragePermission { granted ->
if (granted) {
selectAlbum()
}
}
}
.show()
}
/**
* 检查相机权限
*/
private fun checkCameraPermission(callback: (Boolean) -> Unit) {
val permissions = mutableListOf(Manifest.permission.CAMERA)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
val allGranted = permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
if (allGranted) {
callback(true)
} else {
requestPermissionsLauncher.launch(permissions.toTypedArray())
callback(false)
}
}
/**
* 检查存储权限
*/
private fun checkStoragePermission(callback: (Boolean) -> Unit) {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
callback(true)
} else {
requestPermissionsLauncher.launch(arrayOf(permission))
callback(false)
}
}
/**
* 调用系统相机拍照
*/
private fun takePhoto() {
try {
photoFile = createImageFile()
val photoUri = FileProvider.getUriForFile(
this,
"$packageName.fileprovider",
photoFile!!
)
takePhotoLauncher.launch(photoUri)
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this, getString(R.string.create_file_failed), Toast.LENGTH_SHORT).show()
}
}
/**
* 创建图片文件
*/
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${timeStamp}_",
".jpg",
storageDir
).apply {
// 保存文件路径以便后续使用
Log.d("PersonalProfile", "创建图片文件: $absolutePath")
}
}
/**
* 调用系统相册选择图片
*/
private fun selectAlbum() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
selectAlbumLauncher.launch(intent)
}
/**
* 从 Uri 获取文件路径
*/
private fun getFilePathFromUri(uri: Uri): String? {
val contentResolver: ContentResolver = contentResolver
val inputStream: InputStream? = contentResolver.openInputStream(uri)
inputStream?.use {
try {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val file = File(storageDir, "JPEG_${timeStamp}_temp.jpg")
FileOutputStream(file).use { outputStream ->
val buffer = ByteArray(1024)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
outputStream.write(buffer, 0, bytesRead)
}
}
return file.absolutePath
} catch (e: IOException) {
e.printStackTrace()
return null
}
}
return null
}
/**
* 上传头像到 OSS 并更新个人信息
*/
private fun uploadAvatar(filePath: String) {
Toast.makeText(this, getString(R.string.uploading), Toast.LENGTH_SHORT).show()
OssUtil.uploadFileAsync(this, filePath, "avatar") { ossUrl ->
runOnUiThread {
if (ossUrl != null) {
Log.d("PersonalProfile", "OSS 上传成功URL: $ossUrl")
updateAvatar(ossUrl)
} else {
Toast.makeText(this@PersonalProfileActivity, getString(R.string.upload_failed), Toast.LENGTH_SHORT).show()
}
}
}
}
/**
* 调用修改个人信息接口更新头像
*/
private fun updateAvatar(avatarUrl: String) {
CoroutineScope(Dispatchers.IO).launch {
try {
val request = UpdateUserInfo(
userId = userInfo?.userId,
avatar = avatarUrl
)
val response = RetrofitNetwork.getNetworkService().updateUserInfo(request)
withContext(Dispatchers.Main) {
if (response.code == 200) {
// 更新本地头像显示
Glide.with(this@PersonalProfileActivity)
.load(avatarUrl)
.apply(RequestOptions()
.placeholder(R.drawable.img)
.error(R.drawable.img))
.circleCrop()
.into(binding.ivAvatar)
userInfo = userInfo?.copy(avatar = avatarUrl)
Toast.makeText(this@PersonalProfileActivity, getString(R.string.operation_success), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@PersonalProfileActivity, getString(R.string.operation_failed), Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@PersonalProfileActivity, getString(R.string.network_error), Toast.LENGTH_SHORT).show()
}
}
}
}
}