更新 google 登录
This commit is contained in:
@@ -68,7 +68,9 @@ dependencies {
|
|||||||
implementation(libs.androidx.media3.ui) // UI组件(可选)
|
implementation(libs.androidx.media3.ui) // UI组件(可选)
|
||||||
implementation(libs.androidx.media3.session)
|
implementation(libs.androidx.media3.session)
|
||||||
implementation(libs.androidx.activity.ktx)
|
implementation(libs.androidx.activity.ktx)
|
||||||
implementation(libs.androidx.lifecycle.common.jvm) // 用于媒体会话(可选)
|
implementation(libs.androidx.lifecycle.common.jvm)
|
||||||
|
implementation(libs.googleid)
|
||||||
|
implementation(libs.identity.credential) // 用于媒体会话(可选)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@@ -83,12 +85,12 @@ dependencies {
|
|||||||
implementation("com.google.android.gms:play-services-auth:21.2.0")
|
implementation("com.google.android.gms:play-services-auth:21.2.0")
|
||||||
implementation("io.github.serpro69:kotlin-faker:2.0.0-rc.5")
|
implementation("io.github.serpro69:kotlin-faker:2.0.0-rc.5")
|
||||||
implementation("androidx.compose.material:material:1.6.8")
|
implementation("androidx.compose.material:material:1.6.8")
|
||||||
implementation("com.facebook.android:facebook-android-sdk:17.0.0")
|
|
||||||
// platform("com.google.firebase:firebase-bom:33.1.2")
|
|
||||||
// implementation("com.google.firebase:firebase-analytics")
|
|
||||||
implementation("net.engawapg.lib:zoomable:1.6.1")
|
implementation("net.engawapg.lib:zoomable:1.6.1")
|
||||||
implementation("com.squareup.retrofit2:retrofit:2.11.0")
|
implementation("com.squareup.retrofit2:retrofit:2.11.0")
|
||||||
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
|
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
|
||||||
|
implementation("androidx.credentials:credentials:1.2.2")
|
||||||
|
implementation("androidx.credentials:credentials-play-services-auth:1.2.2")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ package com.aiosman.riderpro
|
|||||||
|
|
||||||
object ConstVars {
|
object ConstVars {
|
||||||
// api 地址
|
// 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.NavHostController
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.ServiceException
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
|
||||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
|
||||||
import com.aiosman.riderpro.data.UserService
|
|
||||||
import com.aiosman.riderpro.ui.Navigation
|
import com.aiosman.riderpro.ui.Navigation
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.index.NavigationItem
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import retrofit2.HttpException
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private val scope = CoroutineScope(Dispatchers.Main)
|
private val scope = CoroutineScope(Dispatchers.Main)
|
||||||
suspend fun getAccount(): Boolean {
|
suspend fun getAccount(): Boolean {
|
||||||
val accountService: AccountService = TestAccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
try {
|
try {
|
||||||
val resp = accountService.getMyAccount()
|
val resp = accountService.getMyAccount()
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package com.aiosman.riderpro.data
|
|||||||
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.aiosman.riderpro.AppStore
|
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
import com.aiosman.riderpro.data.api.ChangePasswordRequestBody
|
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.LoginUserRequestBody
|
||||||
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
import com.aiosman.riderpro.data.api.RegisterRequestBody
|
||||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||||
@@ -24,11 +24,13 @@ data class AccountLikeEntity(
|
|||||||
val user: NoticeUserEntity,
|
val user: NoticeUserEntity,
|
||||||
val likeTime: Date,
|
val likeTime: Date,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AccountFavouriteEntity(
|
data class AccountFavouriteEntity(
|
||||||
val post: NoticePostEntity,
|
val post: NoticePostEntity,
|
||||||
val user: NoticeUserEntity,
|
val user: NoticeUserEntity,
|
||||||
val favoriteTime: Date,
|
val favoriteTime: Date,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AccountProfileEntity(
|
data class AccountProfileEntity(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val followerCount: Int,
|
val followerCount: Int,
|
||||||
@@ -281,10 +283,12 @@ interface AccountService {
|
|||||||
suspend fun getAccountProfileById(id: Int): AccountProfileEntity
|
suspend fun getAccountProfileById(id: Int): AccountProfileEntity
|
||||||
suspend fun getMyAccount(): UserAuth
|
suspend fun getMyAccount(): UserAuth
|
||||||
suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth
|
suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth
|
||||||
|
suspend fun loginUserWithGoogle(googleId: String): UserAuth
|
||||||
suspend fun logout()
|
suspend fun logout()
|
||||||
suspend fun updateAvatar(uri: String)
|
suspend fun updateAvatar(uri: String)
|
||||||
suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?)
|
suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?)
|
||||||
suspend fun registerUserWithPassword(loginName: String, password: String)
|
suspend fun registerUserWithPassword(loginName: String, password: String)
|
||||||
|
suspend fun regiterUserWithGoogleAccount(idToken: String)
|
||||||
suspend fun changeAccountPassword(oldPassword: String, newPassword: String)
|
suspend fun changeAccountPassword(oldPassword: String, newPassword: String)
|
||||||
suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer<AccountLike>
|
suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer<AccountLike>
|
||||||
suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer<AccountFollow>
|
suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer<AccountFollow>
|
||||||
@@ -293,7 +297,7 @@ interface AccountService {
|
|||||||
suspend fun updateNotice(payload: UpdateNoticeRequestBody)
|
suspend fun updateNotice(payload: UpdateNoticeRequestBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestAccountServiceImpl : AccountService {
|
class AccountServiceImpl : AccountService {
|
||||||
override suspend fun getMyAccountProfile(): AccountProfileEntity {
|
override suspend fun getMyAccountProfile(): AccountProfileEntity {
|
||||||
val resp = ApiClient.api.getMyAccount()
|
val resp = ApiClient.api.getMyAccount()
|
||||||
val body = resp.body() ?: throw ServiceException("Failed to get account")
|
val body = resp.body() ?: throw ServiceException("Failed to get account")
|
||||||
@@ -318,6 +322,19 @@ class TestAccountServiceImpl : AccountService {
|
|||||||
return UserAuth(0, body.token)
|
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() {
|
override suspend fun logout() {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,22 @@ data class RegisterRequestBody(
|
|||||||
@SerializedName("username")
|
@SerializedName("username")
|
||||||
val username: String,
|
val username: String,
|
||||||
@SerializedName("password")
|
@SerializedName("password")
|
||||||
val password: String
|
val password: String,
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class LoginUserRequestBody(
|
data class LoginUserRequestBody(
|
||||||
@SerializedName("username")
|
@SerializedName("username")
|
||||||
val username: String,
|
val username: String? = null,
|
||||||
@SerializedName("password")
|
@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(
|
data class AuthResult(
|
||||||
@@ -219,4 +227,7 @@ interface RiderProAPI {
|
|||||||
@Query("nickname") search: String? = null,
|
@Query("nickname") search: String? = null,
|
||||||
): Response<ListContainer<AccountProfile>>
|
): 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 androidx.compose.ui.unit.dp
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ChangePasswordViewModel {
|
class ChangePasswordViewModel {
|
||||||
val accountService :AccountService = TestAccountServiceImpl()
|
val accountService :AccountService = AccountServiceImpl()
|
||||||
suspend fun changePassword(currentPassword: String, newPassword: String) {
|
suspend fun changePassword(currentPassword: String, newPassword: String) {
|
||||||
accountService.changeAccountPassword(currentPassword, newPassword)
|
accountService.changeAccountPassword(currentPassword, newPassword)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,9 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.aiosman.riderpro.data.AccountProfileEntity
|
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||||
import com.aiosman.riderpro.data.AccountService
|
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.TestUserServiceImpl
|
||||||
import com.aiosman.riderpro.data.UploadImage
|
import com.aiosman.riderpro.data.UploadImage
|
||||||
import com.aiosman.riderpro.data.UserService
|
import com.aiosman.riderpro.data.UserService
|
||||||
@@ -49,7 +48,7 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun AccountEditScreen() {
|
fun AccountEditScreen() {
|
||||||
val userService: UserService = TestUserServiceImpl()
|
val userService: UserService = TestUserServiceImpl()
|
||||||
val accountService: AccountService = TestAccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
var name by remember { mutableStateOf("") }
|
var name by remember { mutableStateOf("") }
|
||||||
var bio by remember { mutableStateOf("") }
|
var bio by remember { mutableStateOf("") }
|
||||||
var imageUrl by remember { mutableStateOf<Uri?>(null) }
|
var imageUrl by remember { mutableStateOf<Uri?>(null) }
|
||||||
|
|||||||
@@ -1,12 +1,45 @@
|
|||||||
package com.aiosman.riderpro.ui.composables
|
package com.aiosman.riderpro.ui.composables
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import androidx.compose.runtime.Composable
|
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.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
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.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import coil.request.SuccessResult
|
||||||
import com.aiosman.riderpro.utils.Utils.getImageLoader
|
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
|
@Composable
|
||||||
fun CustomAsyncImage(
|
fun CustomAsyncImage(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -15,9 +48,10 @@ fun CustomAsyncImage(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
contentScale: ContentScale = ContentScale.Crop
|
contentScale: ContentScale = ContentScale.Crop
|
||||||
) {
|
) {
|
||||||
|
val bitmap = rememberImageBitmap(imageUrl, getImageLoader(context))
|
||||||
val imageLoader = getImageLoader(context)
|
val imageLoader = getImageLoader(context)
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = imageUrl,
|
model = bitmap,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentScale = contentScale,
|
contentScale = contentScale,
|
||||||
|
|||||||
@@ -7,23 +7,19 @@ import androidx.paging.Pager
|
|||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import com.aiosman.riderpro.data.AccountFavourite
|
|
||||||
import com.aiosman.riderpro.data.AccountFavouriteEntity
|
import com.aiosman.riderpro.data.AccountFavouriteEntity
|
||||||
import com.aiosman.riderpro.data.AccountLike
|
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.FavoriteItemPagingSource
|
import com.aiosman.riderpro.data.FavoriteItemPagingSource
|
||||||
import com.aiosman.riderpro.data.LikeItemPagingSource
|
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||||
import com.aiosman.riderpro.ui.like.LikePageViewModel
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object FavouritePageViewModel : ViewModel() {
|
object FavouritePageViewModel : ViewModel() {
|
||||||
private val accountService: AccountService = TestAccountServiceImpl()
|
private val accountService: AccountService = AccountServiceImpl()
|
||||||
private val _favouriteItemsFlow =
|
private val _favouriteItemsFlow =
|
||||||
MutableStateFlow<PagingData<AccountFavouriteEntity>>(PagingData.empty())
|
MutableStateFlow<PagingData<AccountFavouriteEntity>>(PagingData.empty())
|
||||||
val favouriteItemsFlow = _favouriteItemsFlow.asStateFlow()
|
val favouriteItemsFlow = _favouriteItemsFlow.asStateFlow()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import androidx.paging.map
|
|||||||
import com.aiosman.riderpro.data.AccountFollow
|
import com.aiosman.riderpro.data.AccountFollow
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.FollowItemPagingSource
|
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.TestUserServiceImpl
|
||||||
import com.aiosman.riderpro.data.UserService
|
import com.aiosman.riderpro.data.UserService
|
||||||
import com.aiosman.riderpro.data.api.ApiClient
|
import com.aiosman.riderpro.data.api.ApiClient
|
||||||
@@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object FollowerViewModel : ViewModel() {
|
object FollowerViewModel : ViewModel() {
|
||||||
private val accountService: AccountService = TestAccountServiceImpl()
|
private val accountService: AccountService = AccountServiceImpl()
|
||||||
private val userService: UserService = TestUserServiceImpl()
|
private val userService: UserService = TestUserServiceImpl()
|
||||||
private val _followerItemsFlow =
|
private val _followerItemsFlow =
|
||||||
MutableStateFlow<PagingData<AccountFollow>>(PagingData.empty())
|
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.CommentPagingSource
|
||||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||||
import com.aiosman.riderpro.data.CommentService
|
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 com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object MessageListViewModel : ViewModel() {
|
object MessageListViewModel : ViewModel() {
|
||||||
val accountService: AccountService = TestAccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
var noticeInfo by mutableStateOf<AccountNotice?>(null)
|
var noticeInfo by mutableStateOf<AccountNotice?>(null)
|
||||||
|
|
||||||
private val commentService: CommentService = TestCommentServiceImpl()
|
private val commentService: CommentService = TestCommentServiceImpl()
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
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.AnimatedCounter
|
||||||
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
||||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
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.CustomAsyncImage
|
||||||
import com.aiosman.riderpro.ui.composables.RelPostCard
|
import com.aiosman.riderpro.ui.composables.RelPostCard
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
@@ -102,7 +102,10 @@ fun MomentsList() {
|
|||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
items(moments.itemCount) { idx ->
|
items(
|
||||||
|
moments.itemCount,
|
||||||
|
key = { idx -> moments[idx]?.id ?: idx }
|
||||||
|
) { idx ->
|
||||||
val momentItem = moments[idx] ?: return@items
|
val momentItem = moments[idx] ?: return@items
|
||||||
MomentCard(momentEntity = momentItem,
|
MomentCard(momentEntity = momentItem,
|
||||||
onAddComment = {
|
onAddComment = {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.aiosman.riderpro.data.AccountService
|
|||||||
import com.aiosman.riderpro.data.MomentPagingSource
|
import com.aiosman.riderpro.data.MomentPagingSource
|
||||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||||
import com.aiosman.riderpro.data.MomentService
|
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.TestMomentServiceImpl
|
||||||
import com.aiosman.riderpro.model.MomentEntity
|
import com.aiosman.riderpro.model.MomentEntity
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -24,7 +24,7 @@ object MomentViewModel : ViewModel() {
|
|||||||
private val momentService: MomentService = TestMomentServiceImpl()
|
private val momentService: MomentService = TestMomentServiceImpl()
|
||||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||||
val momentsFlow = _momentsFlow.asStateFlow()
|
val momentsFlow = _momentsFlow.asStateFlow()
|
||||||
val accountService: AccountService = TestAccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val profile = accountService.getMyAccountProfile()
|
val profile = accountService.getMyAccountProfile()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import androidx.paging.PagingData
|
|||||||
import com.aiosman.riderpro.AppStore
|
import com.aiosman.riderpro.AppStore
|
||||||
import com.aiosman.riderpro.data.AccountProfileEntity
|
import com.aiosman.riderpro.data.AccountProfileEntity
|
||||||
import com.aiosman.riderpro.data.AccountService
|
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.MomentPagingSource
|
||||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||||
@@ -18,7 +18,7 @@ import com.aiosman.riderpro.model.MomentEntity
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
object MyProfileViewModel {
|
object MyProfileViewModel {
|
||||||
val service: AccountService = TestAccountServiceImpl()
|
val service: AccountService = AccountServiceImpl()
|
||||||
val userService = TestUserServiceImpl()
|
val userService = TestUserServiceImpl()
|
||||||
var profile by mutableStateOf<AccountProfileEntity?>(null)
|
var profile by mutableStateOf<AccountProfileEntity?>(null)
|
||||||
var momentsFlow by mutableStateOf<Flow<PagingData<MomentEntity>>?>(null)
|
var momentsFlow by mutableStateOf<Flow<PagingData<MomentEntity>>?>(null)
|
||||||
|
|||||||
@@ -7,11 +7,10 @@ import androidx.paging.Pager
|
|||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import com.aiosman.riderpro.data.AccountLike
|
|
||||||
import com.aiosman.riderpro.data.AccountLikeEntity
|
import com.aiosman.riderpro.data.AccountLikeEntity
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.LikeItemPagingSource
|
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.ApiClient
|
||||||
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -21,7 +20,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
|
|
||||||
object LikePageViewModel : ViewModel() {
|
object LikePageViewModel : ViewModel() {
|
||||||
private val accountService: AccountService = TestAccountServiceImpl()
|
private val accountService: AccountService = AccountServiceImpl()
|
||||||
private val _likeItemsFlow = MutableStateFlow<PagingData<AccountLikeEntity>>(PagingData.empty())
|
private val _likeItemsFlow = MutableStateFlow<PagingData<AccountLikeEntity>>(PagingData.empty())
|
||||||
val likeItemsFlow = _likeItemsFlow.asStateFlow()
|
val likeItemsFlow = _likeItemsFlow.asStateFlow()
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import com.aiosman.riderpro.LocalNavController
|
|||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.ServiceException
|
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.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
@@ -47,7 +47,7 @@ fun EmailSignupScreen() {
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val accountService: AccountService = TestAccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
fun validateForm(): Boolean {
|
fun validateForm(): Boolean {
|
||||||
if (email.isEmpty()) {
|
if (email.isEmpty()) {
|
||||||
Toast.makeText(context, "Email is required", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Email is required", Toast.LENGTH_SHORT).show()
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.aiosman.riderpro.ui.login
|
package com.aiosman.riderpro.ui.login
|
||||||
|
|
||||||
import android.app.Activity
|
import android.content.ContentValues.TAG
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
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.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
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.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.AppStore
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
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.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
|
||||||
import com.google.android.gms.common.api.ApiException
|
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
|
||||||
import com.google.android.gms.tasks.Task
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SignupScreen() {
|
fun SignupScreen() {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val googleSignInClient = GoogleSignIn.getClient(context, AppStore.googleSignInOptions)
|
val coroutineScope = rememberCoroutineScope()
|
||||||
fun handleSignInResult(
|
val credentialManager = CredentialManager.create(context)
|
||||||
task: Task<GoogleSignInAccount>,
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
onSignInSuccess: (GoogleSignInAccount) -> Unit
|
|
||||||
) {
|
fun registerWithGoogle(idToken: String) {
|
||||||
|
coroutineScope.launch {
|
||||||
try {
|
try {
|
||||||
val account = task.getResult(ApiException::class.java)
|
accountService.regiterUserWithGoogleAccount(idToken)
|
||||||
onSignInSuccess(account)
|
} catch (e: Exception) {
|
||||||
} catch (e: ApiException) {
|
Log.e(TAG, "Failed to register with google", e)
|
||||||
// Handle sign-in failure
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val launcher = rememberLauncherForActivityResult(
|
AppStore.apply {
|
||||||
contract = ActivityResultContracts.StartActivityForResult(),
|
token = authResp.token
|
||||||
onResult = { result ->
|
this.rememberMe = true
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
saveData()
|
||||||
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
|
}
|
||||||
handleSignInResult(task) {account ->
|
// 获取token 信息
|
||||||
// Handle sign-in success
|
try {
|
||||||
Log.d("SignupScreen", "handleSignInResult: $account")
|
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(
|
Scaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -162,8 +221,10 @@ fun SignupScreen() {
|
|||||||
text = "CONTINUE WITH GOOGLE".uppercase(),
|
text = "CONTINUE WITH GOOGLE".uppercase(),
|
||||||
backgroundImage = R.mipmap.rider_pro_signup_white_bg
|
backgroundImage = R.mipmap.rider_pro_signup_white_bg
|
||||||
) {
|
) {
|
||||||
val signInIntent = googleSignInClient.signInIntent
|
// val signInIntent = googleSignInClient.signInIntent
|
||||||
launcher.launch(signInIntent)
|
// launcher.launch(signInIntent)
|
||||||
|
googleLogin()
|
||||||
|
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package com.aiosman.riderpro.ui.login
|
package com.aiosman.riderpro.ui.login
|
||||||
|
|
||||||
|
import android.content.ContentValues.TAG
|
||||||
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
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.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.AppStore
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.data.ServiceException
|
import com.aiosman.riderpro.data.ServiceException
|
||||||
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.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
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
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
@@ -57,10 +67,13 @@ fun UserAuthScreen() {
|
|||||||
var email by remember { mutableStateOf("") }
|
var email by remember { mutableStateOf("") }
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var rememberMe by remember { mutableStateOf(false) }
|
var rememberMe by remember { mutableStateOf(false) }
|
||||||
var accountService: AccountService = TestAccountServiceImpl()
|
var accountService: AccountService = AccountServiceImpl()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val credentialManager = CredentialManager.create(context)
|
||||||
|
|
||||||
|
|
||||||
fun onLogin() {
|
fun onLogin() {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
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 {
|
StatusBarMaskLayout {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@@ -150,6 +216,25 @@ fun UserAuthScreen() {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(121.dp))
|
Spacer(modifier = Modifier.height(121.dp))
|
||||||
Text("or login with", color = Color(0xFF999999))
|
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.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
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.Edit
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.Star
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -72,7 +70,6 @@ import androidx.paging.cachedIn
|
|||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import androidx.paging.map
|
import androidx.paging.map
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
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.CommentService
|
||||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||||
import com.aiosman.riderpro.data.MomentService
|
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.TestMomentServiceImpl
|
||||||
import com.aiosman.riderpro.data.TestUserServiceImpl
|
import com.aiosman.riderpro.data.TestUserServiceImpl
|
||||||
import com.aiosman.riderpro.data.UserService
|
import com.aiosman.riderpro.data.UserService
|
||||||
@@ -133,7 +130,7 @@ class PostViewModel(
|
|||||||
|
|
||||||
var accountProfileEntity by mutableStateOf<AccountProfileEntity?>(null)
|
var accountProfileEntity by mutableStateOf<AccountProfileEntity?>(null)
|
||||||
var moment by mutableStateOf<MomentEntity?>(null)
|
var moment by mutableStateOf<MomentEntity?>(null)
|
||||||
var accountService: AccountService = TestAccountServiceImpl()
|
var accountService: AccountService = AccountServiceImpl()
|
||||||
|
|
||||||
suspend fun initData() {
|
suspend fun initData() {
|
||||||
moment = service.getMomentById(postId.toInt())
|
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>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
accompanistSystemuicontroller = "0.27.0"
|
accompanistSystemuicontroller = "0.27.0"
|
||||||
agp = "8.4.0"
|
agp = "8.4.0"
|
||||||
animation = "1.7.0-beta05"
|
animation = "1.7.0-beta05"
|
||||||
|
composeImageBlurhash = "3.0.2"
|
||||||
kotlin = "1.9.0"
|
kotlin = "1.9.0"
|
||||||
coreKtx = "1.10.1"
|
coreKtx = "1.10.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
@@ -18,6 +19,8 @@ pagingRuntime = "3.3.0"
|
|||||||
activityKtx = "1.9.0"
|
activityKtx = "1.9.0"
|
||||||
lifecycleCommonJvm = "2.8.2"
|
lifecycleCommonJvm = "2.8.2"
|
||||||
places = "3.3.0"
|
places = "3.3.0"
|
||||||
|
googleid = "1.1.1"
|
||||||
|
identityCredential = "20231002"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
|
||||||
@@ -29,6 +32,7 @@ androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "medi
|
|||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingRuntime" }
|
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingRuntime" }
|
||||||
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pagingRuntime" }
|
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pagingRuntime" }
|
||||||
|
compose-image-blurhash = { module = "com.github.orlando-dev-code:compose-image-blurhash", version.ref = "composeImageBlurhash" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
@@ -47,6 +51,8 @@ maps-compose = { module = "com.google.maps.android:maps-compose", version.ref =
|
|||||||
androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
|
androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
|
||||||
androidx-lifecycle-common-jvm = { group = "androidx.lifecycle", name = "lifecycle-common-jvm", version.ref = "lifecycleCommonJvm" }
|
androidx-lifecycle-common-jvm = { group = "androidx.lifecycle", name = "lifecycle-common-jvm", version.ref = "lifecycleCommonJvm" }
|
||||||
places = { module = "com.google.android.libraries.places:places", version.ref = "places" }
|
places = { module = "com.google.android.libraries.places:places", version.ref = "places" }
|
||||||
|
googleid = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "googleid" }
|
||||||
|
identity-credential = { group = "com.android.identity", name = "identity-credential", version.ref = "identityCredential" }
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
|||||||
Reference in New Issue
Block a user