集成退出登录

后台对接通知服务
master
pan 4 years ago
parent f15fd6588e
commit 61671b3f1b
  1. 1
      background/src/main/AndroidManifest.xml
  2. 6
      background/src/main/java/com/gyf/csams/account/model/LoginViewModel.kt
  3. 1
      background/src/main/java/com/gyf/csams/account/ui/LoginActivity.kt
  4. 19
      background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  5. 1
      background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt
  6. 10
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  7. 49
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  8. 29
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  9. 5
      foreground/src/main/java/com/gyf/csams/message/model/SysMessageViewModel.kt
  10. 47
      lib/src/main/java/com/gyf/lib/model/AbstractLoginViewModel.kt
  11. 26
      lib/src/main/java/com/gyf/lib/service/BaseActivity.kt
  12. 13
      lib/src/main/java/com/gyf/lib/service/MessageService.kt
  13. 70
      lib/src/main/java/com/gyf/lib/service/NotificationWorker.kt
  14. 6
      lib/src/main/java/com/gyf/lib/util/Api.kt
  15. 31
      lib/src/main/java/com/gyf/lib/util/ContextUtil.kt
  16. 9
      lib/src/main/java/com/gyf/lib/util/TokenUtil.kt
  17. 27
      lib/src/main/java/com/gyf/lib/util/vo.kt

@ -35,6 +35,7 @@
<activity android:name=".main.ui.ManagerActActivity" />
<activity android:name=".main.ui.CheckActActivity" />
<activity android:name=".main.ui.CheckQualityReportActivity" />
<service android:name="com.gyf.lib.service.MessageService" />
</application>
</manifest>

@ -1,11 +1,14 @@
package com.gyf.csams.account.model
import android.app.Activity
import android.app.Application
import android.os.Build
import com.gyf.csams.account.ui.LoginActivity
import com.gyf.lib.model.AbstractLoginViewModel
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ClientType
data class ManagerVo(val account: String, val password: String, val device: String)
@ -28,4 +31,7 @@ class LoginViewModel(application: Application) : AbstractLoginViewModel(applicat
device = "${Build.MANUFACTURER} ${Build.MODEL}"
)
}
override val clientType: ClientType = ClientType.Background
override val loginClass: Class<out Activity> = LoginActivity::class.java
}

