diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..e0e8a13 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "150225239581", + "project_id": "riderp-30072", + "storage_bucket": "riderp-30072.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:150225239581:android:422283723db3908bdfd0f4", + "android_client_info": { + "package_name": "com.aiosman.riderpro" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDNGmfzdCTJ0PxQ3yTwdWhRaDhqS1JDWaQ" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/store.kt b/app/src/main/java/com/aiosman/riderpro/store.kt index c1dd0f0..9649f50 100644 --- a/app/src/main/java/com/aiosman/riderpro/store.kt +++ b/app/src/main/java/com/aiosman/riderpro/store.kt @@ -2,6 +2,7 @@ package com.aiosman.riderpro import android.content.Context import android.content.SharedPreferences +import com.google.android.gms.auth.api.signin.GoogleSignInOptions /** * 持久化本地数据 @@ -12,10 +13,16 @@ object AppStore { var token: String? = null var rememberMe: Boolean = false private lateinit var sharedPreferences: SharedPreferences - + lateinit var googleSignInOptions: GoogleSignInOptions fun init(context: Context) { sharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) this.loadData() + + val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken("754277015802-uarf8br8k8gkpbj0t9g65bvkvit630q5.apps.googleusercontent.com") // Replace with your server's client ID + .requestEmail() + .build() + googleSignInOptions = gso } suspend fun saveData() { diff --git a/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt b/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt index d86e0f7..d584c54 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt @@ -1,5 +1,6 @@ package com.aiosman.riderpro.ui +import ImageViewer import ModificationListScreen import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionLayout @@ -62,6 +63,7 @@ sealed class NavigationRoute( data object UserAuth : NavigationRoute("UserAuth") data object EmailSignUp : NavigationRoute("EmailSignUp") data object AccountEdit : NavigationRoute("AccountEditScreen") + data object ImageViewer : NavigationRoute("ImageViewer") } @@ -171,6 +173,13 @@ fun NavigationController( composable(route = NavigationRoute.AccountEdit.route) { AccountEditScreen() } + composable(route = NavigationRoute.ImageViewer.route) { + CompositionLocalProvider( + LocalAnimatedContentScope provides this, + ) { + ImageViewer() + } + } } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt b/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt index d92f9ce..8cd19d7 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt @@ -4,8 +4,6 @@ import android.app.Activity import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -38,14 +36,12 @@ import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.TestAccountServiceImpl import com.aiosman.riderpro.data.TestUserServiceImpl import com.aiosman.riderpro.data.UserService -import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.modifiers.noRippleClickable import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun AccountEditScreen() { - val userService: UserService = TestUserServiceImpl() val accountService: AccountService = TestAccountServiceImpl() var name by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt new file mode 100644 index 0000000..772a8ca --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt @@ -0,0 +1,11 @@ +package com.aiosman.riderpro.ui.imageviewer + +object ImageViewerViewModel { + var imageList = mutableListOf() + var initialIndex = 0 + fun asNew(images: List, index: Int = 0) { + imageList.clear() + imageList.addAll(images) + initialIndex = index + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/imageviewer.kt b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/imageviewer.kt new file mode 100644 index 0000000..c501221 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/imageviewer.kt @@ -0,0 +1,74 @@ +import androidx.compose.animation.ExperimentalSharedTransitionApi +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import coil.compose.AsyncImage +import com.aiosman.riderpro.LocalAnimatedContentScope +import com.aiosman.riderpro.LocalNavController +import com.aiosman.riderpro.LocalSharedTransitionScope +import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout +import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import net.engawapg.lib.zoomable.rememberZoomState +import net.engawapg.lib.zoomable.zoomable + + +@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class) +@Composable +fun ImageViewer() { + val model = ImageViewerViewModel + val images = model.imageList + val pagerState = rememberPagerState(pageCount = { images.size }, initialPage = model.initialIndex) + val systemUiController = rememberSystemUiController() + val navController = LocalNavController.current + val sharedTransitionScope = LocalSharedTransitionScope.current + val animatedVisibilityScope = LocalAnimatedContentScope.current + + LaunchedEffect(Unit) { + systemUiController.setStatusBarColor(Color.Black) + systemUiController.setNavigationBarColor(Color.Black) + } + StatusBarMaskLayout( + modifier = Modifier.background(Color.Black), + ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black) + ) { + HorizontalPager( + state = pagerState, + modifier = Modifier.fillMaxSize() + ) { page -> + val zoomState = rememberZoomState() + with(sharedTransitionScope) { + AsyncImage( + images[page], + contentDescription = null, + modifier = Modifier.sharedElement( + rememberSharedContentState(key = images[page]), + animatedVisibilityScope = animatedVisibilityScope + ) + .fillMaxSize() + .zoomable( + zoomState = zoomState, + onTap = { + navController.popBackStack() + } + ), + contentScale = ContentScale.Fit, + ) + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/login/signup.kt b/app/src/main/java/com/aiosman/riderpro/ui/login/signup.kt index 6b3a780..9fa89ec 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/login/signup.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/login/signup.kt @@ -1,5 +1,9 @@ package com.aiosman.riderpro.ui.login +import android.app.Activity +import android.util.Log +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -20,18 +24,49 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext 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 com.aiosman.riderpro.AppStore import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.R 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 @Composable fun SignupScreen() { val navController = LocalNavController.current + val context = LocalContext.current + val googleSignInClient = GoogleSignIn.getClient(context, AppStore.googleSignInOptions) + fun handleSignInResult( + task: Task, + 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") + } + } + } + ) Scaffold( modifier = Modifier .fillMaxSize() @@ -126,7 +161,10 @@ fun SignupScreen() { }, text = "CONTINUE WITH GOOGLE".uppercase(), backgroundImage = R.mipmap.rider_pro_signup_white_bg - ) + ) { + val signInIntent = googleSignInClient.signInIntent + launcher.launch(signInIntent) + } Spacer(modifier = Modifier.height(16.dp)) Box( modifier = Modifier diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt index 3b828e9..1dbd847 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt @@ -70,7 +70,9 @@ 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 import com.aiosman.riderpro.R import com.aiosman.riderpro.data.AccountProfile import com.aiosman.riderpro.data.AccountService @@ -87,6 +89,7 @@ import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder import com.aiosman.riderpro.ui.composables.EditCommentBottomModal +import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.google.accompanist.systemuicontroller.rememberSystemUiController @@ -332,28 +335,43 @@ fun Header(accountProfile: AccountProfile?) { } -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class) @Composable fun PostImageView( postId: String, images: List, ) { val pagerState = rememberPagerState(pageCount = { images.size }) + val navController = LocalNavController.current + val sharedTransitionScope = LocalSharedTransitionScope.current + val animatedVisibilityScope = LocalAnimatedContentScope.current Column { HorizontalPager( state = pagerState, modifier = Modifier .weight(1f) - .fillMaxWidth(), + .fillMaxWidth().background(Color.Black), ) { page -> val image = images[page] - AsyncImage( - image, - contentDescription = "Image", - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxSize() - ) + with(sharedTransitionScope) { + AsyncImage( + image, + contentDescription = "Image", + contentScale = ContentScale.Fit, + modifier = Modifier + .sharedElement( + rememberSharedContentState(key = image), + animatedVisibilityScope = animatedVisibilityScope + ) + .fillMaxSize() + .noRippleClickable { + ImageViewerViewModel.asNew(images, page) + navController.navigate( + NavigationRoute.ImageViewer.route + ) + } + ) + } } // Indicator container @@ -547,7 +565,11 @@ fun BottomNavigationBar( onClick = { onLikeClick() }) { - Icon(Icons.Filled.Favorite, contentDescription = "like", tint = if (momentItem?.liked == true) Color.Red else Color.Gray) + Icon( + Icons.Filled.Favorite, + contentDescription = "like", + tint = if (momentItem?.liked == true) Color.Red else Color.Gray + ) } Text(text = momentItem?.likeCount.toString()) IconButton( diff --git a/app/src/main/java/com/aiosman/riderpro/ui/search/search.kt b/app/src/main/java/com/aiosman/riderpro/ui/search/search.kt new file mode 100644 index 0000000..086d6c3 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/ui/search/search.kt @@ -0,0 +1,67 @@ +package com.aiosman.riderpro.ui.search + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +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 + +@Composable +fun SearchScreen() { + Scaffold { paddingValues -> + var tabIndex by remember { mutableStateOf(0) } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(paddingValues) + ) { + TextField( + value = "", + onValueChange = {}, + label = { Text("Search") }, + modifier = Modifier.fillMaxWidth(), + trailingIcon = { + Icon( + imageVector = Icons.Default.Search, + contentDescription = null + ) + } + ) + TabRow(selectedTabIndex = tabIndex) { + Tab(text = { Text("Post") }, + selected = tabIndex == 0, + onClick = { tabIndex = 0 } + ) + Tab(text = { Text("User") }, + selected = tabIndex == 1, + onClick = { tabIndex = 1 } + ) + } + when (tabIndex) { + 0 -> SearchPostResults() + 1 -> SearchUserResults() + } + } + } +} + +@Composable +fun SearchPostResults() { + Text("Post Results") +} +@Composable +fun SearchUserResults(){ + Text("User Results") +} \ No newline at end of file