Skip to content

Commit 3ece78b

Browse files
committed
[FEAT] Service for sending requests to VM, login, saveid
1 parent 150948e commit 3ece78b

8 files changed

Lines changed: 157 additions & 58 deletions

File tree

.idea/deviceManager.xml

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle.kts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ plugins {
22
alias(libs.plugins.android.application)
33
alias(libs.plugins.kotlin.android)
44
alias(libs.plugins.kotlin.compose)
5+
alias(libs.plugins.kotlin.serialization)
56
}
67

8+
kotlin {
9+
compilerOptions {
10+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
11+
}
12+
}
713
android {
814
namespace = "com.example.blockchainaccess"
915
compileSdk = 35
@@ -31,16 +37,19 @@ android {
3137
sourceCompatibility = JavaVersion.VERSION_11
3238
targetCompatibility = JavaVersion.VERSION_11
3339
}
34-
kotlinOptions {
35-
jvmTarget = "11"
36-
}
40+
3741
buildFeatures {
3842
compose = true
3943
}
4044
}
4145

4246
dependencies {
43-
47+
implementation("io.ktor:ktor-client-core:3.2.3")
48+
implementation("io.ktor:ktor-client-cio:3.2.3")
49+
implementation("io.ktor:ktor-client-content-negotiation:3.2.3")
50+
implementation("io.ktor:ktor-serialization-kotlinx-json:3.2.3")
51+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
52+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
4453
implementation(libs.androidx.core.ktx)
4554
implementation(libs.androidx.lifecycle.runtime.ktx)
4655
implementation(libs.androidx.activity.compose)
@@ -50,6 +59,7 @@ dependencies {
5059
implementation(libs.androidx.ui.tooling.preview)
5160
implementation(libs.androidx.material3)
5261
implementation("androidx.datastore:datastore-preferences:1.1.7")
62+
implementation(libs.firebase.crashlytics.buildtools)
5363
testImplementation(libs.junit)
5464
androidTestImplementation(libs.androidx.junit)
5565
androidTestImplementation(libs.androidx.espresso.core)

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44
<uses-permission android:name="android.permission.NFC" />
5+
<uses-permission android:name="android.permission.INTERNET" />
56
<uses-feature android:name="android.hardware.nfc.hce" android:required="true" />
67
<application
78
android:allowBackup="true"

app/src/main/java/com/example/blockchainaccess/CreatePofileActivity.kt

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.example.blockchainaccess
22

3+
import android.annotation.SuppressLint
34
import android.os.Bundle
45
import android.content.Intent
56
import androidx.activity.ComponentActivity
@@ -32,9 +33,7 @@ import androidx.compose.ui.tooling.preview.Preview
3233
import androidx.compose.ui.layout.ContentScale
3334
import androidx.compose.ui.res.painterResource
3435
import com.example.blockchainaccess.ui.theme.BlockchainAccessTheme
35-
import com.example.blockchainaccess.ProfileService
36-
import kotlinx.coroutines.CoroutineScope
37-
import kotlinx.coroutines.launch
36+
import kotlinx.coroutines.*
3837
import android.widget.Toast
3938

4039
class CreateProfileActivity : ComponentActivity() {
@@ -54,6 +53,7 @@ class CreateProfileActivity : ComponentActivity() {
5453
}
5554
}
5655

56+
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
5757
@Composable
5858
fun CreateProfileScreen(name: String, modifier: Modifier = Modifier) {
5959
val context = LocalContext.current
@@ -111,8 +111,8 @@ fun CreateProfileScreen(name: String, modifier: Modifier = Modifier) {
111111
onValueChange = { username = it },
112112
label = { Text("Username") },
113113
modifier = Modifier
114-
.fillMaxWidth()
115-
.padding(vertical = 8.dp)
114+
.fillMaxWidth(0.88f)
115+
.padding(vertical = 6.dp)
116116
)
117117

118118
TextField(
@@ -121,17 +121,17 @@ fun CreateProfileScreen(name: String, modifier: Modifier = Modifier) {
121121
label = { Text("Password") },
122122
visualTransformation = PasswordVisualTransformation(),
123123
modifier = Modifier
124-
.fillMaxWidth()
125-
.padding(vertical = 8.dp)
124+
.fillMaxWidth(0.88f)
125+
.padding(vertical = 6.dp)
126126
)
127127
TextField(
128128
value = repeatPassword,
129129
onValueChange = { repeatPassword = it },
130130
label = { Text("Repeat Password") },
131131
visualTransformation = PasswordVisualTransformation(),
132132
modifier = Modifier
133-
.fillMaxWidth()
134-
.padding(vertical = 8.dp)
133+
.fillMaxWidth(0.88f)
134+
.padding(vertical = 6.dp)
135135
)
136136
Spacer(modifier = Modifier.height(34.dp))
137137
Button(
@@ -165,22 +165,42 @@ fun register(
165165
repeatPassword: String,
166166
coroutineScope: CoroutineScope
167167
) {
168-
val result = ProfileService.createProfile(username, password, repeatPassword)
168+
if (username.isBlank() || password.isBlank() || repeatPassword.isBlank()) {
169+
Toast.makeText(context, "All fields must be filled.", Toast.LENGTH_SHORT).show()
170+
return
171+
}
169172

170-
result.fold(
171-
onSuccess = { message ->
172-
val id = message.substringAfter("ID: ").trim()
173-
Toast.makeText(context, "Success: $message", Toast.LENGTH_SHORT).show()
173+
if (password != repeatPassword) {
174+
Toast.makeText(context, "Passwords do not match.", Toast.LENGTH_SHORT).show()
175+
return
176+
}
174177

175-
coroutineScope.launch {
176-
SessionManager.saveSession(context, id, username)
177-
// Navigate to UserActivity after session is saved
178-
val intent = Intent(context, UserActivity::class.java)
179-
context.startActivity(intent)
180-
}
181-
},
182-
onFailure = { error ->
183-
Toast.makeText(context, error.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
178+
// Launch a coroutine to handle the asynchronous network request
179+
coroutineScope.launch(Dispatchers.IO) {
180+
// The network request is made here, on a background thread.
181+
val result = NetworkClient.createAndRegisterProfile(username, password)
182+
183+
// Switch back to the main thread to update UI or navigate
184+
withContext(Dispatchers.Main) {
185+
result.fold(
186+
onSuccess = { message ->
187+
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
188+
189+
// Here's where the network call has already completed successfully.
190+
// Now, we can safely save the session and navigate.
191+
// Note: The message from the network client contains the ID.
192+
val id = message.substringAfter("ID: ")
193+
194+
SessionManager.saveSession(context, id, username)
195+
196+
val intent = Intent(context, UserActivity::class.java)
197+
context.startActivity(intent)
198+
},
199+
onFailure = { error ->
200+
// This block runs if the network call failed for any reason.
201+
Toast.makeText(context, error.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
202+
}
203+
)
184204
}
185-
)
205+
}
186206
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.client.HttpClient
2+
import io.ktor.client.*
3+
import io.ktor.client.call.*
4+
import io.ktor.client.engine.cio.* // Use CIO for a lightweight client
5+
import io.ktor.client.plugins.contentnegotiation.*
6+
import io.ktor.client.request.*
7+
import io.ktor.client.statement.*
8+
import io.ktor.http.*
9+
import io.ktor.serialization.kotlinx.json.*
10+
import kotlinx.serialization.Serializable
11+
import kotlinx.serialization.json.Json
12+
13+
14+
object NetworkClient {
15+
16+
private val client = HttpClient(CIO) {
17+
install(ContentNegotiation) {
18+
json(Json {
19+
ignoreUnknownKeys = true
20+
})
21+
}
22+
}
23+
24+
@Serializable
25+
private data class CreateProfileRequest(
26+
val username: String,
27+
val password: String
28+
)
29+
30+
@Serializable
31+
private data class CreateProfileResponse(
32+
val id: String
33+
)
34+
35+
@Serializable
36+
private data class UpdateUserRequest(
37+
val id: String
38+
)
39+
40+
suspend fun createAndRegisterProfile(username: String, password: String): Result<String> {
41+
try {
42+
val createProfileUrl = "http://10.0.2.2:8081/login"
43+
val response: HttpResponse = client.post(createProfileUrl) {
44+
contentType(ContentType.Application.Json)
45+
setBody(CreateProfileRequest(username, password))
46+
}
47+
48+
if (response.status != HttpStatusCode.OK) {
49+
return Result.failure(Exception("Profile creation failed with status: ${response.status}"))
50+
}
51+
52+
val creationResponse = response.body<CreateProfileResponse>()
53+
54+
if (creationResponse.id == "wrong") {
55+
return Result.failure(Exception("Server returned an error"))
56+
}
57+
58+
val generatedId = creationResponse.id;
59+
// println("Profile created. ID: $generatedId")
60+
// --- Step 2: Register user ID with 127.0.0.1:8081 ---
61+
val registerUrl = "http://10.0.2.2:8081/app_save_id"
62+
val registerResponse: HttpResponse = client.post(registerUrl) {
63+
contentType(ContentType.Application.Json)
64+
setBody(UpdateUserRequest(generatedId))
65+
}
66+
67+
if (registerResponse.status != HttpStatusCode.OK) {
68+
return Result.failure(Exception("User ID registration failed with status: ${registerResponse.status}"))
69+
}
70+
71+
println("User ID $generatedId registered successfully.")
72+
return Result.success("Profile created and registered with ID: $generatedId")
73+
74+
} catch (e: Exception) {
75+
// Handle network or serialization errors
76+
return Result.failure(Exception("Network error: ${e.message}"))
77+
}
78+
}
79+
}

app/src/main/java/com/example/blockchainaccess/ProfileService.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.

gradle/libs.versions.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
[versions]
2-
agp = "8.10.1"
3-
kotlin = "2.0.21"
2+
agp = "8.12.0"
3+
kotlin = "2.2.0"
44
coreKtx = "1.16.0"
55
junit = "4.13.2"
66
junitVersion = "1.2.1"
77
espressoCore = "3.6.1"
88
lifecycleRuntimeKtx = "2.9.1"
99
activityCompose = "1.10.1"
1010
composeBom = "2024.09.00"
11+
firebaseCrashlyticsBuildtools = "3.0.5"
1112

1213
[libraries]
1314
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -24,9 +25,10 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
2425
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
2526
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
2627
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
28+
firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" }
2729

2830
[plugins]
2931
android-application = { id = "com.android.application", version.ref = "agp" }
3032
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
3133
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
32-
34+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Tue Jul 08 18:32:02 CEST 2025
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)