@ -34,7 +34,6 @@ class LoginActivity : ComponentActivity() {
setContent {
Body {
val context = LocalContext.current as LoginActivity
MainBoxFrame(
background = { /*TODO 背景图*/ },
contentAlignment = Alignment.Center

@ -2,7 +2,6 @@ package com.gyf.csams.main.ui
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -17,17 +16,25 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.account.model.LoginViewModel
import com.gyf.csams.main.model.MainViewModel
import com.gyf.csams.main.model.MenuType
import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.*
import com.gyf.lib.util.ClientType
class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Background
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body {
val model: MainViewModel = viewModel()
val loginViewModel: LoginViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel()
val person by model.person.observeAsState()
MainColumnFrame(background = { /*TODO*/ }) {
person?.let {
@ -68,6 +75,14 @@ class MainActivity : ComponentActivity() {
}, modifier = Modifier.fillMaxWidth()) {
Text(text = stringResource(id = R.string.activity_management))
}
OutlinedButton(onClick = {
loginViewModel.logout(this@MainActivity) {
scaffoldModel.update(message = it)
}
}, modifier = Modifier.fillMaxWidth()) {
Text(text = "退出登录")
}
}
}
}

@ -112,7 +112,6 @@ class ManagementOfficerActivity : ComponentActivity() {
Spacer(modifier = Modifier.weight(0.05F))
Text(text = context.getString(id), modifier = Modifier.weight(0.1F))
Spacer(modifier = Modifier.weight(0.05F))
val columnSize = 4
LazyRow(modifier = Modifier.weight(0.6F)) {
officerVoList.withIndex().forEach {

@ -1,5 +1,6 @@
package com.gyf.csams.account.model
import android.app.Activity
import android.app.Application
import android.os.Build
import androidx.lifecycle.LiveData
@ -7,15 +8,13 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
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.util.SimpleCallback
import com.gyf.lib.model.AbstractLoginViewModel
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.Api
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient
import com.gyf.lib.util.*
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -61,6 +60,9 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
override val api: AccountApi = AccountApi.ForegroundLogin
override val clientType: ClientType = ClientType.Foreground
override val loginClass: Class<out Activity> = AccountActivity::class.java
//学号
override val id = object : ValidStringForm(formDesc = "学号", textLength = 8) {
override fun check() {

@ -1,17 +1,12 @@
package com.gyf.csams.main.model
import android.app.Activity
import android.app.Application
import android.content.Intent
import androidx.lifecycle.*
import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.model.UserVo
import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.uikit.AbstractComment
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.service.NotificationDto
import com.gyf.lib.service.ReceiverType
import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.PersonInfoVo
import com.gyf.lib.uikit.StringForm
@ -53,7 +48,7 @@ class NotificationViewModel(application: Application) : AndroidViewModel(applica
}, type = object : TypeToken<ApiResponse<Long>>() {}.type),
jsonParam = NotificationDto(
receiverId = it.id,
receiverClient = ReceiverType.Foreground.name,
receiverClient = ClientType.Foreground,
token = it
)
)
@ -136,7 +131,7 @@ class MarqueeViewModel : AbstractComment() {
Logger.e(it)
callback("留言获取失败")
}, type = object : TypeToken<ApiResponse<List<LeaveMessageFormatVo>>>() {}.type),
jsonParam = OnlyToken()
jsonParam = OnlyToken(clientType = ClientType.Foreground)
)
}
}
@ -262,7 +257,6 @@ data class InfoVo(
override val desc: String
) : PersonInfoVo()
data class UserLogoutVo(val userId: Int)
/**
* 个人中心
@ -293,43 +287,4 @@ class CenterViewModel : ViewModel() {
}
}
fun logout(context: Activity, callback: (message: String) -> Unit) {
val userId = TokenManager.token?.id
if (userId != null) {
Logger.i("帐号$userId 将要退出登录")
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AccountApi.Logout),
SimpleCallback<Boolean>(
action = "登出", onSuccess = { it ->
it.body?.let {
if (it) {
viewModelScope.launch {
val db = AppDatabase.getInstance(context = context)
db?.tokenDao()?.deleteAll()
Logger.i("退出登陆成功")
context.finish()
context.startActivity(
Intent(
context,
AccountActivity::class.java
)
)
}
TokenManager.token = null
}
}
callback(it.message)
}, onFail = { callback("退出登陆失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = UserLogoutVo(userId = userId)
)
}
} else {
callback("操作异常,请联系管理员")
}
}
}

@ -2,7 +2,6 @@ package com.gyf.csams.main.ui
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
@ -25,14 +24,16 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.gyf.csams.R
import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.activity.ui.ActivityDetailActivity
import com.gyf.csams.association.ui.AssociationActivity
import com.gyf.csams.association.ui.RegAssociationActivity
import com.gyf.csams.main.model.*
import com.gyf.csams.message.ui.MessageActivity
import com.gyf.csams.uikit.*
import com.gyf.lib.service.MessageService
import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.*
import com.gyf.lib.util.ClientType
import com.gyf.lib.util.randomChinese
@ -40,13 +41,13 @@ import com.gyf.lib.util.randomChinese
* 主界面
*
*/
class MainActivity : ComponentActivity() {
class MainActivity : BaseActivity() {
override val clientType: ClientType = ClientType.Foreground
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startService(Intent(this, MessageService::class.java))
setContent {
val imageViewModel: ImageViewModel = viewModel()
@ -76,10 +77,6 @@ class MainActivity : ComponentActivity() {
}
override fun onResume() {
super.onResume()
startService(Intent(this, MessageService::class.java))
}
/**
* 个人中心
@ -89,6 +86,7 @@ class MainActivity : ComponentActivity() {
private fun Center(
model: CenterViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(),
accountViewModel: AccountViewModel = viewModel(),
navController: NavHostController
) {
MainColumnFrame(
@ -96,8 +94,6 @@ class MainActivity : ComponentActivity() {
mainMenu = MainMenu.Center,
nav = navController
) {
val context = LocalContext.current as MainActivity
val info by model.info.observeAsState()
info?.let {
@ -115,13 +111,13 @@ class MainActivity : ComponentActivity() {
verticalArrangement = Arrangement.SpaceEvenly
) {
CenterMenuItem(text = model.myAssociationDesc) {
context.startActivity(Intent(context, AssociationActivity::class.java))
startActivity(Intent(this@MainActivity, AssociationActivity::class.java))
}
CenterMenuItem(text = model.myJoinActivity)
CenterMenuItem(text = model.myLikeActivity)
CenterMenuItem(text = model.myCollectActivity)
CenterMenuItem(text = "退出登录") {
model.logout(context = context) {
accountViewModel.logout(this@MainActivity) {
scaffoldModel.update(message = it)
}
}
@ -355,7 +351,8 @@ class MainActivity : ComponentActivity() {
@Composable
private fun Notification(notificationViewModel: NotificationViewModel = viewModel()) {
val context = LocalContext.current
val count by notificationViewModel.count.observeAsState(0)
val count by notificationViewModel.count.observeAsState(null)
count?.let {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
@ -366,7 +363,7 @@ class MainActivity : ComponentActivity() {
context.startActivity(Intent(context, MessageActivity::class.java))
}) {
Icon(
painter = painterResource(id = if (count > 0) R.drawable.ic_notice else R.drawable.ic_notification),
painter = painterResource(id = if (it > 0) R.drawable.ic_notice else R.drawable.ic_notification),
contentDescription = null
)
}
@ -374,6 +371,8 @@ class MainActivity : ComponentActivity() {
}
}
}
/**
* 留言板
*

@ -7,9 +7,6 @@ import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.service.NotificationDto
import com.gyf.lib.service.PageDto
import com.gyf.lib.service.ReceiverType
import com.gyf.lib.util.*
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
@ -122,7 +119,7 @@ class SysMessageViewModel(application: Application) : ScrollViewModel<Notificati
),
jsonParam = NotificationDto(
receiverId = it.id,
receiverClient = ReceiverType.Foreground.name,
receiverClient = ClientType.Foreground,
token = it, page = PageDto(
currentPage = _currentPage.value ?: 1,
pageSize = initSize

@ -1,6 +1,8 @@
package com.gyf.lib.model
import android.app.Activity
import android.app.Application
import android.content.Intent
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@ -35,8 +37,12 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
abstract fun loginParam(): Any
abstract val clientType: ClientType
abstract val api: AccountApi
abstract val loginClass: Class<out Activity>
/**
* 登录
*
@ -77,4 +83,45 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
}
}
}
/**
* 退出登录
*
* @param context
* @param callback
*/
fun logout(context: Activity, callback: (message: String) -> Unit) {
TokenManager.token?.let {
Logger.i("帐号${it.id}将要退出登录")
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AccountApi.Logout),
HttpCallback<Boolean>(
action = "登出", onSuccess = { it ->
it.body?.let {
if (it) {
viewModelScope.launch {
val db = AppDatabase.getInstance(context = context)
db?.tokenDao()?.deleteAll()
Logger.i("退出登陆成功")
context.finish()
context.startActivity(
Intent(
context,
loginClass
)
)
}
TokenManager.token = null
}
}
callback(it.message)
}, onFail = { callback("退出登陆失败") },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = OnlyToken(token = it, clientType = clientType)
)
}
}
}
}

@ -0,0 +1,26 @@
package com.gyf.lib.service
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.gyf.lib.util.ClientType
abstract class BaseActivity : ComponentActivity() {
abstract val clientType: ClientType
private val serviceIntent
get() = Intent(this, MessageService::class.java).apply {
putExtra(ClientType::class.java.name, clientType)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startService(serviceIntent)
}
override fun onResume() {
super.onResume()
startService(serviceIntent)
}
}

@ -12,8 +12,15 @@ import com.orhanobut.logger.Logger
class MessageService : JobIntentService() {
lateinit var clientType: ClientType
val gson = Gson()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
clientType = intent?.getSerializableExtra(ClientType::class.java.name) as ClientType
return super.onStartCommand(intent, flags, startId)
}
override fun onCreate() {
super.onCreate()
Logger.i("服务启动")
@ -30,13 +37,13 @@ class MessageService : JobIntentService() {
url = Api.buildUrl(NotificationApi.Pull),
jsonParam = NotificationDto(
receiverId = it.id,
receiverClient = ReceiverType.Foreground.name,
receiverClient = clientType,
token = it
),
type = object : TypeToken<ApiResponse<List<NotificationVo>>>() {}.type
)?.let { it ->
)?.let { it1 ->
Logger.i("拉取最新通知")
it.body?.forEach {
it1.body?.forEach {
Logger.i("构造通知【${it.title}")
val builder =
NotificationCompat.Builder(applicationContext, NotificationUtil.CHANNEL_ID)

@ -1,70 +0,0 @@
package com.gyf.lib.service
import android.content.Context
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.gyf.lib.util.*
import com.orhanobut.logger.Logger
import java.net.SocketTimeoutException
data class NotificationVo(val title: String, val content: String, val id: Int)
data class PageDto(val currentPage: Long, val pageSize: Int = 10)
data class NotificationDto(
val receiverId: Int,
val receiverClient: String,
override val token: Token,
val page: PageDto? = null
) : BaseToken()
/**
* 通知接收客户端
*
*/
enum class ReceiverType {
//前台
Foreground,
//后台
Background
}
class NotificationWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun doWork(): Result {
TokenManager.token?.let {
return try {
Logger.i("开始拉取通知")
val data = HttpClient.postAsync<NotificationVo>(
url = Api.buildUrl(NotificationApi.Pull),
jsonParam = NotificationDto(
receiverId = it.id,
receiverClient = inputData.getString("receiverClient")
?: throw IllegalArgumentException("缺少receiverClient参数"),
token = it
),
type = object : TypeToken<ApiResponse<NotificationVo>>() {}.type
)
val result =
Data.Builder().putString(NotificationVo::class.java.name, Gson().toJson(data))
.build()
Logger.i("拉取通知成功:\n${result}")
Result.success(result)
} catch (e: SocketTimeoutException) {
Logger.e(e, "网络异常,拉取通知失败稍后再试")
Result.retry()
} catch (e: Exception) {
Logger.e(e, "发生未知异常,中止任务")
Result.failure()
}
}
Logger.w("找不到token信息,中止任务")
return Result.failure()
}
}

@ -1,7 +1,7 @@
package com.gyf.lib.util
import com.gyf.lib.BuildConfig
import com.gyf.lib.service.ReceiverType
import java.util.*
@ -28,10 +28,10 @@ enum class AccountApi(val path: String) : UrlPath {
CheckId("/register/checkId"),
//前台登录
ForegroundLogin("/login/${ReceiverType.Foreground.name.toLowerCase(Locale.ROOT)}"),
ForegroundLogin("/login/${ClientType.Foreground.name.toLowerCase(Locale.ROOT)}"),
//后台登陆
BackgroundLogin("/login/${ReceiverType.Background.name.toLowerCase(Locale.ROOT)}"),
BackgroundLogin("/login/${ClientType.Background.name.toLowerCase(Locale.ROOT)}"),
//前台令牌校验

@ -1,42 +1,11 @@
package com.gyf.lib.util
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.lifecycle.LiveData
import androidx.work.*
import com.gyf.lib.service.NotificationWorker
import com.gyf.lib.service.ReceiverType
import com.orhanobut.logger.Logger
import java.util.*
object ContextUtil {
private var name = "pullNotification"
private fun getNotification(context: Context): UUID {
val data = Data.Builder()
.putString("receiverClient", ReceiverType.Foreground.name)
.build()
val uploadWorkRequest: OneTimeWorkRequest =
OneTimeWorkRequestBuilder<NotificationWorker>()
.setInputData(data)
.build()
Logger.i("构建任务")
val workManager = WorkManager.getInstance(context)
workManager.enqueueUniqueWork(name, ExistingWorkPolicy.KEEP, uploadWorkRequest)
return uploadWorkRequest.id
}
fun getNotificationLiveData(context: Context): LiveData<WorkInfo> {
val id = getNotification(context = context)
return WorkManager.getInstance(context).getWorkInfoByIdLiveData(id)
}
fun getNotification(context: Context, callback: (wordId: UUID) -> Unit) {
callback(getNotification(context = context))
}
fun hideKeyBoard(activity: Activity?) {
Logger.i("隐藏软键盘")

@ -16,14 +16,19 @@ data class Token(
@ColumnInfo val createTime: Long
)
abstract class ClientBaseVo {
abstract val token: Token
abstract val clientType: ClientType
}
abstract class BaseToken {
abstract val token: Token
}
data class OnlyToken(
override val token: Token = TokenManager.token ?: throw IllegalArgumentException("无法获取token")
) : BaseToken()
override val token: Token = TokenManager.token ?: throw IllegalArgumentException("无法获取token"),
override val clientType: ClientType
) : ClientBaseVo()
@Dao
interface TokenDao {

@ -0,0 +1,27 @@
package com.gyf.lib.util
data class NotificationVo(val title: String, val content: String, val id: Int)
data class PageDto(val currentPage: Long, val pageSize: Int = 10)
data class NotificationDto(
val receiverId: Int,
val receiverClient: ClientType,
override val token: Token,
val page: PageDto? = null,
override val clientType: ClientType = receiverClient
) : ClientBaseVo()
/**
* 客户端类型
*
*/
enum class ClientType {
//前台
Foreground,
//后台
Background
}
Loading…
Cancel
Save