Skip to content

Commit dcc4914

Browse files
committed
[FEAT] WhiteListActivity, adding and removing from Whitelist
1 parent 3ece78b commit dcc4914

6 files changed

Lines changed: 262 additions & 20 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
</activity>
2828
<activity android:name=".CreateProfileActivity" />
2929
<activity android:name=".UserActivity" />
30+
<activity
31+
android:name=".WhitelistActivity"
32+
android:label="Authority Whitelist" />
3033
<service
3134
android:name=".MyHostApduService"
3235
android:exported="true"

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,21 +183,17 @@ fun register(
183183
// Switch back to the main thread to update UI or navigate
184184
withContext(Dispatchers.Main) {
185185
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: ")
186+
onSuccess = { (id, whitelist) ->
187+
Toast.makeText(context, "Profile created successfully!", Toast.LENGTH_SHORT).show()
193188

194189
SessionManager.saveSession(context, id, username)
195190

191+
SessionManager.saveWhitelist(context, whitelist)
192+
196193
val intent = Intent(context, UserActivity::class.java)
197194
context.startActivity(intent)
198195
},
199196
onFailure = { error ->
200-
// This block runs if the network call failed for any reason.
201197
Toast.makeText(context, error.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
202198
}
203199
)

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

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.client.HttpClient
1+
package com.example.blockchainaccess
2+
23
import io.ktor.client.*
34
import io.ktor.client.call.*
45
import io.ktor.client.engine.cio.* // Use CIO for a lightweight client
@@ -29,15 +30,57 @@ object NetworkClient {
2930

3031
@Serializable
3132
private data class CreateProfileResponse(
32-
val id: String
33+
val id: String,
34+
val whitelist: Set<String>
3335
)
3436

3537
@Serializable
3638
private data class UpdateUserRequest(
3739
val id: String
3840
)
3941

40-
suspend fun createAndRegisterProfile(username: String, password: String): Result<String> {
42+
@Serializable
43+
private data class WhitelistEntryRequest(
44+
val userId: String,
45+
)
46+
47+
suspend fun addWhitelistEntry(userId: String): Result<Unit> {
48+
return try {
49+
val url = "http://10.0.2.2:8081/new_whitelist_entry"
50+
val response = client.post(url) {
51+
contentType(ContentType.Application.Json)
52+
setBody(WhitelistEntryRequest(userId))
53+
}
54+
55+
if (response.status.isSuccess()) {
56+
Result.success(Unit)
57+
} else {
58+
Result.failure(Exception("Failed to add whitelist entry with status: ${response.status}"))
59+
}
60+
} catch (e: Exception) {
61+
Result.failure(Exception("Network error while adding whitelist entry: ${e.message}"))
62+
}
63+
}
64+
65+
suspend fun removeWhitelistEntry(userId: String): Result<Unit> {
66+
return try {
67+
val url = "http://10.0.2.2:8081/remove_whitelist_entry"
68+
val response = client.post(url) {
69+
contentType(ContentType.Application.Json)
70+
setBody(WhitelistEntryRequest(userId))
71+
}
72+
73+
if (response.status.isSuccess()) {
74+
Result.success(Unit)
75+
} else {
76+
Result.failure(Exception("Failed to remove whitelist entry with status: ${response.status}"))
77+
}
78+
} catch (e: Exception) {
79+
Result.failure(Exception("Network error while removing whitelist entry: ${e.message}"))
80+
}
81+
}
82+
83+
suspend fun createAndRegisterProfile(username: String, password: String): Result<Pair<String, Set<String>>> {
4184
try {
4285
val createProfileUrl = "http://10.0.2.2:8081/login"
4386
val response: HttpResponse = client.post(createProfileUrl) {
@@ -55,8 +98,10 @@ object NetworkClient {
5598
return Result.failure(Exception("Server returned an error"))
5699
}
57100

58-
val generatedId = creationResponse.id;
59-
// println("Profile created. ID: $generatedId")
101+
val generatedId = creationResponse.id
102+
val whitelist = creationResponse.whitelist
103+
104+
println("Profile created. ID: $generatedId")
60105
// --- Step 2: Register user ID with 127.0.0.1:8081 ---
61106
val registerUrl = "http://10.0.2.2:8081/app_save_id"
62107
val registerResponse: HttpResponse = client.post(registerUrl) {
@@ -69,7 +114,7 @@ object NetworkClient {
69114
}
70115

71116
println("User ID $generatedId registered successfully.")
72-
return Result.success("Profile created and registered with ID: $generatedId")
117+
return Result.success(Pair(generatedId, whitelist))
73118

74119
} catch (e: Exception) {
75120
// Handle network or serialization errors

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

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
package com.example.blockchainaccess
22

33
import android.content.Context
4-
import androidx.datastore.preferences.core.*
4+
import androidx.datastore.core.DataStore
5+
import androidx.datastore.preferences.core.Preferences
6+
import androidx.datastore.preferences.core.booleanPreferencesKey
7+
import androidx.datastore.preferences.core.edit
8+
import androidx.datastore.preferences.core.stringPreferencesKey
9+
import androidx.datastore.preferences.core.stringSetPreferencesKey
510
import androidx.datastore.preferences.preferencesDataStore
11+
import kotlinx.coroutines.flow.Flow
612
import kotlinx.coroutines.flow.first
713
import kotlinx.coroutines.flow.map
8-
9-
private val Context.dataStore by preferencesDataStore(name = "user_session")
10-
14+
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs")
1115
object SessionManager {
1216

1317
private val IS_LOGGED_IN = booleanPreferencesKey("is_logged_in")
1418
private val USER_ID = stringPreferencesKey("user_id")
1519
private val USERNAME = stringPreferencesKey("username")
1620

21+
private val WHITELIST = stringSetPreferencesKey("whitelist")
22+
1723
suspend fun saveSession(context: Context, userId: String, username: String) {
1824
context.dataStore.edit { prefs ->
1925
prefs[IS_LOGGED_IN] = true
@@ -39,4 +45,51 @@ object SessionManager {
3945
suspend fun getUsername(context: Context): String? {
4046
return context.dataStore.data.map { it[USERNAME] }.first()
4147
}
48+
49+
suspend fun addToWhitelist(context: Context, userId: String) {
50+
51+
val result = NetworkClient.addWhitelistEntry(userId)
52+
53+
result.fold(
54+
onSuccess = {
55+
context.dataStore.edit { prefs ->
56+
val currentWhitelist = prefs[WHITELIST] ?: emptySet()
57+
prefs[WHITELIST] = currentWhitelist + userId
58+
}
59+
},
60+
onFailure = { error ->
61+
println("Error adding to whitelist: ${error.message}")
62+
}
63+
)
64+
}
65+
66+
suspend fun removeFromWhitelist(context: Context, userId: String) {
67+
68+
val result = NetworkClient.removeWhitelistEntry(userId)
69+
70+
result.fold(
71+
onSuccess = {
72+
context.dataStore.edit { prefs ->
73+
val currentWhitelist = prefs[WHITELIST] ?: emptySet()
74+
prefs[WHITELIST] = currentWhitelist - userId
75+
}
76+
},
77+
onFailure = { error ->
78+
println("Error removing from whitelist: ${error.message}")
79+
}
80+
)
81+
}
82+
83+
suspend fun saveWhitelist(context: Context, whitelist: Set<String>) {
84+
context.dataStore.edit { prefs ->
85+
prefs[WHITELIST] = whitelist
86+
}
87+
}
88+
89+
90+
fun getWhitelist(context: Context): Flow<Set<String>> {
91+
return context.dataStore.data.map { prefs ->
92+
prefs[WHITELIST] ?: emptySet()
93+
}
94+
}
4295
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,14 @@ fun UserScreen(name: String, modifier: Modifier = Modifier) {
230230
)
231231
),
232232
shape = RoundedCornerShape(12.dp)
233-
),
233+
)
234+
.clickable {
235+
context.startActivity(
236+
Intent(context, WhitelistActivity::class.java)
237+
)
238+
},
234239
contentAlignment = Alignment.Center
235-
){
240+
) {
236241
Text(
237242
text = "Authority List",
238243
fontSize = 18.sp,
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.example.blockchainaccess
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
7+
import androidx.compose.foundation.Image
8+
import androidx.compose.foundation.background
9+
import androidx.compose.foundation.layout.*
10+
import androidx.compose.ui.platform.LocalContext
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.Scaffold
13+
import androidx.compose.material3.Text
14+
import androidx.compose.material3.Button
15+
import androidx.compose.material3.OutlinedTextField // Correct import for OutlinedTextField
16+
import androidx.compose.material3.Divider // Correct import for Divider
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.material.icons.Icons
19+
import androidx.compose.material.icons.filled.Delete
20+
import androidx.compose.material3.Icon
21+
import androidx.compose.material3.IconButton
22+
import androidx.compose.ui.unit.dp
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.runtime.*
26+
import kotlinx.coroutines.launch
27+
import com.example.blockchainaccess.ui.theme.BlockchainAccessTheme
28+
import androidx.compose.foundation.lazy.LazyColumn // Correct import for LazyColumn
29+
import androidx.compose.foundation.lazy.items // Correct import for LazyColumn items
30+
import androidx.compose.ui.graphics.Color
31+
import androidx.compose.ui.graphics.graphicsLayer
32+
import androidx.compose.ui.layout.ContentScale
33+
import androidx.compose.ui.res.painterResource
34+
35+
class WhitelistActivity : ComponentActivity() {
36+
override fun onCreate(savedInstanceState: Bundle?) {
37+
super.onCreate(savedInstanceState)
38+
enableEdgeToEdge()
39+
setContent {
40+
BlockchainAccessTheme {
41+
Scaffold(
42+
modifier = Modifier.fillMaxSize()
43+
) { innerPadding ->
44+
WhitelistScreen(
45+
modifier = Modifier.padding(innerPadding)
46+
)
47+
}
48+
}
49+
}
50+
}
51+
}
52+
53+
@Composable
54+
fun WhitelistScreen(modifier: Modifier = Modifier) {
55+
val context = LocalContext.current
56+
val scope = rememberCoroutineScope()
57+
58+
// Collect the whitelist Flow as state. The UI will automatically
59+
// recompose whenever the whitelist changes.
60+
val whitelist by SessionManager.getWhitelist(context).collectAsState(initial = emptySet())
61+
62+
// State for the text field
63+
var newId by remember { mutableStateOf("") }
64+
65+
Column(
66+
modifier = modifier
67+
.fillMaxSize()
68+
.padding(16.dp),
69+
horizontalAlignment = Alignment.CenterHorizontally
70+
) {
71+
// Input section to add new addresses
72+
Row(
73+
modifier = Modifier.fillMaxWidth(),
74+
verticalAlignment = Alignment.CenterVertically
75+
) {
76+
OutlinedTextField(
77+
value = newId,
78+
onValueChange = { newId = it },
79+
label = { Text("New ID") },
80+
modifier = Modifier.weight(1f),
81+
singleLine = true
82+
)
83+
Spacer(modifier = Modifier.width(8.dp))
84+
Button(onClick = {
85+
if (newId.isNotBlank()) {
86+
scope.launch {
87+
SessionManager.addToWhitelist(context, newId)
88+
newId = "" // Clear the text field
89+
}
90+
}
91+
}) {
92+
Text("Add")
93+
}
94+
}
95+
96+
Spacer(modifier = Modifier.height(16.dp))
97+
98+
// List of whitelisted ids
99+
if (whitelist.isEmpty()) {
100+
Text("Whitelist is empty.")
101+
} else {
102+
LazyColumn(modifier = Modifier.fillMaxWidth()) {
103+
items(whitelist.toList()) { id ->
104+
WhitelistItem(
105+
id = id,
106+
onRemove = {
107+
scope.launch {
108+
SessionManager.removeFromWhitelist(context, id)
109+
}
110+
}
111+
)
112+
Divider()
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
@Composable
120+
fun WhitelistItem(id: String, onRemove: () -> Unit) {
121+
Row(
122+
modifier = Modifier
123+
.fillMaxWidth()
124+
.padding(vertical = 8.dp),
125+
verticalAlignment = Alignment.CenterVertically,
126+
horizontalArrangement = Arrangement.SpaceBetween
127+
) {
128+
Text(
129+
text = id,
130+
modifier = Modifier.weight(1f)
131+
)
132+
IconButton(onClick = onRemove) {
133+
Icon(
134+
imageVector = Icons.Default.Delete,
135+
contentDescription = "Remove ID",
136+
tint = MaterialTheme.colorScheme.error
137+
)
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)