This commit is contained in:
2024-08-13 22:32:27 +08:00
parent c99e168c53
commit d5067ca05b
9 changed files with 212 additions and 8 deletions

View File

@@ -2,5 +2,5 @@ package com.aiosman.riderpro
object ConstVars { object ConstVars {
// api 地址 // api 地址
const val BASE_SERVER = "https://api.rider-pro.com" const val BASE_SERVER = "https://8.137.22.101:8088"
} }

View File

@@ -53,7 +53,7 @@ class MainActivity : ComponentActivity() {
try { try {
val resp = accountService.getMyAccount() val resp = accountService.getMyAccount()
return true return true
} catch (e: ServiceException) { } catch (e: Exception) {
return false return false
} }
} }

View File

@@ -2,6 +2,7 @@ package com.aiosman.riderpro.data
import com.aiosman.riderpro.AppStore 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.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.test.TestDatabase import com.aiosman.riderpro.test.TestDatabase
@@ -63,6 +64,7 @@ interface AccountService {
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 changeAccountPassword(oldPassword: String, newPassword: String)
} }
class TestAccountServiceImpl : AccountService { class TestAccountServiceImpl : AccountService {
@@ -120,4 +122,8 @@ class TestAccountServiceImpl : AccountService {
override suspend fun registerUserWithPassword(loginName: String, password: String) { override suspend fun registerUserWithPassword(loginName: String, password: String) {
ApiClient.api.register(RegisterRequestBody(loginName, password)) ApiClient.api.register(RegisterRequestBody(loginName, password))
} }
override suspend fun changeAccountPassword(oldPassword: String, newPassword: String) {
ApiClient.api.changePassword(ChangePasswordRequestBody(oldPassword, newPassword))
}
} }

View File

@@ -7,7 +7,39 @@ import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import java.security.cert.CertificateException
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
fun getUnsafeOkHttpClient(): OkHttpClient {
return try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = arrayOf()
})
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
.addInterceptor(AuthInterceptor())
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
class AuthInterceptor() : Interceptor { class AuthInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder() val requestBuilder = chain.request().newBuilder()
@@ -21,9 +53,7 @@ object ApiClient {
const val BASE_API_URL = "${BASE_SERVER}/api/v1" const val BASE_API_URL = "${BASE_SERVER}/api/v1"
const val RETROFIT_URL = "${BASE_API_URL}/" const val RETROFIT_URL = "${BASE_API_URL}/"
private val okHttpClient: OkHttpClient by lazy { private val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder() getUnsafeOkHttpClient()
.addInterceptor(AuthInterceptor())
.build()
} }
private val retrofit: Retrofit by lazy { private val retrofit: Retrofit by lazy {
Retrofit.Builder() Retrofit.Builder()

View File

@@ -52,6 +52,13 @@ data class CommentRequestBody(
val content: String val content: String
) )
data class ChangePasswordRequestBody(
@SerializedName("currentPassword")
val oldPassword: String = "",
@SerializedName("newPassword")
val newPassword: String = ""
)
interface RiderProAPI { interface RiderProAPI {
@POST("register") @POST("register")
suspend fun register(@Body body: RegisterRequestBody): Response<Unit> suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
@@ -136,6 +143,11 @@ interface RiderProAPI {
@Part("nickname") nickname: RequestBody?, @Part("nickname") nickname: RequestBody?,
): Response<Unit> ): Response<Unit>
@POST("account/my/password")
suspend fun changePassword(
@Body body: ChangePasswordRequestBody
): Response<Unit>
@GET("profile/{id}") @GET("profile/{id}")
suspend fun getAccountProfileById( suspend fun getAccountProfileById(
@Path("id") id: Int @Path("id") id: Int

View File

@@ -1,5 +1,6 @@
package com.aiosman.riderpro.ui package com.aiosman.riderpro.ui
import ChangePasswordScreen
import ImageViewer import ImageViewer
import ModificationListScreen import ModificationListScreen
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -64,6 +65,7 @@ sealed class NavigationRoute(
data object EmailSignUp : NavigationRoute("EmailSignUp") data object EmailSignUp : NavigationRoute("EmailSignUp")
data object AccountEdit : NavigationRoute("AccountEditScreen") data object AccountEdit : NavigationRoute("AccountEditScreen")
data object ImageViewer : NavigationRoute("ImageViewer") data object ImageViewer : NavigationRoute("ImageViewer")
data object ChangePasswordScreen : NavigationRoute("ChangePasswordScreen")
} }
@@ -184,6 +186,9 @@ fun NavigationController(
ImageViewer() ImageViewer()
} }
} }
composable(route = NavigationRoute.ChangePasswordScreen.route) {
ChangePasswordScreen()
}
} }

View File

@@ -0,0 +1,85 @@
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.TestAccountServiceImpl
import kotlinx.coroutines.launch
class ChangePasswordViewModel {
val accountService :AccountService = TestAccountServiceImpl()
suspend fun changePassword(currentPassword: String, newPassword: String) {
accountService.changeAccountPassword(currentPassword, newPassword)
}
}
@Composable
fun ChangePasswordScreen(
) {
val viewModel = remember { ChangePasswordViewModel() }
var currentPassword by remember { mutableStateOf("") }
var newPassword by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") }
var errorMessage by remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
Scaffold { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
verticalArrangement = Arrangement.Center
) {
TextField(
value = currentPassword,
onValueChange = { currentPassword = it },
label = { Text("Current Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = newPassword,
onValueChange = { newPassword = it },
label = { Text("New Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = confirmPassword,
onValueChange = { confirmPassword = it },
label = { Text("Confirm New Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
if (errorMessage.isNotEmpty()) {
Text(
text = errorMessage,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(bottom = 16.dp)
)
}
Button(
onClick = {
if (newPassword == confirmPassword) {
scope.launch {
viewModel.changePassword(currentPassword, newPassword)
navController.popBackStack()
}
} else {
errorMessage = "Passwords do not match"
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Change Password")
}
}
}
}

View File

@@ -76,7 +76,6 @@ fun ProfilePage() {
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
) { ) {
item { item {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -116,6 +115,13 @@ fun ProfilePage() {
}, text = { }, text = {
Text("Edit") Text("Edit")
}) })
DropdownMenuItem(onClick = {
scope.launch {
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
}
}, text = {
Text("Change password")
})
} }
} }

View File

@@ -0,0 +1,60 @@
package com.aiosman.riderpro.ui.splash
import android.window.SplashScreen
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
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.R
@Composable
fun SplashScreen() {
Scaffold {
it
Box(
modifier = Modifier.fillMaxSize()
) {
// to bottom
Box(
contentAlignment = Alignment.TopCenter,
modifier = Modifier.padding(top = 211.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Image(
painter = painterResource(id = R.mipmap.rider_pro_logo),
contentDescription = "Rider Pro",
modifier = Modifier
.width(108.dp)
.height(45.dp)
)
Spacer(modifier = Modifier.height(32.dp))
Text(
"Connecting Riders".uppercase(),
fontSize = 28.sp,
fontWeight = FontWeight.Bold
)
Text("Worldwide".uppercase(), fontSize = 28.sp, fontWeight = FontWeight.Bold)
}
}
}
}
}