更新 google 登录
This commit is contained in:
@@ -2,5 +2,6 @@ package com.aiosman.riderpro
|
||||
|
||||
object ConstVars {
|
||||
// api 地址
|
||||
const val BASE_SERVER = "https://8.137.22.101:8088"
|
||||
const val BASE_SERVER = "http://192.168.31.250:8088"
|
||||
// const val BASE_SERVER = "https://8.137.22.101:8088"
|
||||
}
|
||||
@@ -32,10 +32,7 @@ import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.ServiceException
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.ui.Navigation
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.index.NavigationItem
|
||||
@@ -44,12 +41,11 @@ import com.google.android.libraries.places.api.Places
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.HttpException
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val scope = CoroutineScope(Dispatchers.Main)
|
||||
suspend fun getAccount(): Boolean {
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
try {
|
||||
val resp = accountService.getMyAccount()
|
||||
return true
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.aiosman.riderpro.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.riderpro.AppStore
|
||||
import com.aiosman.riderpro.data.api.ApiClient
|
||||
import com.aiosman.riderpro.data.api.ChangePasswordRequestBody
|
||||
import com.aiosman.riderpro.data.api.GoogleRegisterRequestBody
|
||||
import com.aiosman.riderpro.data.api.LoginUserRequestBody
|
||||
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||
@@ -24,11 +24,13 @@ data class AccountLikeEntity(
|
||||
val user: NoticeUserEntity,
|
||||
val likeTime: Date,
|
||||
)
|
||||
|
||||
data class AccountFavouriteEntity(
|
||||
val post: NoticePostEntity,
|
||||
val user: NoticeUserEntity,
|
||||
val favoriteTime: Date,
|
||||
)
|
||||
|
||||
data class AccountProfileEntity(
|
||||
val id: Int,
|
||||
val followerCount: Int,
|
||||
@@ -281,10 +283,12 @@ interface AccountService {
|
||||
suspend fun getAccountProfileById(id: Int): AccountProfileEntity
|
||||
suspend fun getMyAccount(): UserAuth
|
||||
suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth
|
||||
suspend fun loginUserWithGoogle(googleId: String): UserAuth
|
||||
suspend fun logout()
|
||||
suspend fun updateAvatar(uri: String)
|
||||
suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?)
|
||||
suspend fun registerUserWithPassword(loginName: String, password: String)
|
||||
suspend fun regiterUserWithGoogleAccount(idToken: String)
|
||||
suspend fun changeAccountPassword(oldPassword: String, newPassword: String)
|
||||
suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer<AccountLike>
|
||||
suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer<AccountFollow>
|
||||
@@ -293,7 +297,7 @@ interface AccountService {
|
||||
suspend fun updateNotice(payload: UpdateNoticeRequestBody)
|
||||
}
|
||||
|
||||
class TestAccountServiceImpl : AccountService {
|
||||
class AccountServiceImpl : AccountService {
|
||||
override suspend fun getMyAccountProfile(): AccountProfileEntity {
|
||||
val resp = ApiClient.api.getMyAccount()
|
||||
val body = resp.body() ?: throw ServiceException("Failed to get account")
|
||||
@@ -318,6 +322,19 @@ class TestAccountServiceImpl : AccountService {
|
||||
return UserAuth(0, body.token)
|
||||
}
|
||||
|
||||
override suspend fun loginUserWithGoogle(googleId: String): UserAuth {
|
||||
val resp = ApiClient.api.login(LoginUserRequestBody(googleId=googleId))
|
||||
val body = resp.body() ?: throw ServiceException("Failed to login")
|
||||
return UserAuth(0, body.token)
|
||||
}
|
||||
|
||||
override suspend fun regiterUserWithGoogleAccount(idToken: String) {
|
||||
val resp = ApiClient.api.registerWithGoogle(GoogleRegisterRequestBody(idToken))
|
||||
if (resp.code() != 200) {
|
||||
throw ServiceException("Failed to register")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun logout() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -27,14 +27,22 @@ data class RegisterRequestBody(
|
||||
@SerializedName("username")
|
||||
val username: String,
|
||||
@SerializedName("password")
|
||||
val password: String
|
||||
)
|
||||
val password: String,
|
||||
|
||||
)
|
||||
|
||||
data class LoginUserRequestBody(
|
||||
@SerializedName("username")
|
||||
val username: String,
|
||||
val username: String? = null,
|
||||
@SerializedName("password")
|
||||
val password: String
|
||||
val password: String? = null,
|
||||
@SerializedName("googleId")
|
||||
val googleId: String? = null,
|
||||
)
|
||||
|
||||
data class GoogleRegisterRequestBody(
|
||||
@SerializedName("idToken")
|
||||
val idToken: String
|
||||
)
|
||||
|
||||
data class AuthResult(
|
||||
@@ -219,4 +227,7 @@ interface RiderProAPI {
|
||||
@Query("nickname") search: String? = null,
|
||||
): Response<ListContainer<AccountProfile>>
|
||||
|
||||
@POST("register/google")
|
||||
suspend fun registerWithGoogle(@Body body: GoogleRegisterRequestBody): Response<AuthResult>
|
||||
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ChangePasswordViewModel {
|
||||
val accountService :AccountService = TestAccountServiceImpl()
|
||||
val accountService :AccountService = AccountServiceImpl()
|
||||
suspend fun changePassword(currentPassword: String, newPassword: String) {
|
||||
accountService.changeAccountPassword(currentPassword, newPassword)
|
||||
}
|
||||
|
||||
@@ -33,10 +33,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||
import com.aiosman.riderpro.data.UploadImage
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
@@ -49,7 +48,7 @@ import kotlinx.coroutines.launch
|
||||
@Composable
|
||||
fun AccountEditScreen() {
|
||||
val userService: UserService = TestUserServiceImpl()
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
var name by remember { mutableStateOf("") }
|
||||
var bio by remember { mutableStateOf("") }
|
||||
var imageUrl by remember { mutableStateOf<Uri?>(null) }
|
||||
|
||||
@@ -1,12 +1,45 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import coil.ImageLoader
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.SuccessResult
|
||||
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun rememberImageBitmap(imageUrl: String, imageLoader: ImageLoader): Bitmap? {
|
||||
val context = LocalContext.current
|
||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
|
||||
LaunchedEffect(imageUrl) {
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(imageUrl)
|
||||
.crossfade(true)
|
||||
.build()
|
||||
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
(imageLoader.execute(request) as? SuccessResult)?.drawable?.toBitmap()
|
||||
}
|
||||
|
||||
bitmap = result
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
@Composable
|
||||
fun CustomAsyncImage(
|
||||
context: Context,
|
||||
@@ -15,9 +48,10 @@ fun CustomAsyncImage(
|
||||
modifier: Modifier = Modifier,
|
||||
contentScale: ContentScale = ContentScale.Crop
|
||||
) {
|
||||
val bitmap = rememberImageBitmap(imageUrl, getImageLoader(context))
|
||||
val imageLoader = getImageLoader(context)
|
||||
AsyncImage(
|
||||
model = imageUrl,
|
||||
model = bitmap,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
contentScale = contentScale,
|
||||
|
||||
@@ -7,23 +7,19 @@ import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.aiosman.riderpro.data.AccountFavourite
|
||||
import com.aiosman.riderpro.data.AccountFavouriteEntity
|
||||
import com.aiosman.riderpro.data.AccountLike
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.FavoriteItemPagingSource
|
||||
import com.aiosman.riderpro.data.LikeItemPagingSource
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.api.ApiClient
|
||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||
import com.aiosman.riderpro.ui.like.LikePageViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object FavouritePageViewModel : ViewModel() {
|
||||
private val accountService: AccountService = TestAccountServiceImpl()
|
||||
private val accountService: AccountService = AccountServiceImpl()
|
||||
private val _favouriteItemsFlow =
|
||||
MutableStateFlow<PagingData<AccountFavouriteEntity>>(PagingData.empty())
|
||||
val favouriteItemsFlow = _favouriteItemsFlow.asStateFlow()
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.paging.map
|
||||
import com.aiosman.riderpro.data.AccountFollow
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.FollowItemPagingSource
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
import com.aiosman.riderpro.data.api.ApiClient
|
||||
@@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object FollowerViewModel : ViewModel() {
|
||||
private val accountService: AccountService = TestAccountServiceImpl()
|
||||
private val accountService: AccountService = AccountServiceImpl()
|
||||
private val userService: UserService = TestUserServiceImpl()
|
||||
private val _followerItemsFlow =
|
||||
MutableStateFlow<PagingData<AccountFollow>>(PagingData.empty())
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.aiosman.riderpro.data.CommentEntity
|
||||
import com.aiosman.riderpro.data.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.CommentService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object MessageListViewModel : ViewModel() {
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
var noticeInfo by mutableStateOf<AccountNotice?>(null)
|
||||
|
||||
private val commentService: CommentService = TestCommentServiceImpl()
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
@@ -70,7 +71,6 @@ import com.aiosman.riderpro.ui.comment.CommentModalContent
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedCounter
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
||||
import com.aiosman.riderpro.ui.composables.AsyncBlurImage
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.RelPostCard
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
@@ -102,7 +102,10 @@ fun MomentsList() {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
items(moments.itemCount) { idx ->
|
||||
items(
|
||||
moments.itemCount,
|
||||
key = { idx -> moments[idx]?.id ?: idx }
|
||||
) { idx ->
|
||||
val momentItem = moments[idx] ?: return@items
|
||||
MomentCard(momentEntity = momentItem,
|
||||
onAddComment = {
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.MomentPagingSource
|
||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentEntity
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -24,7 +24,7 @@ object MomentViewModel : ViewModel() {
|
||||
private val momentService: MomentService = TestMomentServiceImpl()
|
||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||
val momentsFlow = _momentsFlow.asStateFlow()
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
|
||||
@@ -9,7 +9,7 @@ import androidx.paging.PagingData
|
||||
import com.aiosman.riderpro.AppStore
|
||||
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.MomentPagingSource
|
||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
@@ -18,7 +18,7 @@ import com.aiosman.riderpro.model.MomentEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
object MyProfileViewModel {
|
||||
val service: AccountService = TestAccountServiceImpl()
|
||||
val service: AccountService = AccountServiceImpl()
|
||||
val userService = TestUserServiceImpl()
|
||||
var profile by mutableStateOf<AccountProfileEntity?>(null)
|
||||
var momentsFlow by mutableStateOf<Flow<PagingData<MomentEntity>>?>(null)
|
||||
|
||||
@@ -7,11 +7,10 @@ import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.aiosman.riderpro.data.AccountLike
|
||||
import com.aiosman.riderpro.data.AccountLikeEntity
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.LikeItemPagingSource
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.api.ApiClient
|
||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -21,7 +20,7 @@ import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
object LikePageViewModel : ViewModel() {
|
||||
private val accountService: AccountService = TestAccountServiceImpl()
|
||||
private val accountService: AccountService = AccountServiceImpl()
|
||||
private val _likeItemsFlow = MutableStateFlow<PagingData<AccountLikeEntity>>(PagingData.empty())
|
||||
val likeItemsFlow = _likeItemsFlow.asStateFlow()
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.ServiceException
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
@@ -47,7 +47,7 @@ fun EmailSignupScreen() {
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
fun validateForm(): Boolean {
|
||||
if (email.isEmpty()) {
|
||||
Toast.makeText(context, "Email is required", Toast.LENGTH_SHORT).show()
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package com.aiosman.riderpro.ui.login
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ContentValues.TAG
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -20,6 +19,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -29,44 +29,103 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.credentials.Credential
|
||||
import androidx.credentials.CredentialManager
|
||||
import androidx.credentials.CustomCredential
|
||||
import androidx.credentials.GetCredentialRequest
|
||||
import androidx.credentials.GetCredentialResponse
|
||||
import com.aiosman.riderpro.AppStore
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.ServiceException
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.common.api.ApiException
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Composable
|
||||
fun SignupScreen() {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val googleSignInClient = GoogleSignIn.getClient(context, AppStore.googleSignInOptions)
|
||||
fun handleSignInResult(
|
||||
task: Task<GoogleSignInAccount>,
|
||||
onSignInSuccess: (GoogleSignInAccount) -> Unit
|
||||
) {
|
||||
try {
|
||||
val account = task.getResult(ApiException::class.java)
|
||||
onSignInSuccess(account)
|
||||
} catch (e: ApiException) {
|
||||
// Handle sign-in failure
|
||||
}
|
||||
}
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult(),
|
||||
onResult = { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
|
||||
handleSignInResult(task) {account ->
|
||||
// Handle sign-in success
|
||||
Log.d("SignupScreen", "handleSignInResult: $account")
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val credentialManager = CredentialManager.create(context)
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
|
||||
fun registerWithGoogle(idToken: String) {
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
accountService.regiterUserWithGoogleAccount(idToken)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to register with google", e)
|
||||
return@launch
|
||||
}
|
||||
// 获取用户信息
|
||||
// 获取 token
|
||||
val authResp = accountService.loginUserWithGoogle(idToken)
|
||||
if (authResp.token != null) {
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Successfully registered", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
AppStore.apply {
|
||||
token = authResp.token
|
||||
this.rememberMe = true
|
||||
saveData()
|
||||
}
|
||||
// 获取token 信息
|
||||
try {
|
||||
accountService.getMyAccount()
|
||||
} catch (e: ServiceException) {
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Failed to get account", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
navController.navigate(NavigationRoute.Index.route) {
|
||||
popUpTo(NavigationRoute.Login.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun handleGoogleSignIn(result: GetCredentialResponse) {
|
||||
val credential: Credential = result.credential
|
||||
|
||||
if (credential is CustomCredential) {
|
||||
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.type)) {
|
||||
try {
|
||||
val googleIdTokenCredential: GoogleIdTokenCredential =
|
||||
GoogleIdTokenCredential.createFrom(credential.data)
|
||||
registerWithGoogle(googleIdTokenCredential.idToken)
|
||||
} catch (e: GoogleIdTokenParsingException) {
|
||||
Log.e(TAG, "Received an invalid google id token response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun googleLogin() {
|
||||
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
|
||||
.setFilterByAuthorizedAccounts(true)
|
||||
.setServerClientId("754277015802-pnua6tg8ibnjq69lv8qdcmsdhbe97ag9.apps.googleusercontent.com")
|
||||
.build()
|
||||
val request = GetCredentialRequest.Builder().addCredentialOption(googleIdOption)
|
||||
.build()
|
||||
coroutineScope.launch {
|
||||
credentialManager.getCredential(context, request).let {
|
||||
// Use the credential
|
||||
handleGoogleSignIn(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -162,8 +221,10 @@ fun SignupScreen() {
|
||||
text = "CONTINUE WITH GOOGLE".uppercase(),
|
||||
backgroundImage = R.mipmap.rider_pro_signup_white_bg
|
||||
) {
|
||||
val signInIntent = googleSignInClient.signInIntent
|
||||
launcher.launch(signInIntent)
|
||||
// val signInIntent = googleSignInClient.signInIntent
|
||||
// launcher.launch(signInIntent)
|
||||
googleLogin()
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Box(
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package com.aiosman.riderpro.ui.login
|
||||
|
||||
import android.content.ContentValues.TAG
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -36,18 +40,24 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.credentials.Credential
|
||||
import androidx.credentials.CredentialManager
|
||||
import androidx.credentials.CustomCredential
|
||||
import androidx.credentials.GetCredentialRequest
|
||||
import androidx.credentials.GetCredentialResponse
|
||||
import com.aiosman.riderpro.AppStore
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.ServiceException
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
||||
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@@ -57,10 +67,13 @@ fun UserAuthScreen() {
|
||||
var email by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var rememberMe by remember { mutableStateOf(false) }
|
||||
var accountService: AccountService = TestAccountServiceImpl()
|
||||
var accountService: AccountService = AccountServiceImpl()
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val credentialManager = CredentialManager.create(context)
|
||||
|
||||
|
||||
fun onLogin() {
|
||||
scope.launch {
|
||||
try {
|
||||
@@ -82,6 +95,59 @@ fun UserAuthScreen() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun onLoginWithGoogle(googleId: String) {
|
||||
scope.launch {
|
||||
try {
|
||||
val authResp = accountService.loginUserWithGoogle(googleId)
|
||||
if (authResp.token != null) {
|
||||
AppStore.apply {
|
||||
token = authResp.token
|
||||
this.rememberMe = rememberMe
|
||||
saveData()
|
||||
}
|
||||
navController.navigate(NavigationRoute.Index.route) {
|
||||
popUpTo(NavigationRoute.Login.route) { inclusive = true }
|
||||
}
|
||||
}
|
||||
} catch (e: ServiceException) {
|
||||
// handle error
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun handleGoogleSignIn(result: GetCredentialResponse) {
|
||||
val credential: Credential = result.credential
|
||||
|
||||
if (credential is CustomCredential) {
|
||||
if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.type)) {
|
||||
try {
|
||||
val googleIdTokenCredential: GoogleIdTokenCredential =
|
||||
GoogleIdTokenCredential.createFrom(credential.data)
|
||||
onLoginWithGoogle(googleIdTokenCredential.idToken)
|
||||
} catch (e: GoogleIdTokenParsingException) {
|
||||
Log.e(TAG, "Received an invalid google id token response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun googleLogin() {
|
||||
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
|
||||
.setFilterByAuthorizedAccounts(true)
|
||||
.setServerClientId("754277015802-pnua6tg8ibnjq69lv8qdcmsdhbe97ag9.apps.googleusercontent.com")
|
||||
.build()
|
||||
val request = GetCredentialRequest.Builder().addCredentialOption(googleIdOption)
|
||||
.build()
|
||||
scope.launch {
|
||||
credentialManager.getCredential(context, request).let {
|
||||
// Use the credential
|
||||
handleGoogleSignIn(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
StatusBarMaskLayout {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -150,6 +216,25 @@ fun UserAuthScreen() {
|
||||
|
||||
Spacer(modifier = Modifier.height(121.dp))
|
||||
Text("or login with", color = Color(0xFF999999))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
.padding(16.dp)
|
||||
.border(2.dp, Color(0xFFEBEBEB))
|
||||
.noRippleClickable {
|
||||
// login with facebook
|
||||
googleLogin()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_google),
|
||||
contentDescription = "Google",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@ import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -72,7 +70,6 @@ import androidx.paging.cachedIn
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.map
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||
@@ -85,7 +82,7 @@ import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.CommentService
|
||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
@@ -133,7 +130,7 @@ class PostViewModel(
|
||||
|
||||
var accountProfileEntity by mutableStateOf<AccountProfileEntity?>(null)
|
||||
var moment by mutableStateOf<MomentEntity?>(null)
|
||||
var accountService: AccountService = TestAccountServiceImpl()
|
||||
var accountService: AccountService = AccountServiceImpl()
|
||||
|
||||
suspend fun initData() {
|
||||
moment = service.getMomentById(postId.toInt())
|
||||
|
||||
11
app/src/main/res/drawable/rider_pro_google.xml
Normal file
11
app/src/main/res/drawable/rider_pro_google.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="150dp" android:viewportHeight="150" android:viewportWidth="150" android:width="150dp">
|
||||
|
||||
<path android:fillColor="#4280EF" android:pathData="M120,76.1c0,-3.1 -0.3,-6.3 -0.8,-9.3H75.9v17.7h24.8c-1,5.7 -4.3,10.7 -9.2,13.9l14.8,11.5C115,101.8 120,90 120,76.1L120,76.1z"/>
|
||||
|
||||
<path android:fillColor="#34A353" android:pathData="M75.9,120.9c12.4,0 22.8,-4.1 30.4,-11.1L91.5,98.4c-4.1,2.8 -9.4,4.4 -15.6,4.4c-12,0 -22.1,-8.1 -25.8,-18.9L34.9,95.6C42.7,111.1 58.5,120.9 75.9,120.9z"/>
|
||||
|
||||
<path android:fillColor="#F6B704" android:pathData="M50.1,83.8c-1.9,-5.7 -1.9,-11.9 0,-17.6L34.9,54.4c-6.5,13 -6.5,28.3 0,41.2L50.1,83.8z"/>
|
||||
|
||||
<path android:fillColor="#E54335" android:pathData="M75.9,47.3c6.5,-0.1 12.9,2.4 17.6,6.9L106.6,41C98.3,33.2 87.3,29 75.9,29.1c-17.4,0 -33.2,9.8 -41,25.3l15.2,11.8C53.8,55.3 63.9,47.3 75.9,47.3z"/>
|
||||
|
||||
</vector>
|
||||
Reference in New Issue
Block a user