From fda6fe4dcf56c68015fab2fc2820b57d97d105fc Mon Sep 17 00:00:00 2001 From: AllenTom Date: Thu, 5 Sep 2024 22:04:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 4 +- app/src/main/AndroidManifest.xml | 21 ++ .../main/java/com/aiosman/riderpro/Colors.kt | 7 + .../main/java/com/aiosman/riderpro/Const.kt | 3 + .../java/com/aiosman/riderpro/MainActivity.kt | 192 +++++------ .../aiosman/riderpro/MainActivityLifecycle.kt | 18 + .../java/com/aiosman/riderpro/Messaging.kt | 30 ++ .../riderpro/MyFirebaseMessagingService.kt | 95 ++++++ .../aiosman/riderpro/data/AccountService.kt | 7 + .../aiosman/riderpro/data/api/RiderProAPI.kt | 11 + .../java/com/aiosman/riderpro/exp/Date.kt | 14 + .../main/java/com/aiosman/riderpro/ui/Navi.kt | 9 +- .../com/aiosman/riderpro/ui/account/edit2.kt | 316 ++++++++++++++++++ .../riderpro/ui/comment/CommentsScreen.kt | 9 +- .../riderpro/ui/index/tabs/profile/Profile.kt | 219 ++++++++---- .../aiosman/riderpro/ui/login/emailsignup.kt | 2 + .../com/aiosman/riderpro/ui/login/signup.kt | 2 + .../com/aiosman/riderpro/ui/login/userauth.kt | 18 +- gradle/libs.versions.toml | 2 + 19 files changed, 800 insertions(+), 179 deletions(-) create mode 100644 app/src/main/java/com/aiosman/riderpro/Colors.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/MainActivityLifecycle.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/Messaging.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/MyFirebaseMessagingService.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/ui/account/edit2.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7c8380a..5c8cbf6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -73,7 +73,8 @@ dependencies { implementation(libs.androidx.activity.ktx) implementation(libs.androidx.lifecycle.common.jvm) implementation(libs.googleid) - implementation(libs.identity.credential) // 用于媒体会话(可选) + implementation(libs.identity.credential) + implementation(libs.androidx.lifecycle.process) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -99,6 +100,7 @@ dependencies { implementation("com.google.firebase:firebase-crashlytics") implementation("com.google.firebase:firebase-analytics") implementation("com.google.firebase:firebase-perf") + implementation("com.google.firebase:firebase-messaging-ktx") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b69175d..4b62647 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/Colors.kt b/app/src/main/java/com/aiosman/riderpro/Colors.kt new file mode 100644 index 0000000..37df191 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/Colors.kt @@ -0,0 +1,7 @@ +package com.aiosman.riderpro + +import androidx.compose.ui.graphics.Color + +object AppColors { + val mainColor = Color(0xffED1C24) +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/Const.kt b/app/src/main/java/com/aiosman/riderpro/Const.kt index a7448c7..76a3018 100644 --- a/app/src/main/java/com/aiosman/riderpro/Const.kt +++ b/app/src/main/java/com/aiosman/riderpro/Const.kt @@ -5,4 +5,7 @@ object ConstVars { // const val BASE_SERVER = "http://192.168.31.190:8088" // const val BASE_SERVER = "http://192.168.31.36:8088" const val BASE_SERVER = "https://8.137.22.101:8088" + const val MOMENT_LIKE_CHANNEL_ID = "moment_like" + const val MOMENT_LIKE_CHANNEL_NAME = "Moment Like" + } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt index 8addff3..df92ffe 100644 --- a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt +++ b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionScope @@ -28,6 +29,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState @@ -44,10 +46,40 @@ import com.google.firebase.ktx.Firebase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import android.Manifest +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import android.widget.Toast +import androidx.lifecycle.ProcessLifecycleOwner +import androidx.lifecycle.viewModelScope +import androidx.navigation.findNavController +import com.aiosman.riderpro.ui.post.PostViewModel +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.messaging.FirebaseMessaging class MainActivity : ComponentActivity() { + // Firebase Analytics private lateinit var analytics: FirebaseAnalytics private val scope = CoroutineScope(Dispatchers.Main) + // 请求通知权限 + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { isGranted: Boolean -> + if (isGranted) { + // FCM SDK (and your app) can post notifications. + } else { + // TODO: Inform user that that your app will not show notifications. + } + } + + /** + * 获取账号信息 + */ suspend fun getAccount(): Boolean { val accountService: AccountService = AccountServiceImpl() try { @@ -60,26 +92,90 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // 监听应用生命周期 + ProcessLifecycleOwner.get().lifecycle.addObserver(MainActivityLifecycleObserver()) + // 创建通知渠道 + createNotificationChannel() + // 沉浸式状态栏 WindowCompat.setDecorFitsSystemWindows(window, false) + // 初始化 Places SDK if (!Places.isInitialized()) { Places.initialize(applicationContext, "AIzaSyDpgLDH1-SECw_pdjJq_msynq1XrxwgKVI") } + // 初始化 Firebase Analytics analytics = Firebase.analytics + // 请求通知权限 + askNotificationPermission() + // 加载一些本地化的配置 AppStore.init(this) enableEdgeToEdge() scope.launch { + // 检查是否有登录态 val isAccountValidate = getAccount() var startDestination = NavigationRoute.Login.route + // 如果有登录态,且记住登录状态,且账号有效,则初始化 FCM,下一步进入首页 if (AppStore.token != null && AppStore.rememberMe && isAccountValidate) { + Messaging.InitFCM(scope) startDestination = NavigationRoute.Index.route } setContent { - Navigation(startDestination) + Navigation(startDestination) { navController -> + // 处理带有 postId 的通知点击 + val postId = intent.getStringExtra("POST_ID") + if (postId != null) { + Log.d("MainActivity", "Navigation to Post$postId") + PostViewModel.postId = postId + PostViewModel.viewModelScope.launch { + PostViewModel.initData() + navController.navigate(NavigationRoute.Post.route.replace("{id}", postId)) + } + + } + } + } + + } + } + + /** + * 请求通知权限 + */ + private fun askNotificationPermission() { + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + ) { + // FCM SDK (and your app) can post notifications. + } else if (shouldShowRequestPermissionRationale(android.Manifest.permission.POST_NOTIFICATIONS)) { + + } else { + // Directly ask for the permission + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } } } + + /** + * 创建通知渠道 + */ + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channelId = ConstVars.MOMENT_LIKE_CHANNEL_ID + val channelName = ConstVars.MOMENT_LIKE_CHANNEL_NAME + val channel = + NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT) + val notificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + } } val LocalNavController = compositionLocalOf { @@ -96,97 +192,3 @@ val LocalAnimatedContentScope = compositionLocalOf { } -// 用于带导航栏的路由的可复用 composable -@Composable -fun ScaffoldWithNavigationBar( - navController: NavHostController, - content: @Composable () -> Unit -) { - val navigationBarHeight = with(LocalDensity.current) { - WindowInsets.navigationBars.getBottom(this).toDp() - } - val item = listOf( - NavigationItem.Home, - NavigationItem.Street, - NavigationItem.Add, - NavigationItem.Message, - NavigationItem.Profile - ) - Scaffold( - modifier = Modifier.statusBarsPadding(), - bottomBar = { - NavigationBar( - modifier = Modifier.height(56.dp + navigationBarHeight), - containerColor = Color.Black - ) { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - val systemUiController = rememberSystemUiController() - item.forEach { it -> - val isSelected = currentRoute == it.route - val iconTint by animateColorAsState( - targetValue = if (isSelected) Color.Red else Color.White, - animationSpec = tween(durationMillis = 250), label = "" - ) - NavigationBarItem( - selected = currentRoute == it.route, - onClick = { - // Check if the current route is not the same as the tab's route to avoid unnecessary navigation - if (currentRoute != it.route) { - navController.navigate(it.route) { - // Avoid creating a new layer on top of the navigation stack - launchSingleTop = true - // Attempt to pop up to the existing instance of the destination, if present - popUpTo(navController.graph.startDestinationId) { - saveState = true - } - // Restore state when navigating back to the composable - restoreState = true - } - } - // Additional logic for system UI color changes - when (it.route) { - NavigationItem.Add.route -> { - systemUiController.setSystemBarsColor(color = Color.Black) - } - - NavigationItem.Message.route -> { - systemUiController.setSystemBarsColor(color = Color.Black) - } - - else -> { - systemUiController.setSystemBarsColor(color = Color.Transparent) - } - } - }, - colors = NavigationBarItemColors( - selectedTextColor = Color.Red, - selectedIndicatorColor = Color.Black, - unselectedTextColor = Color.Red, - disabledIconColor = Color.Red, - disabledTextColor = Color.Red, - selectedIconColor = iconTint, - unselectedIconColor = iconTint, - ), - icon = { - Icon( - modifier = Modifier.size(24.dp), - imageVector = if (currentRoute == it.route) it.selectedIcon() else it.icon(), - contentDescription = null, - tint = iconTint - ) - } - ) - } - - } - } - ) { innerPadding -> - Box( - modifier = Modifier.padding(innerPadding) - ) { - content() - } - } -} - diff --git a/app/src/main/java/com/aiosman/riderpro/MainActivityLifecycle.kt b/app/src/main/java/com/aiosman/riderpro/MainActivityLifecycle.kt new file mode 100644 index 0000000..b98ebaf --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/MainActivityLifecycle.kt @@ -0,0 +1,18 @@ +package com.aiosman.riderpro + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner + +object MainActivityLifecycle { + var isForeground = false +} + +class MainActivityLifecycleObserver : DefaultLifecycleObserver { + override fun onStart(owner: LifecycleOwner) { + MainActivityLifecycle.isForeground = true + } + + override fun onPause(owner: LifecycleOwner) { + MainActivityLifecycle.isForeground = false + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/Messaging.kt b/app/src/main/java/com/aiosman/riderpro/Messaging.kt new file mode 100644 index 0000000..c514452 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/Messaging.kt @@ -0,0 +1,30 @@ +package com.aiosman.riderpro + +import android.util.Log +import com.aiosman.riderpro.data.AccountService +import com.aiosman.riderpro.data.AccountServiceImpl +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.messaging.FirebaseMessaging +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +object Messaging { + fun InitFCM(scope: CoroutineScope) { + val accountService: AccountService = AccountServiceImpl() + FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> + if (!task.isSuccessful) { + Log.w("Pushing", "Fetching FCM registration token failed", task.exception) + return@OnCompleteListener + } + + // Get new FCM registration token + val token = task.result + + // Log and toast + Log.d("Pushing", token) + scope.launch { + accountService.registerMessageChannel(client = "fcm", token) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/MyFirebaseMessagingService.kt b/app/src/main/java/com/aiosman/riderpro/MyFirebaseMessagingService.kt new file mode 100644 index 0000000..5ea51d6 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/MyFirebaseMessagingService.kt @@ -0,0 +1,95 @@ +package com.aiosman.riderpro + +import android.Manifest +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.util.Log +import androidx.compose.material.Icon +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage + +val MessageTypeLike = "like" +fun showLikeNotification(context: Context, title: String, message: String, postId: Int) { + val channelId = ConstVars.MOMENT_LIKE_CHANNEL_ID +// Create an Intent to open the specific activity + val intent = Intent(context, MainActivity::class.java).apply { + putExtra("POST_ID", postId.toString()) + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + + // Create a PendingIntent to wrap the Intent + val pendingIntent = PendingIntent.getActivity( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val notificationBuilder = NotificationCompat.Builder(context, channelId) + .setSmallIcon(R.drawable.rider_pro_favoriate) + .setContentTitle(title) + .setContentText(message) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + + + with(NotificationManagerCompat.from(context)) { + if (ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + // 用户没有授权,不显示通知 + return + } + notify(System.currentTimeMillis().toInt(), notificationBuilder.build()) + } +} + +class MyFirebaseMessagingService : FirebaseMessagingService() { + override fun onNewToken(token: String) { + Log.d("Pushing", "Refreshed token: $token") + super.onNewToken(token) + } + + override fun onMessageReceived(remoteMessage: RemoteMessage) { + + if (remoteMessage.data.containsKey("action")) { + when (remoteMessage.data["action"]) { + MessageTypeLike -> { + val postId = remoteMessage.data["postId"]?.toInt() ?: return + showLikeNotification( + applicationContext, + remoteMessage.data["title"] ?: "FCM Message", + remoteMessage.data["body"] ?: "No message body", + postId + ) + } + + else -> { + Log.w("Pushing", "Unknown message type: ${remoteMessage.data["messageType"]}") + } + } + } + super.onMessageReceived(remoteMessage) + } + + fun prepareIntent(clickAction: String?): Intent { + val intent: Intent + val isAppInBackground: Boolean = !MainActivityLifecycle.isForeground + intent = if (isAppInBackground) { + Intent(this, MainActivity::class.java) + } else { + Intent(clickAction) + } + return intent + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt b/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt index 990df5f..78c272d 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt @@ -4,6 +4,7 @@ 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.RegisterMessageChannelRequestBody import com.aiosman.riderpro.data.api.RegisterRequestBody import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody import com.aiosman.riderpro.entity.AccountFavouriteEntity @@ -300,6 +301,8 @@ interface AccountService { * @param payload 通知信息 */ suspend fun updateNotice(payload: UpdateNoticeRequestBody) + + suspend fun registerMessageChannel(client:String,identifier:String) } class AccountServiceImpl : AccountService { @@ -400,4 +403,8 @@ class AccountServiceImpl : AccountService { ApiClient.api.updateNoticeInfo(payload) } + override suspend fun registerMessageChannel(client: String, identifier: String) { + ApiClient.api.registerMessageChannel(RegisterMessageChannelRequestBody(client,identifier)) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt index 2f2584a..d1f4eb8 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt @@ -79,6 +79,12 @@ data class UpdateNoticeRequestBody( val lastLookFavouriteTime: String? = null ) +data class RegisterMessageChannelRequestBody( + @SerializedName("client") + val client: String, + @SerializedName("identifier") + val identifier: String, +) interface RiderProAPI { @POST("register") suspend fun register(@Body body: RegisterRequestBody): Response @@ -211,6 +217,11 @@ interface RiderProAPI { @Body body: UpdateNoticeRequestBody ): Response + @POST("account/my/messaging") + suspend fun registerMessageChannel( + @Body body: RegisterMessageChannelRequestBody + ): Response + @GET("profile/{id}") suspend fun getAccountProfileById( diff --git a/app/src/main/java/com/aiosman/riderpro/exp/Date.kt b/app/src/main/java/com/aiosman/riderpro/exp/Date.kt index bdda052..4cfc624 100644 --- a/app/src/main/java/com/aiosman/riderpro/exp/Date.kt +++ b/app/src/main/java/com/aiosman/riderpro/exp/Date.kt @@ -46,4 +46,18 @@ fun Date.formatPostTime(): String { SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) } return dateFormat.format(this) +} + +/** + * YYYY.DD.MM HH:MM + */ +fun Date.formatPostTime2(): String { + val calendar = Calendar.getInstance() + calendar.time = this + val year = calendar.get(Calendar.YEAR) + val month = calendar.get(Calendar.MONTH) + 1 + val day = calendar.get(Calendar.DAY_OF_MONTH) + val hour = calendar.get(Calendar.HOUR_OF_DAY) + val minute = calendar.get(Calendar.MINUTE) + return "$year.$month.$day $hour:$minute" } \ No newline at end of file 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 9dd0595..9ba68a7 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/Navi.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp @@ -27,6 +28,7 @@ import com.aiosman.riderpro.LocalAnimatedContentScope import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalSharedTransitionScope import com.aiosman.riderpro.ui.account.AccountEditScreen +import com.aiosman.riderpro.ui.account.AccountEditScreen2 import com.aiosman.riderpro.ui.comment.CommentsScreen import com.aiosman.riderpro.ui.favourite.FavouriteScreen import com.aiosman.riderpro.ui.follower.FollowerScreen @@ -201,7 +203,7 @@ fun NavigationController( EmailSignupScreen() } composable(route = NavigationRoute.AccountEdit.route) { - AccountEditScreen() + AccountEditScreen2() } composable(route = NavigationRoute.ImageViewer.route) { CompositionLocalProvider( @@ -233,8 +235,11 @@ fun NavigationController( @OptIn(ExperimentalSharedTransitionApi::class) @Composable -fun Navigation(startDestination: String = NavigationRoute.Login.route) { +fun Navigation(startDestination: String = NavigationRoute.Login.route,onLaunch: (navController: NavHostController) -> Unit) { val navController = rememberNavController() + LaunchedEffect(Unit) { + onLaunch(navController) + } SharedTransitionLayout { CompositionLocalProvider( LocalNavController provides navController, diff --git a/app/src/main/java/com/aiosman/riderpro/ui/account/edit2.kt b/app/src/main/java/com/aiosman/riderpro/ui/account/edit2.kt new file mode 100644 index 0000000..fe2d1eb --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/ui/account/edit2.kt @@ -0,0 +1,316 @@ +package com.aiosman.riderpro.ui.account + +import android.app.Activity +import android.content.Intent +import android.net.Uri +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.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 +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicText +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar +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.rememberCoroutineScope +import androidx.compose.runtime.setValue +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.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.aiosman.riderpro.AppColors +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.UploadImage +import com.aiosman.riderpro.entity.AccountProfileEntity +import com.aiosman.riderpro.ui.comment.NoticeScreenHeader +import com.aiosman.riderpro.ui.composables.CustomAsyncImage +import com.aiosman.riderpro.ui.composables.StatusBarSpacer +import com.aiosman.riderpro.ui.modifiers.noRippleClickable +import com.aiosman.riderpro.ui.post.NewPostViewModel.uriToFile +import kotlinx.coroutines.launch + +/** + * 编辑用户资料界面 + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AccountEditScreen2() { + val accountService: AccountService = AccountServiceImpl() + var name by remember { mutableStateOf("") } + var bio by remember { mutableStateOf("") } + var imageUrl by remember { mutableStateOf(null) } + var bannerImageUrl by remember { mutableStateOf(null) } + var profile by remember { + mutableStateOf( + null + ) + } + val navController = LocalNavController.current + val scope = rememberCoroutineScope() + val context = LocalContext.current + + /** + * 加载用户资料 + */ + suspend fun reloadProfile() { + accountService.getMyAccountProfile().let { + profile = it + name = it.nickName + bio = it.bio + } + } + + fun updateUserProfile() { + scope.launch { + val newAvatar = imageUrl?.let { + val cursor = context.contentResolver.query(it, null, null, null, null) + var newAvatar: UploadImage? = null + cursor?.use { cur -> + if (cur.moveToFirst()) { + val displayName = cur.getString(cur.getColumnIndex("_display_name")) + val extension = displayName.substringAfterLast(".") + Log.d("NewPost", "File name: $displayName, extension: $extension") + // read as file + val file = uriToFile(context, it) + Log.d("NewPost", "File size: ${file.length()}") + newAvatar = UploadImage(file, displayName, it.toString(), extension) + } + } + newAvatar + } + var newBanner = bannerImageUrl?.let { + val cursor = context.contentResolver.query(it, null, null, null, null) + var newBanner: UploadImage? = null + cursor?.use { cur -> + if (cur.moveToFirst()) { + val displayName = cur.getString(cur.getColumnIndex("_display_name")) + val extension = displayName.substringAfterLast(".") + Log.d("NewPost", "File name: $displayName, extension: $extension") + // read as file + val file = uriToFile(context, it) + Log.d("NewPost", "File size: ${file.length()}") + newBanner = UploadImage(file, displayName, it.toString(), extension) + } + } + newBanner + } + val newName = if (name == profile?.nickName) null else name + accountService.updateProfile( + avatar = newAvatar, + banner = newBanner, + nickName = newName, + bio = bio + ) + reloadProfile() + navController.popBackStack() + } + } + + val pickImageLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val uri = result.data?.data + uri?.let { + imageUrl = it + } + } + } + val pickBannerImageLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val uri = result.data?.data + uri?.let { + bannerImageUrl = uri + } + } + } + + LaunchedEffect(Unit) { + reloadProfile() + } + Column( + modifier = Modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + StatusBarSpacer() + Box( + modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp) + ) { + NoticeScreenHeader( + title = "编辑资料", + moreIcon = false + ) { + + Icon( + modifier = Modifier + .size(24.dp) + .noRippleClickable { + updateUserProfile() + }, + imageVector = Icons.Default.Check, + contentDescription = "保存" + ) + } + } + Spacer(modifier = Modifier.height(32.dp)) + profile?.let { + Box( + modifier = Modifier.size(width = 112.dp, height = 112.dp), + contentAlignment = Alignment.Center + ) { + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.avatar_bold), contentDescription = "" + ) + CustomAsyncImage( + context, + imageUrl?.toString() ?: it.avatar, + modifier = Modifier + .size(width = 88.dp, height = 88.dp) + .clip( + RoundedCornerShape(88.dp) + ), + contentDescription = "", + contentScale = ContentScale.Crop + ) + Box( + modifier = Modifier + .size(32.dp) + .clip(CircleShape) + .background(AppColors.mainColor) + .align(Alignment.BottomEnd).noRippleClickable { + Intent(Intent.ACTION_PICK).apply { + type = "image/*" + pickImageLauncher.launch(this) + } + } + , + contentAlignment = Alignment.Center + ) { + Icon( + Icons.Default.Add, + contentDescription = "Add", + tint = Color.White, + ) + } + + } + Spacer(modifier = Modifier.height(46.dp)) + Column( + modifier = Modifier.padding(horizontal = 24.dp).border( + width = 1.dp, + color = Color(0xFFEBEBEB), + ) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Name", + modifier = Modifier + .widthIn(100.dp), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF333333) + ) + ) + BasicTextField( + maxLines = 1, + value = name, + onValueChange = { + name = it + }, + textStyle = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Normal + ), + modifier = Modifier + .weight(1f) + .padding(start = 16.dp) + ) + } + Box( + modifier = Modifier + .fillMaxWidth() + .height(1.dp) + .padding(horizontal = 16.dp) + .background(Color(0xFFEBEBEB)) + ) + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Bio", + modifier = Modifier + .widthIn(100.dp), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF333333) + ) + ) + BasicTextField( + value = bio, + onValueChange = { + bio = it + }, + textStyle = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Normal + ), + modifier = Modifier + .weight(1f) + .padding(start = 16.dp) + ) + } + } + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentsScreen.kt b/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentsScreen.kt index 8e763c6..cc5c3e9 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentsScreen.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentsScreen.kt @@ -63,7 +63,8 @@ fun CommentsScreen() { @Composable fun NoticeScreenHeader( title:String, - moreIcon: Boolean = true + moreIcon: Boolean = true, + rightIcon: @Composable (() -> Unit)? = null ) { val nav = LocalNavController.current Row( @@ -90,8 +91,10 @@ fun NoticeScreenHeader( modifier = Modifier.size(24.dp) ) } - - + if (rightIcon != null) { + Spacer(modifier = Modifier.weight(1f)) + rightIcon() + } } } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt index 45446e0..b8a6c34 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.annotation.DrawableRes import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.core.Animatable +import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -17,6 +18,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -24,6 +26,7 @@ import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope @@ -32,6 +35,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -44,13 +48,17 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -64,6 +72,7 @@ import com.aiosman.riderpro.R import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.exp.formatPostTime import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.exp.formatPostTime2 import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout @@ -86,7 +95,7 @@ fun ProfilePage() { val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues() LazyColumn( modifier = Modifier - .fillMaxSize().padding(bottom = with(LocalDensity.current) { + .fillMaxSize().background(Color(0xFFF5F5F5)).padding(bottom = with(LocalDensity.current) { val da = WindowInsets.navigationBars.getBottom(this).toDp() + 48.dp da }) @@ -133,53 +142,71 @@ fun ProfilePage() { contentDescription = "", modifier = Modifier.noRippleClickable { expanded = true - } + }, + tint = Color.White ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - DropdownMenuItem(onClick = { - scope.launch { - model.logout() - navController.navigate(NavigationRoute.Login.route) { - popUpTo(NavigationRoute.Index.route) { - inclusive = true - } + MaterialTheme(shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(16.dp))) { + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.width(250.dp).background(Color.White) + ) { + Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) { + Row { + Text("Logout", fontWeight = FontWeight.W500) + Spacer(modifier = Modifier.weight(1f)) + Icon( + painter = painterResource(id = R.mipmap.rider_pro_logout), + contentDescription = "", + modifier = Modifier.size(24.dp).noRippleClickable { + scope.launch { + model.logout() + navController.navigate(NavigationRoute.Login.route) { + popUpTo(NavigationRoute.Index.route) { + inclusive = true + } + } + } + } + ) } } - }, text = { - Text("Logout") - }) - DropdownMenuItem(onClick = { - scope.launch { - navController.navigate(NavigationRoute.AccountEdit.route) + Box(modifier = Modifier.padding(vertical = 14.dp, horizontal = 24.dp)) { + Row { + Text("Change password",fontWeight = FontWeight.W500) + Spacer(modifier = Modifier.weight(1f)) + Icon( + painter = painterResource(id = R.mipmap.rider_pro_change_password), + contentDescription = "", + modifier = Modifier.size(24.dp).noRippleClickable { + scope.launch { + navController.navigate(NavigationRoute.ChangePasswordScreen.route) + } + } + ) + } } - }, text = { - Text("Edit") - }) - DropdownMenuItem(onClick = { - scope.launch { - navController.navigate(NavigationRoute.ChangePasswordScreen.route) - } - }, text = { - Text("Change password") - }) + } } } } -// CarGroup() - + Spacer(modifier = Modifier.height(32.dp)) model.profile?.let { - UserInformation(accountProfileEntity = it) + UserInformation( + accountProfileEntity = it, + onEditProfileClick = { + navController.navigate(NavigationRoute.AccountEdit.route) + } + ) } -// RidingStyle() } items(moments.itemCount) { idx -> val momentItem = moments[idx] ?: return@items MomentPostUnit(momentItem) -// MomentCard(momentItem) + } + item { + Spacer(modifier = Modifier.height(48.dp)) } @@ -239,7 +266,8 @@ fun CarTopPicture() { fun UserInformation( isSelf: Boolean = true, accountProfileEntity: AccountProfileEntity, - onFollowClick: () -> Unit = {} + onFollowClick: () -> Unit = {}, + onEditProfileClick: () -> Unit = {} ) { Column( modifier = Modifier @@ -257,7 +285,8 @@ fun UserInformation( CommunicationOperatorGroup( isSelf = isSelf, isFollowing = accountProfileEntity.isFollowing, - onFollowClick = onFollowClick + onFollowClick = onFollowClick, + onEditProfileClick = onEditProfileClick ) } } @@ -270,20 +299,26 @@ fun UserInformationFollowers(modifier: Modifier, accountProfileEntity: AccountPr text = accountProfileEntity.followerCount.toString(), fontSize = 24.sp, color = Color.Black, - style = TextStyle(fontWeight = FontWeight.Bold) + style = TextStyle(fontStyle = FontStyle.Italic, fontWeight = FontWeight.Bold) ) - Spacer( + Canvas( modifier = Modifier .size(width = 88.83.dp, height = 1.dp) - .border(width = 1.dp, color = Color.Gray) - .padding(top = 5.dp, bottom = 5.dp) - ) + .padding(top = 2.dp, bottom = 5.dp) + ) { + drawLine( + color = Color(0xFFCCCCCC), + start = Offset(0f, 0f), + end = Offset(size.width, 0f), + strokeWidth = 1f, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f) + ) + } Text( modifier = Modifier.padding(top = 5.dp), text = stringResource(R.string.followers_upper), fontSize = 12.sp, color = Color.Black, - style = TextStyle(fontWeight = FontWeight.Bold) ) } } @@ -331,12 +366,6 @@ fun UserInformationBasic(modifier: Modifier, accountProfileEntity: AccountProfil fontSize = 12.sp, color = Color.Gray ) -// Text( -// modifier = Modifier.padding(top = 4.dp), -// text = "Member since Jun 4.2019", -// fontSize = 12.sp, -// color = Color.Gray -// ) } } @@ -351,14 +380,21 @@ fun UserInformationFollowing(modifier: Modifier, accountProfileEntity: AccountPr text = accountProfileEntity.followingCount.toString(), fontSize = 24.sp, color = Color.Black, - style = TextStyle(fontWeight = FontWeight.Bold) + style = TextStyle(fontStyle = FontStyle.Italic, fontWeight = FontWeight.Bold) ) - Box( + Canvas( modifier = Modifier .size(width = 88.83.dp, height = 1.dp) - .border(width = 1.dp, color = Color.Gray) - - ) + .padding(top = 2.dp, bottom = 5.dp) + ) { + drawLine( + color = Color(0xFFCCCCCC), + start = Offset(0f, 0f), + end = Offset(size.width, 0f), + strokeWidth = 1f, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f) + ) + } Text( modifier = Modifier.padding(top = 5.dp), text = stringResource(R.string.following_upper), @@ -384,7 +420,8 @@ fun UserInformationSlogan(accountProfileEntity: AccountProfileEntity) { fun CommunicationOperatorGroup( isSelf: Boolean = true, isFollowing: Boolean = false, - onFollowClick: () -> Unit + onFollowClick: () -> Unit = {}, + onEditProfileClick: () -> Unit = {} ) { val navController = LocalNavController.current Row( @@ -415,6 +452,30 @@ fun CommunicationOperatorGroup( } } + if (isSelf) { + Box( + modifier = Modifier + .size(width = 142.dp, height = 40.dp) + .noRippleClickable { + onEditProfileClick() + }, + contentAlignment = Alignment.Center + ) { + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.mipmap.rider_pro_btn_bg_grey), + contentDescription = "" + ) + Text( + text = "Edit profile", + fontSize = 14.sp, + color = Color.Black, + fontWeight = FontWeight.Bold, + style = TextStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic) + ) + } + } + } } @@ -484,7 +545,7 @@ fun RidingStyleItem(styleContent: String) { @Composable fun MomentPostUnit(momentEntity: MomentEntity) { - TimeGroup(momentEntity.time.formatPostTime()) + TimeGroup(momentEntity.time.formatPostTime2()) ProfileMomentCard( momentEntity.momentTextContent, momentEntity.images[0].thumbnail, @@ -504,13 +565,14 @@ fun TimeGroup(time: String = "2024.06.08 12:23") { verticalAlignment = Alignment.CenterVertically ) { Image( - modifier = Modifier.padding(end = 12.dp), + modifier = Modifier.height(16.dp).width(14.dp), painter = painterResource(id = R.drawable.rider_pro_moment_time_flag), contentDescription = "" ) + Spacer(modifier = Modifier.width(12.dp)) Text( text = time, - fontSize = 22.sp, + fontSize = 16.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold) ) @@ -525,15 +587,46 @@ fun ProfileMomentCard( comment: String, momentEntity: MomentEntity ) { + var columnHeight by remember { mutableStateOf(0) } + Column( modifier = Modifier .fillMaxWidth() - .padding(start = 48.dp, top = 18.dp, end = 24.dp) - .border(width = 1.dp, color = Color(0f, 0f, 0f, 0.1f), shape = RoundedCornerShape(6.dp)) + .padding(start = 24.dp, top = 18.dp, end = 24.dp) ) { - MomentCardTopContent(content) - MomentCardPicture(imageUrl, momentEntity = momentEntity) - MomentCardOperation(like, comment) + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Canvas( + modifier = Modifier + .height(with(LocalDensity.current) { columnHeight.toDp() }) + .width(14.dp) + ) { + drawLine( + color = Color(0xff899DA9), + start = Offset(0f, 0f), + end = Offset(0f, size.height), + strokeWidth = 4f, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f) + ) + } + Spacer(modifier = Modifier.width(10.dp)) + Column( + modifier = Modifier + .background(Color.White) + .weight(1f) + .onGloballyPositioned { coordinates -> + columnHeight = coordinates.size.height + } + ) { + if (content.isNotEmpty()) { + MomentCardTopContent(content) + } + MomentCardPicture(imageUrl, momentEntity = momentEntity) + MomentCardOperation(like, comment) + } + } } } @@ -561,7 +654,7 @@ fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) { imageUrl, modifier = Modifier .fillMaxSize() - .aspectRatio(1f) + .aspectRatio(3f/2f) .padding(16.dp) .noRippleClickable { PostViewModel.preTransit(momentEntity) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/login/emailsignup.kt b/app/src/main/java/com/aiosman/riderpro/ui/login/emailsignup.kt index 1e5d0de..2a20dd4 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/login/emailsignup.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/login/emailsignup.kt @@ -26,6 +26,7 @@ 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.Messaging import com.aiosman.riderpro.R import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.ServiceException @@ -101,6 +102,7 @@ fun EmailSignupScreen() { // 获取token 信息 try { accountService.getMyAccount() + Messaging.InitFCM(scope) } catch (e: ServiceException) { scope.launch(Dispatchers.Main) { Toast.makeText(context, "Failed to get account", Toast.LENGTH_SHORT).show() 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 ba1d299..a463ee5 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 @@ -36,6 +36,7 @@ import androidx.credentials.GetCredentialRequest import androidx.credentials.GetCredentialResponse import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.LocalNavController +import com.aiosman.riderpro.Messaging import com.aiosman.riderpro.R import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountServiceImpl @@ -87,6 +88,7 @@ fun SignupScreen() { // 获取token 信息 try { accountService.getMyAccount() + Messaging.InitFCM(coroutineScope) } catch (e: ServiceException) { coroutineScope.launch(Dispatchers.Main) { Toast.makeText(context, "Failed to get account", Toast.LENGTH_SHORT) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt b/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt index 631ec04..c972e5c 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/login/userauth.kt @@ -1,7 +1,5 @@ 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 @@ -21,8 +19,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Checkbox import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -42,28 +38,19 @@ 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 androidx.credentials.exceptions.GetCredentialProviderConfigurationException -import androidx.credentials.exceptions.NoCredentialException import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.LocalNavController +import com.aiosman.riderpro.Messaging import com.aiosman.riderpro.R import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.ServiceException import com.aiosman.riderpro.data.AccountServiceImpl +import com.aiosman.riderpro.data.ServiceException 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.aiosman.riderpro.utils.GoogleLogin -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 @@ -103,6 +90,7 @@ fun UserAuthScreen() { this.rememberMe = rememberMe saveData() } + Messaging.InitFCM(scope) navController.navigate(NavigationRoute.Index.route) { popUpTo(NavigationRoute.Login.route) { inclusive = true } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d264b51..ac8b209 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,7 @@ lifecycleCommonJvm = "2.8.2" places = "3.3.0" googleid = "1.1.1" identityCredential = "20231002" +lifecycleProcess = "2.8.4" [libraries] accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" } @@ -53,6 +54,7 @@ androidx-lifecycle-common-jvm = { group = "androidx.lifecycle", name = "lifecycl 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" } +androidx-lifecycle-process = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "lifecycleProcess" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }