diff --git a/background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt b/background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt index e0d5dc4..59e7f2c 100644 --- a/background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt +++ b/background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt @@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.OutlinedButton import androidx.compose.material.Text +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -17,16 +19,16 @@ import com.gyf.csams.R import com.gyf.csams.account.model.LoginViewModel import com.gyf.csams.main.model.MenuType import com.gyf.csams.module.ClientType -import com.gyf.csams.module.ManagerInfoVo import com.gyf.lib.service.BaseActivity import com.gyf.lib.uikit.* - import com.gyf.lib.util.TokenManager class MainActivity : BaseActivity() { override val clientType: ClientType = ClientType.Background + private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -34,17 +36,28 @@ class MainActivity : BaseActivity() { Body { val loginViewModel: LoginViewModel = viewModel() val scaffoldModel: ScaffoldModel = viewModel() - MainColumnFrame(background = { /*TODO*/ }) { - (TokenManager.getPersonInfo() as? ManagerInfoVo)?.let { + + MainColumnFrame(background = { }) { + val photoLauncher = choosePhoto.photoLauncher() + val permissionLauncher = + choosePhoto.permissionLauncher(photoLauncher = photoLauncher) + val personInfoData by TokenManager.getPersonInfoData().observeAsState() + personInfoData?.let { Profile( modifier = Modifier - .weight(0.3F) + .weight(0.4F) .padding(10.dp), - personInfoVo = it + personInfoVo = it, + setHeadImg = { + choosePhoto.requestPhoto( + photoLauncher = photoLauncher, + permissionLauncher = permissionLauncher + ) + } ) } Column( - modifier = Modifier.weight(0.7F), + modifier = Modifier.weight(0.6F), verticalArrangement = Arrangement.SpaceEvenly ) { diff --git a/background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt b/background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt index 203c240..2774482 100644 --- a/background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt +++ b/background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt @@ -104,7 +104,8 @@ class ManagementOfficerActivity : ComponentActivity() { .width(200.dp) .fillMaxHeight() .border(width = 1.dp, color = MaterialTheme.colors.onBackground), - personInfoVo = it.value + personInfoVo = it.value, + readOnly = true ) { Logger.i("expanded=$expanded") Text( diff --git a/background/src/main/java/com/gyf/csams/uikit/CheckForm.kt b/background/src/main/java/com/gyf/csams/uikit/CheckForm.kt index af7ca25..0f39d3d 100644 --- a/background/src/main/java/com/gyf/csams/uikit/CheckForm.kt +++ b/background/src/main/java/com/gyf/csams/uikit/CheckForm.kt @@ -14,7 +14,6 @@ import com.gyf.csams.R import com.gyf.csams.main.model.BaseAuditViewModel import com.gyf.csams.module.AuditVo import com.gyf.csams.module.Duty -import com.gyf.csams.module.ManagerVo import com.gyf.lib.uikit.* import com.gyf.lib.util.BottomButton import com.gyf.lib.util.TokenManager @@ -78,7 +77,7 @@ inline fun > CheckForm( ) { - (TokenManager.getOwnInfo() as? ManagerVo)?.let { it -> + TokenManager.getManagerInfo()?.let { it -> var confirmDesc: Int? = null var backDesc: Int? = null var onConfirm: (() -> Unit)? = null diff --git a/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt b/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt index fa6bd27..b9d355d 100644 --- a/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt @@ -3,7 +3,6 @@ package com.gyf.csams.account.model import android.app.Activity import android.app.Application import android.os.Build -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -11,11 +10,17 @@ import com.google.gson.reflect.TypeToken import com.gyf.csams.R import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountRoute -import com.gyf.csams.module.* +import com.gyf.csams.module.ApiResponse +import com.gyf.csams.module.ClientType +import com.gyf.csams.module.UserLoginVo +import com.gyf.csams.module.UserRegVo import com.gyf.lib.model.AbstractLoginViewModel import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.ValidStringForm -import com.gyf.lib.util.* +import com.gyf.lib.util.AccountApi +import com.gyf.lib.util.Api +import com.gyf.lib.util.HttpCallback +import com.gyf.lib.util.HttpClient import com.orhanobut.logger.Logger import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -204,26 +209,3 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic } -class RefreshViewModel(application: Application) : AndroidViewModel(application) { - - private val _refresh = MutableLiveData() - val refresh: LiveData = _refresh - - fun refresh() { - viewModelScope.launch { - HttpClient.post( - url = Api.buildUrl(AccountApi.Refresh), - callback = HttpCallback(action = "刷新个人信息", onSuccess = { it -> - it.body.let { - if (it != null) { - TokenManager.update(it) - } - _refresh.postValue(it != null) - } - - }, typeToken = object : TypeToken>() {}.type), - jsonParam = OnlyToken(clientType = ClientType.Foreground) - ) - } - } -} \ No newline at end of file diff --git a/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt b/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt index 2770f37..152a4e5 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/model/ApplyActViewModel.kt @@ -586,11 +586,8 @@ class ApplyActViewModel(application: Application) : AndroidViewModel(application * 提交申请 * */ - fun apply(callback: (message: String) -> Unit) { + fun apply(associationId: Int, callback: (message: String) -> Unit) { - - val associationId = (TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId - ?: throw IllegalArgumentException("社团id为空") viewModelScope.launch { HttpClient.post( url = Api.buildUrl(ActivityApi.Register), diff --git a/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt b/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt index 3be767b..a31fea2 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt @@ -379,21 +379,14 @@ class ActivityDetailActivity : ComponentActivity() { contentAlignment = Alignment.Center ) { val photo = rememberCoilPainter(request = Api.buildUrl(vo.url)) - Image( - painter = photo, - contentDescription = vo.name, - ) - when (photo.loadState) { - is ImageLoadState.Loading -> { - // Display a circular progress indicator whilst loading - CircularProgressIndicator(Modifier.align(Alignment.Center)) - } - is ImageLoadState.Error -> { - // If you wish to display some content if the request fails - } - else -> { - } + when (photo.loadState) { + is ImageLoadState.Loading -> CircularProgressIndicator(Modifier.align(Alignment.Center)) + is ImageLoadState.Success -> Image( + painter = photo, + contentDescription = vo.name, + ) + else -> Text(text = "图片加载失败") } } } diff --git a/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt b/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt index f8bd37f..dff405a 100644 --- a/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/activity/ui/ApplyActActivity.kt @@ -41,6 +41,7 @@ import com.gyf.csams.MainApplication import com.gyf.csams.MyLocationListener import com.gyf.csams.R import com.gyf.csams.activity.model.ApplyActViewModel +import com.gyf.csams.association.ui.AssociationActivity import com.gyf.csams.module.CheckStatus import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.BackgroundImage @@ -71,6 +72,15 @@ class ApplyActActivity : AppCompatActivity() { private lateinit var mLocationClient: LocationClient + private val associationId: Int + get() { + val id = intent.getIntExtra( + AssociationActivity::class.java.name, + 0 + ) + return if (id == 0) throw IllegalArgumentException("不存在社团id,获取失败") else id + } + override fun onStop() { super.onStop() mLocationClient.stop() @@ -216,7 +226,7 @@ class ApplyActActivity : AppCompatActivity() { ), confirmDesc = if (checkInfo?.body != null) R.string.reg_again_btn else R.string.confirm_btn, ) { - model.apply { + model.apply(associationId = associationId) { scaffoldModel.update(message = it, actionLabel = "返回") { onBackPressed() } diff --git a/foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt b/foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt index 3554ab9..c9710bc 100644 --- a/foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt +++ b/foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import android.graphics.Bitmap import android.net.Uri import androidx.core.graphics.drawable.toBitmap -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -17,6 +16,7 @@ import com.gyf.csams.module.AssociationCheckVo import com.gyf.csams.module.AssociationRegVo import com.gyf.csams.module.ClientType import com.gyf.lib.BuildConfig +import com.gyf.lib.model.ChoosePhotoViewModel import com.gyf.lib.uikit.AsyncStringForm import com.gyf.lib.uikit.FormStatus import com.gyf.lib.util.* @@ -24,19 +24,15 @@ import com.orhanobut.logger.Logger import kotlinx.coroutines.launch import java.io.File import java.io.FileOutputStream -import java.io.InputStream -class RegAssociationViewModel(application: Application) : AndroidViewModel(application) { +class RegAssociationViewModel(application: Application) : ChoosePhotoViewModel(application) { val frameDesc = "社团注册资料" val name = AsyncStringForm(formDesc = "社团名称", textLength = 5) val desc = AsyncStringForm(formDesc = "社团简介", textLength = 30) - private val _picture = MutableLiveData() - val picture: LiveData = _picture - private val _fileId = MutableLiveData() val fileId: LiveData = _fileId @@ -51,17 +47,6 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli read() } - fun setPicture(uri: Uri) { - _picture.value = uri - } - - private fun getInputSteam(): InputStream? { - _picture.value?.let { - val resolver = getApplication().contentResolver - return resolver.openInputStream(it) - } - throw IllegalArgumentException(UNKNOW_ERROR) - } /** * 加载历史提交的注册资料,可能为空 @@ -73,17 +58,17 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli Api.buildUrl(AssociationApi.Read), HttpCallback(action = "加载历史注册资料", onSuccess = { it -> _checkInfo.postValue(it) - it.body?.let { it -> - name.setValue(it.associationVo.name) - desc.setValue(it.associationVo.desc) + it.body?.let { checkVo -> + name.setValue(checkVo.associationVo.name) + desc.setValue(checkVo.associationVo.desc) val context = getApplication() val request = ImageRequest.Builder(context) - .data("${BuildConfig.SERVER_ADDRESS}/${it.associationVo.logo}") + .data("${BuildConfig.SERVER_ADDRESS}/${checkVo.associationVo.logo}") .target( onSuccess = { result -> - it.associationVo.logo.split("/").apply { + checkVo.associationVo.logo.split("/").apply { File.createTempFile(last(), null, context.cacheDir).apply { Logger.d("文件路径:${absolutePath}") FileOutputStream(this).use { @@ -97,7 +82,7 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli .apply { Logger.i("读取缓存图片") setPicture(Uri.fromFile(this)) - _fileId.postValue(it.fileId) + _fileId.postValue(checkVo.fileId) } } diff --git a/foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt b/foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt index 07836e4..b6fb0c4 100644 --- a/foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt @@ -400,45 +400,15 @@ class AssociationActivity : ComponentActivity() { } val data by model.data.observeAsState() val listState = rememberLazyListState() - LazyColumn(state = listState, modifier = modifier) { + LazyColumn( + state = listState, modifier = modifier, verticalArrangement = + Arrangement.spacedBy(space = 10.dp, alignment = Alignment.CenterVertically) + ) { data?.forEach { item { - Column { - Spacer(modifier = Modifier.height(10.dp)) - Row { - val weight = 1F / 3 - Spacer(modifier = Modifier.weight(weight)) - Card( - modifier = Modifier.weight(weight), - backgroundColor = MaterialTheme.colors.background - ) { - Text(text = it.name) - } - Spacer(modifier = Modifier.weight(weight)) - } - Spacer(modifier = Modifier.height(10.dp)) - Divider(color = MaterialTheme.colors.background) - } + Profile(modifier = Modifier.height(300.dp), personInfoVo = it, readOnly = true) } } - item { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxWidth() - ) { - IconButton(onClick = { - model.load(associationId) { - - } - }) { - Icon( - painter = painterResource(id = R.drawable.ic_arrow_down), - contentDescription = null - ) - } - } - - } } } diff --git a/foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt b/foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt index 0c831e1..61c06bd 100644 --- a/foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt @@ -1,15 +1,10 @@ package com.gyf.csams.association.ui -import android.Manifest import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle -import android.provider.MediaStore import androidx.activity.ComponentActivity -import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image import androidx.compose.foundation.border import androidx.compose.foundation.layout.* @@ -24,7 +19,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.coil.rememberCoilPainter import com.gyf.csams.R @@ -43,6 +37,9 @@ import com.orhanobut.logger.Logger * */ class RegAssociationActivity : ComponentActivity() { + + private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { @@ -111,7 +108,6 @@ class RegAssociationActivity : ComponentActivity() { } - /** * 社团Logo * @@ -123,42 +119,11 @@ class RegAssociationActivity : ComponentActivity() { scaffoldModel: ScaffoldModel = viewModel(), modifier: Modifier ) { - val photoIntent = Intent( - Intent.ACTION_PICK, - MediaStore.Images.Media.EXTERNAL_CONTENT_URI - ) - val uri: Uri? by model.picture.observeAsState() - - val resultLauncher = - rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { - when (it.resultCode) { - RESULT_OK -> { - - it.data?.data?.apply { - Logger.i("uri=$this") - model.setPicture(this) - } - } - } - } - - val loadPicture = { - //model.loadPicture(context) - resultLauncher.launch(photoIntent) - } - val launcher = rememberLauncherForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted: Boolean -> - if (isGranted) { - // Permission Accepted: Do something - loadPicture() - } else { - // Permission Denied: Do something - scaffoldModel.update(message = getString(R.string.denined_photo_permission)) - } - } + val uri: Uri? by model.picture.observeAsState() + val photoLauncher = choosePhoto.photoLauncher(model = model) + val permissionLauncher = choosePhoto.permissionLauncher(photoLauncher = photoLauncher) Box(contentAlignment = Alignment.Center, modifier = modifier) { @@ -171,19 +136,10 @@ class RegAssociationActivity : ComponentActivity() { if (uri == null) { OutlinedButton(onClick = { - when (PackageManager.PERMISSION_GRANTED) { - ContextCompat.checkSelfPermission( - applicationContext, - Manifest.permission.READ_EXTERNAL_STORAGE - ) -> { - // Some works that require permission - loadPicture() - } - else -> { - // Asking for permission - launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) - } - } + choosePhoto.requestPhoto( + photoLauncher = photoLauncher, + permissionLauncher = permissionLauncher + ) }) { Text(text = model.picturePlaceHolder) } @@ -201,7 +157,7 @@ class RegAssociationActivity : ComponentActivity() { verticalArrangement = Arrangement.SpaceEvenly ) { IconButton(onClick = { - loadPicture() + choosePhoto.loadPicture(photoLauncher = photoLauncher) }) { Image( painter = painterResource(id = R.drawable.ic_exchange_rate), diff --git a/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt b/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt index 1bd0e62..56fbe8a 100644 --- a/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt +++ b/foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt @@ -33,7 +33,6 @@ import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.rememberPagerState import com.gyf.csams.R import com.gyf.csams.account.model.AccountViewModel -import com.gyf.csams.account.model.RefreshViewModel import com.gyf.csams.activity.ui.ActivityDetailActivity import com.gyf.csams.association.ui.AssociationActivity import com.gyf.csams.association.ui.RegAssociationActivity @@ -41,6 +40,7 @@ import com.gyf.csams.main.model.* import com.gyf.csams.message.ui.SysMessageActivity import com.gyf.csams.module.* import com.gyf.csams.uikit.* +import com.gyf.lib.model.RefreshViewModel import com.gyf.lib.service.BaseActivity import com.gyf.lib.uikit.* import com.gyf.lib.util.* @@ -55,6 +55,8 @@ class MainActivity : BaseActivity() { override val clientType: ClientType = ClientType.Foreground + private val choosePhoto: ChoosePhoto = ChoosePhoto(activity = this) + @ExperimentalPagerApi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -100,23 +102,36 @@ class MainActivity : BaseActivity() { accountViewModel: AccountViewModel = viewModel(), navController: NavHostController ) { + MainColumnFrame( background = { Background(image = BackgroundImage.Center, alpha = 0.5F) }, mainMenu = MainMenu.Center, nav = navController ) { - Profile( - modifier = Modifier - .weight(0.3F) - .padding(10.dp) - ) + val photoLauncher = choosePhoto.photoLauncher() + val permissionLauncher = choosePhoto.permissionLauncher(photoLauncher = photoLauncher) + val personInfoData by TokenManager.getPersonInfoData().observeAsState() + personInfoData?.let { + Profile( + modifier = Modifier + .weight(0.4F) + .padding(10.dp), + setHeadImg = { + choosePhoto.requestPhoto( + photoLauncher = photoLauncher, + permissionLauncher = permissionLauncher + ) + }, + personInfoVo = it + ) + } Column( - modifier = Modifier.weight(0.7F), + modifier = Modifier.weight(0.6F), verticalArrangement = Arrangement.SpaceEvenly ) { - (TokenManager.getOwnInfo() as? UserVo)?.associationVo?.associationId?.let { + TokenManager.getUserInfo()?.associationVo?.associationId?.let { CenterMenuItem(text = model.myAssociationDesc) { startActivity( Intent( @@ -215,8 +230,7 @@ class MainActivity : BaseActivity() { mainMenu = MainMenu.List, nav = navController ) { - val associationVo: AssociationVo? = - (TokenManager.getOwnInfo() as? UserVo)?.associationVo + val associationVo = TokenManager.getUserInfo()?.associationVo if (associationVo == null) { RegisterAssociation(navController = navController) } @@ -506,37 +520,31 @@ class MainActivity : BaseActivity() { .fillMaxWidth() ) { when (photo.loadState) { - is ImageLoadState.Loading -> { - // Display a circular progress indicator whilst loading - CircularProgressIndicator() + is ImageLoadState.Loading -> CircularProgressIndicator() + is ImageLoadState.Success -> it[page].apply { + Image( + painter = photo, + contentDescription = activityVo.activityName, + modifier = Modifier.clickable(onClick = { + startActivity( + Intent( + this@MainActivity, + ActivityDetailActivity::class.java + ).apply { + putExtra( + ActivityDetailActivity::class.java.name, + activityVo.activityId + ) + putExtra( + AssociationActivity::class.java.name, + associationVo.associationId + ) + }) + }) + ) } - is ImageLoadState.Error -> { - // If you wish to display some content if the request fails - Logger.e("轮播图加载失败") - } - else -> - it[page].apply { - Image( - painter = photo, - contentDescription = activityVo.activityName, - modifier = Modifier.clickable(onClick = { - startActivity( - Intent( - this@MainActivity, - ActivityDetailActivity::class.java - ).apply { - putExtra( - ActivityDetailActivity::class.java.name, - activityVo.activityId - ) - putExtra( - AssociationActivity::class.java.name, - associationVo.associationId - ) - }) - }) - ) - } + else -> Text(text = "图片加载失败") + } } diff --git a/foreground/src/main/res/values-en/strings.xml b/foreground/src/main/res/values-en/strings.xml index 925e6c5..474a3cc 100644 --- a/foreground/src/main/res/values-en/strings.xml +++ b/foreground/src/main/res/values-en/strings.xml @@ -6,7 +6,6 @@ 申请入团 更新题库 提交答案 - 相册权限请求失败,无法读取相册 部分权限请求失败,无法获取地理位置 城市 diff --git a/foreground/src/main/res/values-zh/strings.xml b/foreground/src/main/res/values-zh/strings.xml index 925e6c5..474a3cc 100644 --- a/foreground/src/main/res/values-zh/strings.xml +++ b/foreground/src/main/res/values-zh/strings.xml @@ -6,7 +6,6 @@ 申请入团 更新题库 提交答案 - 相册权限请求失败,无法读取相册 部分权限请求失败,无法获取地理位置 城市 diff --git a/foreground/src/main/res/values/strings.xml b/foreground/src/main/res/values/strings.xml index 86dea0d..dc7e3ad 100644 --- a/foreground/src/main/res/values/strings.xml +++ b/foreground/src/main/res/values/strings.xml @@ -6,7 +6,6 @@ 申请入团 更新题库 提交答案 - 相册权限请求失败,无法读取相册 部分权限请求失败,无法获取地理位置 城市 diff --git a/lib/src/main/java/com/gyf/lib/model/ChoosePhotoViewModel.kt b/lib/src/main/java/com/gyf/lib/model/ChoosePhotoViewModel.kt new file mode 100644 index 0000000..23b8ba7 --- /dev/null +++ b/lib/src/main/java/com/gyf/lib/model/ChoosePhotoViewModel.kt @@ -0,0 +1,57 @@ +package com.gyf.lib.model + +import android.app.Application +import android.net.Uri +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.google.gson.reflect.TypeToken +import com.gyf.csams.module.ApiResponse +import com.gyf.lib.util.* +import kotlinx.coroutines.launch +import java.io.File +import java.io.InputStream + +open class ChoosePhotoViewModel(application: Application) : AndroidViewModel(application) { + protected val _picture = MutableLiveData() + val picture: LiveData = _picture + + fun setPicture(uri: Uri) { + _picture.value = uri + } + + + protected fun getInputSteam(): InputStream? { + _picture.value?.let { + val resolver = getApplication().contentResolver + return resolver.openInputStream(it) + } + throw IllegalArgumentException("图片uri为空") + } + + /** + * 上传头像 + * + * @param callback + */ + fun upload(callback: (flag: Boolean) -> Unit) { + viewModelScope.launch { + getInputSteam()?.readBytes()?.apply { + val context = getApplication() + runCatching { + val cacheFile = File(context.cacheDir, "${System.currentTimeMillis()}") + cacheFile.writeBytes(this@apply) + HttpClient.uploadFile( + url = Api.buildUrl(AccountApi.UploadHeadimg), + HttpCallback(action = "上传头像", onSuccess = { + it.body?.let(callback) + _picture.postValue(null) + }, typeToken = object : TypeToken>() {}.type), + fileList = arrayOf(cacheFile) + ) + } + } + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/gyf/lib/model/RefreshViewModel.kt b/lib/src/main/java/com/gyf/lib/model/RefreshViewModel.kt new file mode 100644 index 0000000..d8785bc --- /dev/null +++ b/lib/src/main/java/com/gyf/lib/model/RefreshViewModel.kt @@ -0,0 +1,61 @@ +package com.gyf.lib.model + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.google.gson.reflect.TypeToken +import com.gyf.csams.module.* +import com.gyf.lib.uikit.AsyncStringForm +import com.gyf.lib.util.* +import kotlinx.coroutines.launch + +class RefreshViewModel(application: Application) : AndroidViewModel(application) { + + private val _refresh = MutableLiveData() + val refresh: LiveData = _refresh + + val name = AsyncStringForm(formDesc = "姓名", textLength = 4) + val desc = AsyncStringForm(formDesc = "简介", textLength = 20) + + fun onSuccess(res: ApiResponse) { + res.body.let { + if (it != null) { + TokenManager.update(it) + } + _refresh.postValue(it != null) + } + } + + fun refresh() { + viewModelScope.launch { + val jsonParam = InfoUpdateVo( + name = name.formValue.value, desc = desc.formValue.value, + token = TokenManager.getToken(), + clientType = TokenManager.getClientType() + ) + when (jsonParam.clientType) { + ClientType.Foreground -> HttpClient.post( + url = Api.buildUrl(AccountApi.Refresh), + callback = HttpCallback( + action = "刷新个人信息", + onSuccess = { onSuccess(it) }, + typeToken = object : TypeToken>() {}.type + ), + jsonParam = jsonParam + ) + ClientType.Background -> HttpClient.post( + url = Api.buildUrl(AccountApi.Refresh), + callback = HttpCallback( + action = "刷新个人信息", + onSuccess = { onSuccess(it) }, + typeToken = object : TypeToken>() {}.type + ), + jsonParam = jsonParam + ) + } + + } + } +} \ No newline at end of file diff --git a/lib/src/main/java/com/gyf/lib/uikit/ChoosePhoto.kt b/lib/src/main/java/com/gyf/lib/uikit/ChoosePhoto.kt new file mode 100644 index 0000000..b22e709 --- /dev/null +++ b/lib/src/main/java/com/gyf/lib/uikit/ChoosePhoto.kt @@ -0,0 +1,83 @@ +package com.gyf.lib.uikit + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.provider.MediaStore +import androidx.activity.ComponentActivity +import androidx.activity.compose.ManagedActivityResultLauncher +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.Composable +import androidx.core.content.ContextCompat +import androidx.lifecycle.viewmodel.compose.viewModel +import com.gyf.lib.R +import com.gyf.lib.model.ChoosePhotoViewModel +import com.orhanobut.logger.Logger + +class ChoosePhoto(val activity: ComponentActivity) { + + fun loadPicture(photoLauncher: ManagedActivityResultLauncher) { + photoLauncher.launch( + Intent( + Intent.ACTION_PICK, + MediaStore.Images.Media.EXTERNAL_CONTENT_URI + ) + ) + } + + @Composable + fun photoLauncher(model: ChoosePhotoViewModel = viewModel()): ManagedActivityResultLauncher { + return rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { + when (it.resultCode) { + ComponentActivity.RESULT_OK -> { + + it.data?.data?.apply { + Logger.i("uri=$this") + model.setPicture(this) + } + } + } + } + } + + @Composable + fun permissionLauncher( + photoLauncher: ManagedActivityResultLauncher, + scaffoldModel: ScaffoldModel = viewModel() + ): ManagedActivityResultLauncher { + + return rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + // Permission Accepted: Do something + loadPicture(photoLauncher = photoLauncher) + } else { + // Permission Denied: Do something + scaffoldModel.update(message = activity.getString(R.string.denined_photo_permission)) + } + } + } + + fun requestPhoto( + photoLauncher: ManagedActivityResultLauncher, + permissionLauncher: ManagedActivityResultLauncher + ) { + when (PackageManager.PERMISSION_GRANTED) { + ContextCompat.checkSelfPermission( + activity.applicationContext, + Manifest.permission.READ_EXTERNAL_STORAGE + ) -> { + // Some works that require permission + loadPicture(photoLauncher = photoLauncher) + } + else -> { + // Asking for permission + permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + } + +} diff --git a/lib/src/main/java/com/gyf/lib/uikit/Profile.kt b/lib/src/main/java/com/gyf/lib/uikit/Profile.kt index 3240748..057edc4 100644 --- a/lib/src/main/java/com/gyf/lib/uikit/Profile.kt +++ b/lib/src/main/java/com/gyf/lib/uikit/Profile.kt @@ -2,43 +2,56 @@ package com.gyf.lib.uikit import androidx.compose.foundation.Image import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.material.Card -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.coil.rememberCoilPainter +import com.google.accompanist.imageloading.ImageLoadState import com.gyf.csams.module.ManagerInfoVo import com.gyf.csams.module.ManagerVo import com.gyf.csams.module.PersonInfoVo import com.gyf.csams.module.UserVo +import com.gyf.lib.model.ChoosePhotoViewModel +import com.gyf.lib.model.RefreshViewModel import com.gyf.lib.util.Api - -import com.gyf.lib.util.TokenManager +import com.orhanobut.logger.Logger @Composable fun Profile( modifier: Modifier, - personInfoVo: PersonInfoVo = TokenManager.getOwnInfo(), + personInfoVo: PersonInfoVo, + setHeadImg: () -> Unit = {}, + choosePhotoViewModel: ChoosePhotoViewModel = viewModel(), + refreshViewModel: RefreshViewModel = viewModel(), + readOnly: Boolean = false, duty: @Composable () -> Unit = { Text( text = when (personInfoVo) { is UserVo -> personInfoVo.manager?.duty?.desc ?: "----" is ManagerVo -> personInfoVo.duty.desc is ManagerInfoVo -> personInfoVo.duty.desc - else -> throw IllegalArgumentException("个人信息类型错误:${personInfoVo}") + else -> "-----" } ) } ) { Column( - modifier = modifier, + modifier = modifier.border(1.dp, Color.Black), + verticalArrangement = Arrangement.SpaceEvenly ) { + var isEdit by remember { + mutableStateOf(false) + } + Logger.i("info:${personInfoVo}") Row( modifier = Modifier .fillMaxWidth() @@ -46,28 +59,51 @@ fun Profile( .border(width = 1.dp, color = MaterialTheme.colors.onBackground), horizontalArrangement = Arrangement.SpaceBetween ) { - + val picture by choosePhotoViewModel.picture.observeAsState() + picture?.let { + Logger.i("picture=${it}") + choosePhotoViewModel.upload { + refreshViewModel.refresh() + } + } personInfoVo.headImg.let { - if (it != null) { - val headImg = rememberCoilPainter(request = Api.buildUrl(it)) - Image( - painter = headImg, - contentDescription = null, - modifier = Modifier - .weight(0.4F) - .fillMaxHeight() - ) - } else { - Box( + when { + it != null -> { + val headImg = rememberCoilPainter(request = Api.buildUrl(it)) + + when (headImg.loadState) { + is ImageLoadState.Loading -> + Box( + modifier = Modifier + .weight(0.4F) + .fillMaxHeight(), contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + is ImageLoadState.Success -> Image( + painter = headImg, + contentDescription = null, + modifier = Modifier + .weight(0.4F) + .fillMaxHeight() + .clickable(onClick = setHeadImg) + ) + else -> Text(text = "图片加载失败") + } + } + !readOnly -> Box( modifier = Modifier .weight(0.4F) .fillMaxHeight(), contentAlignment = Alignment.Center ) { - Text(text = "没有头像") + OutlinedButton(onClick = setHeadImg) { + Text(text = "设置头像") + } } } } + Column( modifier = Modifier .weight(0.4F) @@ -76,23 +112,64 @@ fun Profile( verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = personInfoVo.name) + if (isEdit) { + BaseTextField(form = refreshViewModel.name) + } else { + Text(text = personInfoVo.name) + } duty() } } - Spacer(modifier = Modifier.weight(0.05F)) Card( backgroundColor = MaterialTheme.colors.background, - modifier = Modifier.weight(0.15F) + modifier = Modifier.weight(0.3F) ) { + Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center + modifier = Modifier.fillMaxWidth() ) { - Text(text = personInfoVo.desc) + Box( + contentAlignment = Alignment.Center, modifier = Modifier + .weight(0.8F) + .fillMaxHeight() + ) { + if (isEdit) { + BaseTextField(form = refreshViewModel.desc) + } else { + Text(text = personInfoVo.desc) + } + } + if (!readOnly) { + Box( + modifier = Modifier + .weight(0.2F) + .fillMaxHeight(), + contentAlignment = Alignment.Center + ) { + OutlinedButton(onClick = { + isEdit = !isEdit + if (isEdit) { + refreshViewModel.name.setValue(personInfoVo.name) + refreshViewModel.desc.setValue(personInfoVo.desc) + } else { + refreshViewModel.refresh() + } + }) { + if (isEdit) { + Text(text = "完成") + } else { + Text(text = "编辑") + } + + } + } + } + } + + } } } \ No newline at end of file diff --git a/lib/src/main/java/com/gyf/lib/util/Api.kt b/lib/src/main/java/com/gyf/lib/util/Api.kt index 612e160..7a5b389 100644 --- a/lib/src/main/java/com/gyf/lib/util/Api.kt +++ b/lib/src/main/java/com/gyf/lib/util/Api.kt @@ -49,6 +49,8 @@ enum class AccountApi(val path: String) : UrlPath { //加载部门干事概况 Load("/load/manager"), + UploadHeadimg("/upload/img"), + //加载部门详情 LoadDetail("${Load.path}/detail"); diff --git a/lib/src/main/java/com/gyf/lib/util/TokenUtil.kt b/lib/src/main/java/com/gyf/lib/util/TokenUtil.kt index 51e9082..d064493 100644 --- a/lib/src/main/java/com/gyf/lib/util/TokenUtil.kt +++ b/lib/src/main/java/com/gyf/lib/util/TokenUtil.kt @@ -23,7 +23,7 @@ class ClientToken( data class OnlyToken( - override val clientType: ClientType + override val clientType: ClientType = TokenManager.getClientType() ) : ClientBaseVo() { override val token: ClientToken = TokenManager.getToken() } @@ -46,51 +46,69 @@ interface TokenDao { object TokenManager { - private var ownInfo: OwnInfoVo? = null - private var personInfo: PersonInfoVo? = null - private var clientToken: ClientToken? = null + private val ownInfo = MutableLiveData() + private val personInfo = MutableLiveData() + private val clientToken = MutableLiveData() fun update(ownInfo: OwnInfoVo) { - this.ownInfo = ownInfo + this.ownInfo.postValue(ownInfo) ownInfo.token.let { - clientToken = ClientToken(id = it.id, token = it.token, createTime = it.createTime) + clientToken.postValue( + ClientToken( + id = it.id, + token = it.token, + createTime = it.createTime + ) + ) } when (ownInfo) { - is ManagerVo -> this.personInfo = ManagerInfoVo( - duty = ownInfo.duty, - name = ownInfo.name, - headImg = ownInfo.headImg, - desc = ownInfo.desc + is ManagerVo -> this.personInfo.postValue( + ManagerInfoVo( + duty = ownInfo.duty, + name = ownInfo.name, + headImg = ownInfo.headImg, + desc = ownInfo.desc + ) + ) + is UserVo -> this.personInfo.postValue( + UserInfoVo( + name = ownInfo.name, + headImg = ownInfo.headImg, + desc = ownInfo.desc + ) ) - is UserVo -> this.personInfo = - UserInfoVo(name = ownInfo.name, headImg = ownInfo.headImg, desc = ownInfo.desc) else -> throw IllegalArgumentException("token初始化失败") } Logger.i("token刷新完成") } fun clear() { - ownInfo = null + ownInfo.postValue(null) + personInfo.postValue(null) + clientToken.postValue(null) } - fun getOwnInfo(): OwnInfoVo { - return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用") + fun getClientType(): ClientType { + return when (ownInfo.value) { + is UserVo -> ClientType.Foreground + is ManagerVo -> ClientType.Background + else -> throw IllegalArgumentException("个人信息还没有初始化") + } } fun getUserInfo(): UserVo? { - return ownInfo as? UserVo + return ownInfo.value as? UserVo } - fun getManagerInfo(): ManagerVo? { - return ownInfo as? ManagerVo + fun getPersonInfoData(): LiveData { + return personInfo } - fun getPersonInfo(): PersonInfoVo { - return personInfo ?: throw IllegalArgumentException("个人信息没有初始化,非法调用") + fun getManagerInfo(): ManagerVo? { + return ownInfo.value as? ManagerVo } - fun getToken(): ClientToken { - return clientToken ?: throw IllegalArgumentException("token没有初始化,非法调用") + return clientToken.value ?: throw IllegalArgumentException("token没有初始化,非法调用") } } diff --git a/lib/src/main/res/values-en/strings.xml b/lib/src/main/res/values-en/strings.xml index 9df4988..c19dc2e 100644 --- a/lib/src/main/res/values-en/strings.xml +++ b/lib/src/main/res/values-en/strings.xml @@ -33,4 +33,5 @@ 活动日期 通过 拒绝 + 相册权限请求失败,无法读取相册 \ No newline at end of file diff --git a/lib/src/main/res/values-zh/strings.xml b/lib/src/main/res/values-zh/strings.xml index 9df4988..c19dc2e 100644 --- a/lib/src/main/res/values-zh/strings.xml +++ b/lib/src/main/res/values-zh/strings.xml @@ -33,4 +33,5 @@ 活动日期 通过 拒绝 + 相册权限请求失败,无法读取相册 \ No newline at end of file diff --git a/lib/src/main/res/values/strings.xml b/lib/src/main/res/values/strings.xml index 9df4988..c19dc2e 100644 --- a/lib/src/main/res/values/strings.xml +++ b/lib/src/main/res/values/strings.xml @@ -33,4 +33,5 @@ 活动日期 通过 拒绝 + 相册权限请求失败,无法读取相册 \ No newline at end of file