parent
0e2e960c31
commit
5f5f8bb45c
@ -0,0 +1,56 @@ |
|||||||
|
package com.gyf.csams |
||||||
|
|
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import com.gyf.csams.account.ui.AccountActivity |
||||||
|
import com.gyf.csams.ui.AnimationText |
||||||
|
import com.gyf.csams.ui.MainActivity |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
|
||||||
|
class InitActivity : ComponentActivity() { |
||||||
|
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
|
||||||
|
// 检查网络 |
||||||
|
setContent { |
||||||
|
val initViewModel:InitViewModel= viewModel() |
||||||
|
initViewModel.checkServer() |
||||||
|
val isNetWorkWorking:Boolean? by initViewModel.isNetWorkWorking.observeAsState(null) |
||||||
|
when(isNetWorkWorking){ |
||||||
|
null-> AnimationText(text = "检查服务器网络状态中!!!") |
||||||
|
true-> { |
||||||
|
Init() |
||||||
|
finish() |
||||||
|
} |
||||||
|
false->{ |
||||||
|
TODO("无法连接到服务器,请检查本地网络或联系管理员") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun Init(initViewModel:InitViewModel= viewModel()){ |
||||||
|
Logger.i("初始化。。。。") |
||||||
|
val context= LocalContext.current |
||||||
|
//后台检查token |
||||||
|
initViewModel.hasOnlyUserToken(context) |
||||||
|
//监听token校验状态 |
||||||
|
val isValid: Boolean? by initViewModel.token.observeAsState(null) |
||||||
|
|
||||||
|
when (isValid) { |
||||||
|
false -> context.startActivity(Intent(context, AccountActivity::class.java)) |
||||||
|
true -> context.startActivity(Intent(context,MainActivity::class.java)) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package com.gyf.csams |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import androidx.lifecycle.ViewModel |
||||||
|
import androidx.lifecycle.viewModelScope |
||||||
|
import com.google.gson.Gson |
||||||
|
import com.google.gson.reflect.TypeToken |
||||||
|
import com.gyf.csams.util.* |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
|
||||||
|
data class TokenVo(val token:String,val studentId:String) |
||||||
|
|
||||||
|
class InitViewModel: ViewModel() { |
||||||
|
/** |
||||||
|
* 服务器网络状态是否正常,true=正常,false=不正常 |
||||||
|
*/ |
||||||
|
private val _isNetWorkWorking = MutableLiveData<Boolean>() |
||||||
|
val isNetWorkWorking: LiveData<Boolean> = _isNetWorkWorking |
||||||
|
|
||||||
|
/** |
||||||
|
* token |
||||||
|
*/ |
||||||
|
private val _token = MutableLiveData<Boolean>() |
||||||
|
val token: LiveData<Boolean> = _token |
||||||
|
|
||||||
|
|
||||||
|
fun checkServer(){ |
||||||
|
Logger.i("测试连接到服务端") |
||||||
|
_isNetWorkWorking.postValue(true) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查询本地是否有且只有一个用户token,如果有则自动登录 |
||||||
|
*/ |
||||||
|
fun hasOnlyUserToken(context: Context){ |
||||||
|
viewModelScope.launch{ |
||||||
|
val db=AppDatabase.getInstance(context) |
||||||
|
val tokenList=db?.tokenDao()?.queryAll() |
||||||
|
if (tokenList != null && tokenList.size == 1) { |
||||||
|
val currentToken: Token = tokenList[0] |
||||||
|
val url=Api.buildUrl(AccountApi.loginToken) |
||||||
|
val action="校验token" |
||||||
|
Logger.i("${action}api=$url") |
||||||
|
HttpClient.post(url,SimpleCallback<Boolean>( |
||||||
|
action=action, |
||||||
|
onSuccess = { |
||||||
|
_token.postValue(it.body) |
||||||
|
Logger.i("token校验结果:${it.body}") |
||||||
|
}, |
||||||
|
onFail = { TODO("token校验失败")}, |
||||||
|
type = object : TypeToken<ApiResponse<Boolean>>(){}.type |
||||||
|
),jsonBody = Gson().toJson(TokenVo(token=currentToken.token,studentId = currentToken.studentId))) |
||||||
|
}else if(tokenList != null && tokenList.size > 1){ |
||||||
|
//TODO 实现切换历史登录帐号 |
||||||
|
Logger.i("token数量大于一,需要手动登录") |
||||||
|
_token.postValue(false) |
||||||
|
}else{ |
||||||
|
Logger.i("本地没有任何token,跳转到登录界面") |
||||||
|
_token.postValue(false) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,377 +0,0 @@ |
|||||||
package com.gyf.csams |
|
||||||
|
|
||||||
import android.os.Bundle |
|
||||||
import androidx.activity.ComponentActivity |
|
||||||
import androidx.activity.compose.setContent |
|
||||||
import androidx.compose.animation.animateColor |
|
||||||
import androidx.compose.animation.core.* |
|
||||||
import androidx.compose.foundation.layout.* |
|
||||||
import androidx.compose.foundation.text.KeyboardActions |
|
||||||
import androidx.compose.foundation.text.KeyboardOptions |
|
||||||
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.platform.LocalFocusManager |
|
||||||
import androidx.compose.ui.text.buildAnnotatedString |
|
||||||
import androidx.compose.ui.text.input.ImeAction |
|
||||||
import androidx.compose.ui.text.input.KeyboardType |
|
||||||
import androidx.compose.ui.text.withStyle |
|
||||||
import androidx.compose.ui.tooling.preview.Preview |
|
||||||
import androidx.compose.ui.unit.dp |
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel |
|
||||||
import com.gyf.csams.account.model.DialogMessage |
|
||||||
import com.gyf.csams.account.model.RegisterViewModel |
|
||||||
import com.gyf.csams.ui.theme.CSAMSTheme |
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() { |
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) { |
|
||||||
super.onCreate(savedInstanceState) |
|
||||||
|
|
||||||
setContent { |
|
||||||
|
|
||||||
CSAMSTheme { |
|
||||||
// A surface container using the 'background' color from the theme |
|
||||||
Surface(color = MaterialTheme.colors.background) { |
|
||||||
Register() |
|
||||||
RegisterDialog() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 注册表单 |
|
||||||
* |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun Register(registerViewModel: RegisterViewModel=viewModel()){ |
|
||||||
val scaffoldState = rememberScaffoldState() |
|
||||||
Scaffold(scaffoldState=scaffoldState) { |
|
||||||
Row( |
|
||||||
horizontalArrangement = Arrangement.Center, |
|
||||||
verticalAlignment = Alignment.CenterVertically, |
|
||||||
modifier = Modifier.fillMaxSize() |
|
||||||
) { |
|
||||||
Column(modifier = Modifier.width(IntrinsicSize.Min)) { |
|
||||||
val name: String by registerViewModel.name.observeAsState("") |
|
||||||
Text(buildAnnotatedString { |
|
||||||
withStyle( |
|
||||||
style = MaterialTheme.typography.subtitle1.toSpanStyle() |
|
||||||
.copy(color = MaterialTheme.colors.primary) |
|
||||||
) { |
|
||||||
append(name) |
|
||||||
} |
|
||||||
withStyle(style = MaterialTheme.typography.subtitle1.toSpanStyle()) { |
|
||||||
append(registerViewModel.welcomeStart) |
|
||||||
} |
|
||||||
withStyle(style = MaterialTheme.typography.subtitle2.toSpanStyle()) { |
|
||||||
append(registerViewModel.welcomeEnd) |
|
||||||
} |
|
||||||
withStyle( |
|
||||||
style = MaterialTheme.typography.subtitle2.toSpanStyle() |
|
||||||
.copy(color = MaterialTheme.colors.secondary) |
|
||||||
) { |
|
||||||
append(BuildConfig.APP_NAME) |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
StudentId() |
|
||||||
Spacer(modifier = Modifier.height(10.dp)) |
|
||||||
Name(name) |
|
||||||
Spacer(modifier = Modifier.height(10.dp)) |
|
||||||
|
|
||||||
val isValidForm: Boolean by registerViewModel.isValidForm.observeAsState(false) |
|
||||||
if(isValidForm) { |
|
||||||
Password() |
|
||||||
} |
|
||||||
Spacer(modifier = Modifier.height(10.dp)) |
|
||||||
RegisterButton(isValidForm,scaffoldState = scaffoldState) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 学号 |
|
||||||
* |
|
||||||
* @param registerViewModel |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun StudentId(registerViewModel: RegisterViewModel=viewModel()){ |
|
||||||
Column { |
|
||||||
|
|
||||||
val studentId: String by registerViewModel.studentId.observeAsState("") |
|
||||||
val isValidStudentId : Boolean by registerViewModel.isValidStudentId.observeAsState(false) |
|
||||||
val focusManager = LocalFocusManager.current |
|
||||||
OutlinedTextField( |
|
||||||
value = studentId, |
|
||||||
onValueChange = { registerViewModel.onStudentIdChange(it) }, |
|
||||||
label = { Text(text = registerViewModel.studentIdDesc) }, |
|
||||||
placeholder = { Text(text = registerViewModel.studentIdPlaceholder) }, |
|
||||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
|
||||||
keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Number,imeAction = ImeAction.Done), |
|
||||||
singleLine = true, |
|
||||||
isError = !isValidStudentId |
|
||||||
) |
|
||||||
if (isValidStudentId) { |
|
||||||
val isRepeat:Boolean? by registerViewModel.isRepeat.observeAsState(null) |
|
||||||
when(isRepeat){ |
|
||||||
null-> AnimationText(text = registerViewModel.checkRegTip) |
|
||||||
true-> |
|
||||||
Text(buildAnnotatedString { |
|
||||||
append(registerViewModel.studentIdDesc) |
|
||||||
withStyle(style = MaterialTheme.typography.body1.toSpanStyle().copy( |
|
||||||
color=MaterialTheme.colors.error)){ |
|
||||||
append(studentId) |
|
||||||
} |
|
||||||
append(registerViewModel.registered) |
|
||||||
}) |
|
||||||
false-> |
|
||||||
Text(buildAnnotatedString { |
|
||||||
append(registerViewModel.studentIdDesc) |
|
||||||
withStyle(style = MaterialTheme.typography.body1.toSpanStyle().copy( |
|
||||||
color=MaterialTheme.colors.primary)){ |
|
||||||
append(studentId) |
|
||||||
} |
|
||||||
append(registerViewModel.canRegister) |
|
||||||
}) |
|
||||||
} |
|
||||||
}else{ |
|
||||||
Text( |
|
||||||
text = registerViewModel.studentIdFormat, |
|
||||||
color = MaterialTheme.colors.error, |
|
||||||
style = MaterialTheme.typography.body1 |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 淡入淡出并且颜色变化文本 |
|
||||||
* |
|
||||||
* @param text |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun AnimationText(text:String){ |
|
||||||
val infiniteTransition = rememberInfiniteTransition() |
|
||||||
val color by infiniteTransition.animateColor( |
|
||||||
initialValue = Color.Red, |
|
||||||
targetValue = Color.Green, |
|
||||||
animationSpec = infiniteRepeatable( |
|
||||||
animation = tween(1000, easing = LinearEasing), |
|
||||||
repeatMode = RepeatMode.Reverse |
|
||||||
) |
|
||||||
) |
|
||||||
|
|
||||||
Text( |
|
||||||
text = text, |
|
||||||
color = color, |
|
||||||
style = MaterialTheme.typography.body1 |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 注册弹窗 |
|
||||||
* |
|
||||||
* @param registerViewModel |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun RegisterDialog(registerViewModel: RegisterViewModel=viewModel()){ |
|
||||||
val dialogMsg:DialogMessage? by registerViewModel.dialogMsg.observeAsState(null) |
|
||||||
|
|
||||||
val message=dialogMsg?.userResDto?.password |
|
||||||
if(message?.isNotEmpty() == true){ |
|
||||||
PasswordDialog(message = message) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 密码弹窗 |
|
||||||
* |
|
||||||
* @param registerViewModel |
|
||||||
* @param message |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun PasswordDialog(registerViewModel: RegisterViewModel=viewModel(),message:String){ |
|
||||||
val button:@Composable () -> Unit = { |
|
||||||
Row(horizontalArrangement=Arrangement.Center,modifier = Modifier |
|
||||||
.fillMaxWidth() |
|
||||||
.padding(bottom = 10.dp)) { |
|
||||||
OutlinedButton(onClick = { registerViewModel.resetDialogMsg() }, |
|
||||||
modifier = Modifier.padding(end = 10.dp)) { |
|
||||||
Text(text = registerViewModel.confirmDesc) |
|
||||||
} |
|
||||||
OutlinedButton(onClick = { TODO() }, |
|
||||||
colors = ButtonDefaults.outlinedButtonColors( |
|
||||||
contentColor = MaterialTheme.colors.onBackground)) { |
|
||||||
Text(text = registerViewModel.backDesc) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
AlertDialog(onDismissRequest = { registerViewModel.resetDialogMsg() }, |
|
||||||
buttons = button, |
|
||||||
title = { Text(text = registerViewModel.title) }, |
|
||||||
text = { |
|
||||||
Text(buildAnnotatedString { |
|
||||||
append(registerViewModel.passwordDialogStart) |
|
||||||
withStyle(style = MaterialTheme.typography.body1.toSpanStyle() |
|
||||||
.copy(color = MaterialTheme.colors.secondary)){ |
|
||||||
append(message) |
|
||||||
} |
|
||||||
append(registerViewModel.passwordDialogEnd) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 姓名 |
|
||||||
* |
|
||||||
* @param registerViewModel |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun Name(name:String,registerViewModel: RegisterViewModel=viewModel()){ |
|
||||||
Column { |
|
||||||
|
|
||||||
val isValidName:Boolean by registerViewModel.isValidName.observeAsState(false) |
|
||||||
val focusManager = LocalFocusManager.current |
|
||||||
OutlinedTextField(value = name, |
|
||||||
onValueChange = {registerViewModel.onNameChange(it)}, |
|
||||||
label={ Text(text = registerViewModel.nameDesc)}, |
|
||||||
placeholder = { Text(text = registerViewModel.namePlaceholder)}, |
|
||||||
singleLine = true, |
|
||||||
isError = !isValidName, |
|
||||||
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)) |
|
||||||
|
|
||||||
if (!isValidName){ |
|
||||||
Text(text = registerViewModel.nameFormat, |
|
||||||
color=MaterialTheme.colors.error) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Composable |
|
||||||
fun Password(registerViewModel: RegisterViewModel=viewModel()) { |
|
||||||
Text(text = registerViewModel.passwordTip |
|
||||||
,color=MaterialTheme.colors.primary, |
|
||||||
modifier = Modifier.fillMaxWidth()) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 注册按钮 |
|
||||||
* |
|
||||||
* @param registerViewModel |
|
||||||
*/ |
|
||||||
@Composable |
|
||||||
fun RegisterButton(isValidForm:Boolean,scaffoldState:ScaffoldState,registerViewModel: RegisterViewModel=viewModel()){ |
|
||||||
|
|
||||||
OutlinedButton(onClick = { registerViewModel.register()}, |
|
||||||
enabled = isValidForm, |
|
||||||
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp)) { |
|
||||||
Text(text = registerViewModel.regBtnDesc) |
|
||||||
} |
|
||||||
|
|
||||||
OutlinedButton(onClick = { TODO()}, |
|
||||||
modifier = Modifier.fillMaxWidth(), |
|
||||||
colors = ButtonDefaults.outlinedButtonColors( |
|
||||||
contentColor = MaterialTheme.colors.onBackground)) { |
|
||||||
Text(text = registerViewModel.backDesc) |
|
||||||
} |
|
||||||
|
|
||||||
val snackBarMsg:String by registerViewModel.snackBarMsg.observeAsState("") |
|
||||||
|
|
||||||
if(snackBarMsg!=""){ |
|
||||||
LaunchedEffect(scaffoldState) { |
|
||||||
scaffoldState.snackbarHostState.showSnackbar( |
|
||||||
message = snackBarMsg |
|
||||||
) |
|
||||||
registerViewModel.resetRegisterResMsg() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Preview(showBackground = true) |
|
||||||
|
|
||||||
@Composable |
|
||||||
fun DefaultPreview() { |
|
||||||
CSAMSTheme { |
|
||||||
|
|
||||||
Row ( |
|
||||||
horizontalArrangement=Arrangement.Center, |
|
||||||
verticalAlignment = Alignment.CenterVertically, |
|
||||||
modifier = Modifier.fillMaxSize()) { |
|
||||||
Column(modifier = Modifier.width(IntrinsicSize.Min)) { |
|
||||||
val model=RegisterViewModel() |
|
||||||
|
|
||||||
StudentId(model) |
|
||||||
Spacer(modifier = Modifier.height(10.dp)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Preview |
|
||||||
@Composable |
|
||||||
fun AnimationTextPreview(){ |
|
||||||
AnimationText(text = "6666") |
|
||||||
} |
|
||||||
|
|
||||||
@Preview |
|
||||||
@Composable |
|
||||||
fun PasswordDial(){ |
|
||||||
CSAMSTheme { |
|
||||||
// A surface container using the 'background' color from the theme |
|
||||||
Surface(color = MaterialTheme.colors.background) { |
|
||||||
// val model=RegisterViewModel() |
|
||||||
// PasswordDialog(registerViewModel=model,message = "99999") |
|
||||||
val openDialog = remember { mutableStateOf(true) } |
|
||||||
AlertDialog( |
|
||||||
onDismissRequest = { |
|
||||||
// Dismiss the dialog when the user clicks outside the dialog or on the back |
|
||||||
// button. If you want to disable that functionality, simply use an empty |
|
||||||
// onCloseRequest. |
|
||||||
openDialog.value = false |
|
||||||
}, |
|
||||||
title = { |
|
||||||
Text(text = "Title") |
|
||||||
}, |
|
||||||
text = { |
|
||||||
Text( |
|
||||||
"This area typically contains the supportive text " + |
|
||||||
"which presents the details regarding the Dialog's purpose." |
|
||||||
) |
|
||||||
}, |
|
||||||
confirmButton = { |
|
||||||
TextButton( |
|
||||||
onClick = { |
|
||||||
openDialog.value = false |
|
||||||
} |
|
||||||
) { |
|
||||||
Text("Confirm") |
|
||||||
} |
|
||||||
}, |
|
||||||
dismissButton = { |
|
||||||
TextButton( |
|
||||||
onClick = { |
|
||||||
openDialog.value = false |
|
||||||
} |
|
||||||
) { |
|
||||||
Text("Dismiss") |
|
||||||
} |
|
||||||
} |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,391 @@ |
|||||||
|
package com.gyf.csams.account.ui |
||||||
|
|
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.foundation.text.KeyboardActions |
||||||
|
import androidx.compose.foundation.text.KeyboardOptions |
||||||
|
import androidx.compose.material.* |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.LaunchedEffect |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.platform.LocalFocusManager |
||||||
|
import androidx.compose.ui.text.buildAnnotatedString |
||||||
|
import androidx.compose.ui.text.input.ImeAction |
||||||
|
import androidx.compose.ui.text.input.KeyboardType |
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation |
||||||
|
import androidx.compose.ui.text.withStyle |
||||||
|
import androidx.compose.ui.tooling.preview.Preview |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import androidx.navigation.compose.NavHost |
||||||
|
import androidx.navigation.compose.composable |
||||||
|
import androidx.navigation.compose.navigate |
||||||
|
import androidx.navigation.compose.rememberNavController |
||||||
|
import com.gyf.csams.BuildConfig |
||||||
|
import com.gyf.csams.account.model.AccountViewModel |
||||||
|
import com.gyf.csams.account.model.DialogMessage |
||||||
|
import com.gyf.csams.ui.AnimationText |
||||||
|
import com.gyf.csams.ui.theme.CSAMSTheme |
||||||
|
|
||||||
|
|
||||||
|
enum class AccountRoute{ |
||||||
|
login, |
||||||
|
register |
||||||
|
} |
||||||
|
|
||||||
|
class AccountActivity: ComponentActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
|
||||||
|
setContent { |
||||||
|
CSAMSTheme { |
||||||
|
// A surface container using the 'background' color from the theme |
||||||
|
Surface(color = MaterialTheme.colors.background) { |
||||||
|
val navController = rememberNavController() |
||||||
|
val scaffoldState = rememberScaffoldState() |
||||||
|
|
||||||
|
Scaffold(scaffoldState=scaffoldState) { |
||||||
|
NavHost(navController, startDestination = AccountRoute.login.name) { |
||||||
|
composable(AccountRoute.login.name) { |
||||||
|
Account(scaffoldState=scaffoldState,route = AccountRoute.login) { isValidForm: Boolean, accountViewModel: AccountViewModel -> |
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
|
||||||
|
OutlinedButton(onClick = {accountViewModel.login()}, |
||||||
|
enabled = isValidForm, |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.padding(bottom = 10.dp)) { |
||||||
|
|
||||||
|
Text(text = accountViewModel.loginDesc) |
||||||
|
} |
||||||
|
|
||||||
|
val finishLogin:Boolean? by accountViewModel.finishLogin.observeAsState() |
||||||
|
if(finishLogin==true){ |
||||||
|
finish() |
||||||
|
} |
||||||
|
|
||||||
|
OutlinedButton(onClick = { navController.navigate(AccountRoute.register.name)}, |
||||||
|
modifier = Modifier.fillMaxWidth(), |
||||||
|
colors = ButtonDefaults.outlinedButtonColors( |
||||||
|
contentColor = MaterialTheme.colors.onBackground)) { |
||||||
|
Text(text = accountViewModel.goRegister) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
composable(AccountRoute.register.name) { |
||||||
|
Account(scaffoldState=scaffoldState,route = AccountRoute.register) { isValidForm: Boolean, accountViewModel: AccountViewModel -> |
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
|
||||||
|
OutlinedButton(onClick = { accountViewModel.register()}, |
||||||
|
enabled = isValidForm, |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.padding(bottom = 10.dp)) { |
||||||
|
Text(text = accountViewModel.regBtnDesc) |
||||||
|
} |
||||||
|
|
||||||
|
OutlinedButton(onClick = { navController.navigate(AccountRoute.login.name)}, |
||||||
|
modifier = Modifier.fillMaxWidth(), |
||||||
|
colors = ButtonDefaults.outlinedButtonColors( |
||||||
|
contentColor = MaterialTheme.colors.onBackground)) { |
||||||
|
Text(text = accountViewModel.backLogin) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 帐号表单 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
* @param Action 表单操作区域 |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun Account(accountViewModel: AccountViewModel = viewModel(), |
||||||
|
scaffoldState: ScaffoldState, |
||||||
|
route: AccountRoute, |
||||||
|
Action: @Composable (isValidForm:Boolean,accountViewModel: AccountViewModel) -> Unit){ |
||||||
|
accountViewModel.route=route |
||||||
|
Row( |
||||||
|
horizontalArrangement = Arrangement.Center, |
||||||
|
verticalAlignment = Alignment.CenterVertically, |
||||||
|
modifier = Modifier.fillMaxSize() |
||||||
|
) { |
||||||
|
Column(modifier = Modifier.width(IntrinsicSize.Min)) { |
||||||
|
val name: String by accountViewModel.name.observeAsState("") |
||||||
|
Text(buildAnnotatedString { |
||||||
|
withStyle( |
||||||
|
style = MaterialTheme.typography.subtitle1.toSpanStyle() |
||||||
|
.copy(color = MaterialTheme.colors.primary) |
||||||
|
) { |
||||||
|
append(name) |
||||||
|
} |
||||||
|
withStyle(style = MaterialTheme.typography.subtitle1.toSpanStyle()) { |
||||||
|
append(accountViewModel.welcomeStart) |
||||||
|
} |
||||||
|
withStyle(style = MaterialTheme.typography.subtitle2.toSpanStyle()) { |
||||||
|
append(accountViewModel.welcomeEnd) |
||||||
|
} |
||||||
|
withStyle( |
||||||
|
style = MaterialTheme.typography.subtitle2.toSpanStyle() |
||||||
|
.copy(color = MaterialTheme.colors.secondary) |
||||||
|
) { |
||||||
|
append(BuildConfig.APP_NAME) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
StudentId(checkRepeat=route==AccountRoute.register) |
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
|
||||||
|
|
||||||
|
if (route==AccountRoute.register) Name(name=name) else Password() |
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
PasswordTip() |
||||||
|
val isValidForm: Boolean by accountViewModel.isValidForm.observeAsState(false) |
||||||
|
Action(isValidForm=isValidForm,accountViewModel=accountViewModel) |
||||||
|
|
||||||
|
val snackBarMsg:String by accountViewModel.snackBarMsg.observeAsState("") |
||||||
|
|
||||||
|
if(snackBarMsg!=""){ |
||||||
|
LaunchedEffect(scaffoldState) { |
||||||
|
scaffoldState.snackbarHostState.showSnackbar( |
||||||
|
message = snackBarMsg |
||||||
|
) |
||||||
|
accountViewModel.resetRegisterResMsg() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RegisterDialog() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 学号 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun StudentId(accountViewModel: AccountViewModel = viewModel(),checkRepeat:Boolean){ |
||||||
|
Column { |
||||||
|
|
||||||
|
val studentId: String by accountViewModel.studentId.observeAsState("") |
||||||
|
val isValidStudentId : Boolean by accountViewModel.isValidStudentId.observeAsState(false) |
||||||
|
val focusManager = LocalFocusManager.current |
||||||
|
OutlinedTextField( |
||||||
|
value = studentId, |
||||||
|
onValueChange = { accountViewModel.onStudentIdChange(it) }, |
||||||
|
label = { Text(text = accountViewModel.studentIdDesc) }, |
||||||
|
placeholder = { Text(text = accountViewModel.studentIdPlaceholder) }, |
||||||
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
||||||
|
keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Number,imeAction = ImeAction.Done), |
||||||
|
singleLine = true, |
||||||
|
isError = !isValidStudentId |
||||||
|
) |
||||||
|
if (isValidStudentId) { |
||||||
|
if(checkRepeat) { |
||||||
|
val isRepeat: Boolean? by accountViewModel.isRepeat.observeAsState(null) |
||||||
|
when (isRepeat) { |
||||||
|
null -> AnimationText(text = accountViewModel.checkRegTip) |
||||||
|
true -> |
||||||
|
Text(buildAnnotatedString { |
||||||
|
append(accountViewModel.studentIdDesc) |
||||||
|
withStyle( |
||||||
|
style = MaterialTheme.typography.body1.toSpanStyle().copy( |
||||||
|
color = MaterialTheme.colors.error |
||||||
|
) |
||||||
|
) { |
||||||
|
append(studentId) |
||||||
|
} |
||||||
|
append(accountViewModel.registered) |
||||||
|
}) |
||||||
|
false -> |
||||||
|
Text(buildAnnotatedString { |
||||||
|
append(accountViewModel.studentIdDesc) |
||||||
|
withStyle( |
||||||
|
style = MaterialTheme.typography.body1.toSpanStyle().copy( |
||||||
|
color = MaterialTheme.colors.primary |
||||||
|
) |
||||||
|
) { |
||||||
|
append(studentId) |
||||||
|
} |
||||||
|
append(accountViewModel.canRegister) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
}else{ |
||||||
|
Text( |
||||||
|
text = accountViewModel.studentIdFormat, |
||||||
|
color = MaterialTheme.colors.error, |
||||||
|
style = MaterialTheme.typography.body1 |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 注册弹窗 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()){ |
||||||
|
val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null) |
||||||
|
|
||||||
|
val message=dialogMsg?.userResDto?.password |
||||||
|
if(message?.isNotEmpty() == true){ |
||||||
|
PasswordDialog(message = message) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 密码弹窗 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
* @param message |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun PasswordDialog(accountViewModel: AccountViewModel = viewModel(), message:String){ |
||||||
|
val context= LocalContext.current |
||||||
|
val button:@Composable () -> Unit = { |
||||||
|
Row(horizontalArrangement= Arrangement.Center,modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.padding(bottom = 10.dp)) { |
||||||
|
OutlinedButton(onClick = { accountViewModel.resetDialogMsg() }, |
||||||
|
modifier = Modifier.padding(end = 10.dp)) { |
||||||
|
Text(text = accountViewModel.confirmDesc) |
||||||
|
} |
||||||
|
OutlinedButton(onClick = { |
||||||
|
context.startActivity(Intent(context,AccountActivity::class.java)) |
||||||
|
}, |
||||||
|
colors = ButtonDefaults.outlinedButtonColors( |
||||||
|
contentColor = MaterialTheme.colors.onBackground)) { |
||||||
|
Text(text = accountViewModel.backLogin) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
AlertDialog(onDismissRequest = { accountViewModel.resetDialogMsg() }, |
||||||
|
buttons = button, |
||||||
|
title = { Text(text = accountViewModel.title) }, |
||||||
|
text = { |
||||||
|
Text(buildAnnotatedString { |
||||||
|
append(accountViewModel.passwordDialogStart) |
||||||
|
withStyle(style = MaterialTheme.typography.body1.toSpanStyle() |
||||||
|
.copy(color = MaterialTheme.colors.secondary)){ |
||||||
|
append(message) |
||||||
|
} |
||||||
|
append(accountViewModel.passwordDialogEnd) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 姓名文本框 |
||||||
|
* |
||||||
|
* @param name 姓名 |
||||||
|
* @param accountViewModel |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun Name(name:String, accountViewModel: AccountViewModel = viewModel()){ |
||||||
|
Column { |
||||||
|
|
||||||
|
val isValidName:Boolean by accountViewModel.isValidName.observeAsState(false) |
||||||
|
val focusManager = LocalFocusManager.current |
||||||
|
OutlinedTextField(value = name, |
||||||
|
onValueChange = {accountViewModel.onNameChange(it)}, |
||||||
|
label={ Text(text = accountViewModel.nameDesc)}, |
||||||
|
placeholder = { Text(text = accountViewModel.namePlaceholder)}, |
||||||
|
singleLine = true, |
||||||
|
isError = !isValidName, |
||||||
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)) |
||||||
|
|
||||||
|
if (!isValidName){ |
||||||
|
Text(text = accountViewModel.nameFormat, |
||||||
|
color=MaterialTheme.colors.error) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 密码框 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun Password(accountViewModel: AccountViewModel= viewModel()){ |
||||||
|
Column { |
||||||
|
val isValidPwd:Boolean by accountViewModel.isValidPwd.observeAsState(false) |
||||||
|
val focusManager = LocalFocusManager.current |
||||||
|
val password:String by accountViewModel.password.observeAsState("") |
||||||
|
OutlinedTextField(value = password, |
||||||
|
visualTransformation=PasswordVisualTransformation(), |
||||||
|
onValueChange = {accountViewModel.onPasswordChange(it)}, |
||||||
|
label={ Text(text = accountViewModel.passwordDesc)}, |
||||||
|
placeholder = { Text(text = accountViewModel.passwordPlaceholder)}, |
||||||
|
singleLine = true, |
||||||
|
isError = !isValidPwd, |
||||||
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done,keyboardType = KeyboardType.Number)) |
||||||
|
|
||||||
|
if(!isValidPwd){ |
||||||
|
Text(text = accountViewModel.passwordFormat, |
||||||
|
color=MaterialTheme.colors.error) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 提示自动生成密码 |
||||||
|
* |
||||||
|
* @param accountViewModel |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
private fun PasswordTip(accountViewModel: AccountViewModel = viewModel()) { |
||||||
|
if(accountViewModel.isValidForm.value==true) { |
||||||
|
Text( |
||||||
|
text = accountViewModel.passwordTip, color = MaterialTheme.colors.primary, |
||||||
|
modifier = Modifier.fillMaxWidth() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Preview(showBackground = true) |
||||||
|
@Composable |
||||||
|
fun DefaultPreview() { |
||||||
|
CSAMSTheme { |
||||||
|
|
||||||
|
Row ( |
||||||
|
horizontalArrangement=Arrangement.Center, |
||||||
|
verticalAlignment = Alignment.CenterVertically, |
||||||
|
modifier = Modifier.fillMaxSize()) { |
||||||
|
Column(modifier = Modifier.width(IntrinsicSize.Min)) { |
||||||
|
val model:AccountViewModel= viewModel() |
||||||
|
|
||||||
|
StudentId(model,false) |
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,41 @@ |
|||||||
|
package com.gyf.csams.ui |
||||||
|
|
||||||
|
import androidx.compose.animation.animateColor |
||||||
|
import androidx.compose.animation.core.* |
||||||
|
import androidx.compose.material.MaterialTheme |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.ui.graphics.Color |
||||||
|
import androidx.compose.ui.tooling.preview.Preview |
||||||
|
|
||||||
|
/** |
||||||
|
* 淡入淡出并且颜色变化文本 |
||||||
|
* |
||||||
|
* @param text |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun AnimationText(text:String){ |
||||||
|
val infiniteTransition = rememberInfiniteTransition() |
||||||
|
val color by infiniteTransition.animateColor( |
||||||
|
initialValue = Color.Red, |
||||||
|
targetValue = Color.Green, |
||||||
|
animationSpec = infiniteRepeatable( |
||||||
|
animation = tween(1000, easing = LinearEasing), |
||||||
|
repeatMode = RepeatMode.Reverse |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
Text( |
||||||
|
text = text, |
||||||
|
color = color, |
||||||
|
style = MaterialTheme.typography.body1 |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Preview |
||||||
|
@Composable |
||||||
|
fun AnimationTextPreview(){ |
||||||
|
AnimationText(text = "6666") |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.gyf.csams.ui |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.compose.foundation.layout.Arrangement |
||||||
|
import androidx.compose.foundation.layout.Row |
||||||
|
import androidx.compose.foundation.layout.fillMaxSize |
||||||
|
import androidx.compose.material.MaterialTheme |
||||||
|
import androidx.compose.material.Surface |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.tooling.preview.Preview |
||||||
|
import com.gyf.csams.ui.theme.CSAMSTheme |
||||||
|
|
||||||
|
class MainActivity: ComponentActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
setContent { |
||||||
|
CSAMSTheme { |
||||||
|
// A surface container using the 'background' color from the theme |
||||||
|
Surface(color = MaterialTheme.colors.background) { |
||||||
|
Row(horizontalArrangement = Arrangement.Center, |
||||||
|
verticalAlignment = Alignment.CenterVertically, |
||||||
|
modifier = Modifier.fillMaxSize()){ |
||||||
|
AnimationText(text = "主界面设计中。。。。") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Preview(showBackground = true) |
||||||
|
@Composable |
||||||
|
fun DefaultPreview() { |
||||||
|
CSAMSTheme { |
||||||
|
// A surface container using the 'background' color from the theme |
||||||
|
AnimationText(text = "sdfsdf") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
package com.gyf.csams.util |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import androidx.room.* |
||||||
|
import kotlinx.serialization.Serializable |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 登陆令牌 |
||||||
|
*/ |
||||||
|
@Entity |
||||||
|
@Serializable |
||||||
|
data class Token(@PrimaryKey val studentId:String,@ColumnInfo val token:String,@ColumnInfo val createTime:Long) |
||||||
|
|
||||||
|
/** |
||||||
|
* 令牌传输 |
||||||
|
* |
||||||
|
* @property isValid |
||||||
|
* @property token |
||||||
|
*/ |
||||||
|
@Serializable |
||||||
|
data class TokenResDto(val isValid:Boolean,val token: Token?) |
||||||
|
|
||||||
|
@Dao |
||||||
|
interface TokenDao { |
||||||
|
@Query("select * from token") |
||||||
|
suspend fun queryAll(): List<Token> |
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE) |
||||||
|
suspend fun save(token: Token) |
||||||
|
|
||||||
|
@Delete |
||||||
|
suspend fun delete(user: Token) |
||||||
|
} |
||||||
|
|
||||||
|
class TokenManager private constructor(private var token: Token?) { |
||||||
|
companion object { |
||||||
|
@Volatile |
||||||
|
private var instance: TokenManager? = null |
||||||
|
|
||||||
|
fun getInstance(token: Token?=null) = |
||||||
|
instance ?: synchronized(this) { |
||||||
|
instance ?: TokenManager(token).also { instance = it } |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Database(entities = [Token::class], version = 1, exportSchema = false) |
||||||
|
abstract class AppDatabase : RoomDatabase() { |
||||||
|
abstract fun tokenDao(): TokenDao |
||||||
|
private val mIsDatabaseCreated = MutableLiveData<Boolean>() |
||||||
|
|
||||||
|
/** |
||||||
|
* Check whether the database already exists and expose it via [.getDatabaseCreated] |
||||||
|
*/ |
||||||
|
private fun updateDatabaseCreated(context: Context) { |
||||||
|
if (context.getDatabasePath(DATABASE_NAME).exists()) { |
||||||
|
setDatabaseCreated() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun setDatabaseCreated() { |
||||||
|
mIsDatabaseCreated.postValue(true) |
||||||
|
} |
||||||
|
|
||||||
|
val databaseCreated: LiveData<Boolean> |
||||||
|
get() = mIsDatabaseCreated |
||||||
|
|
||||||
|
companion object { |
||||||
|
private var sInstance: AppDatabase? = null |
||||||
|
const val DATABASE_NAME = "basic-sample-db" |
||||||
|
fun getInstance(context: Context): AppDatabase? { |
||||||
|
if (sInstance == null) { |
||||||
|
synchronized(AppDatabase::class.java) { |
||||||
|
if (sInstance == null) { |
||||||
|
sInstance = |
||||||
|
buildDatabase(context.applicationContext) |
||||||
|
sInstance!!.updateDatabaseCreated(context.applicationContext) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return sInstance |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Build the database. [Builder.build] only sets up the database configuration and |
||||||
|
* creates a new instance of the database. |
||||||
|
* The SQLite database is only created when it's accessed for the first time. |
||||||
|
*/ |
||||||
|
private fun buildDatabase(appContext: Context): AppDatabase { |
||||||
|
return Room.databaseBuilder(appContext, AppDatabase::class.java, DATABASE_NAME) |
||||||
|
.build() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue