Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ android {
}

dependencies {
implementation ("com.github.franmontiel:PersistentCookieJar:v1.0.1")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
implementation("io.github.webrtc-sdk:android:125.6422.06.1")
implementation("androidx.compose.material:material-icons-extended")
Expand Down
171 changes: 171 additions & 0 deletions app/src/main/java/com/example/neurology_project_android/FormManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.example.neurology_project_android

import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import org.json.JSONArray
import org.json.JSONObject
import java.io.IOException

object FormManager {

fun submitFormToServer(form: NIHForm, client: OkHttpClient, onResult: (Boolean) -> Unit) {
val json = JSONObject().apply {
put("patientName", form.patientName)
put("DOB", form.dob)
put("formDate", form.date)
put("results", form.formData)
put("username", form.username)
}

val requestBody = RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
json.toString()
)

val request = Request.Builder()
.url("https://videochat-signaling-app.ue.r.appspot.com/key=peerjs/post")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("Action", "submitStrokeScale")
.build()

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
onResult(false)
}

override fun onResponse(call: Call, response: Response) {
onResult(response.isSuccessful)
}
})
}

suspend fun fetchFormsForUser(
username: String,
client: OkHttpClient
): List<NIHForm> = withContext(Dispatchers.IO) {
val forms = mutableListOf<NIHForm>()

val json = JSONObject().apply {
put("username", username)
}

val requestBody = RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
json.toString()
)

val request = Request.Builder()
.url("https://videochat-signaling-app.ue.r.appspot.com/key=peerjs/post")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("Action", "getUsersForms")
.build()

try {
val response = client.newCall(request).execute()
if (response.isSuccessful) {
val bodyString = response.body?.string()
val jsonArray = JSONArray(bodyString)

for (i in 0 until jsonArray.length()) {
val item = jsonArray.getJSONObject(i)
forms.add(
NIHForm(
id = item.getInt("id"),
patientName = item.getString("name"),
dob = item.getString("dob"),
date = item.getString("form_date"),
formData = item.getString("results"),
username = item.getString("username")
)
)
}
} else {
Log.e("FORM_MANAGER", "Server error: ${response.code}")
}
} catch (e: IOException) {
Log.e("FORM_MANAGER", "Network error: ${e.message}")
}

return@withContext forms
}

fun deleteForm(formId: Int, username: String, client: OkHttpClient, callback: (Boolean) -> Unit) {
val json = JSONObject().apply {
put("id", formId)
put("username", username)
}

val requestBody = RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
json.toString()
)

val request = Request.Builder()
.url("https://videochat-signaling-app.ue.r.appspot.com/key=peerjs/post")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("Action", "deleteForm")
.build()

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("DELETE", "Failed: ${e.message}")
callback(false)
}

override fun onResponse(call: Call, response: Response) {
Log.d("DELETE", "Response code: ${response.code}")
callback(response.isSuccessful)
}
})
}

fun updateForm(form: NIHForm, client: OkHttpClient, onComplete: (Boolean) -> Unit) {
val json = JSONObject().apply {
put("id", form.id)
put("patientName", form.patientName)
put("dob", form.dob)
put("formDate", form.date)
put("results", form.formData)
put("username", form.username)
}

val requestBody = RequestBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
json.toString()
)

val request = Request.Builder()
.url("https://videochat-signaling-app.ue.r.appspot.com/key=peerjs/post")
.post(requestBody)
.addHeader("Content-Type", "application/json")
.addHeader("Action", "updateForm")
.build()

client.newCall(request).enqueue(SimpleCallback("UPDATE", onComplete))
}

private fun SimpleCallback(tag: String, onComplete: (Boolean) -> Unit) = object : Callback {
override fun onFailure(call: okhttp3.Call, e: java.io.IOException) {
Log.e(tag, "Request failed: ${e.message}")
onComplete(false)
}

override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
if (response.isSuccessful) {
Log.d(tag, "Request successful")
onComplete(true)
} else {
Log.e(tag, "Server error: ${response.code}")
onComplete(false)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,32 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.tooling.preview.Preview

class ListNIHFormActivity : ComponentActivity() {
private var refreshTrigger by mutableStateOf(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ListNIHFormScreen()
ListNIHFormScreen(refreshTrigger)
}
}

override fun onResume() {
super.onResume()
refreshTrigger++
}
}

@Composable
fun ListNIHFormScreen() {
fun ListNIHFormScreen(refreshTrigger: Int) {
val context = LocalContext.current
val nihFormDao = NIHFormDatabase.getDatabase(context).nihFormDao()
val sessionManager = remember { SessionManager(context) }
val client = sessionManager.client
val username = sessionManager.fetchUsername() ?: "anonymous"

var savedForms by remember { mutableStateOf<List<NIHForm>>(emptyList()) }

LaunchedEffect(Unit) {
nihFormDao.getAllForms().collect { forms ->
savedForms = forms
}
LaunchedEffect(refreshTrigger) { // runs every time refreshTrigger changes
val fetchedForms = FormManager.fetchFormsForUser(username, sessionManager.client)
savedForms = fetchedForms
}

Column(
Expand Down Expand Up @@ -82,8 +89,14 @@ fun ListNIHFormScreen() {
SavedFormItem(
form = SavedForm(form.patientName, form.date),
onClick = {
val intent = Intent(context, SavedNIHFormActivity::class.java)
intent.putExtra("formId", form.id)
val intent = Intent(context, SavedNIHFormActivity::class.java).apply {
putExtra("formId", form.id)
putExtra("patientName", form.patientName)
putExtra("dob", form.dob)
putExtra("date", form.date)
putExtra("formData", form.formData)
putExtra("username", form.username)
}
context.startActivity(intent)
}
)
Expand Down Expand Up @@ -129,9 +142,3 @@ fun SavedFormItem(form: SavedForm, onClick: () -> Unit) {
}

data class SavedForm(val patientName: String, val dateRecorded: String)

@Preview(showBackground = true)
@Composable
fun ListNIHFormScreenPreview() {
ListNIHFormScreen()
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fun LoginScreen(sessionManager: SessionManager, onLoginSuccess: () -> Unit) {
var password by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf<String?>(null) }
val client = sessionManager.client

GradientBackground {
Box(
Expand Down Expand Up @@ -118,7 +119,6 @@ fun LoginScreen(sessionManager: SessionManager, onLoginSuccess: () -> Unit) {
isLoading = true
error = null

val client = OkHttpClient()
val json = """
{
"username": "$username",
Expand All @@ -141,23 +141,28 @@ fun LoginScreen(sessionManager: SessionManager, onLoginSuccess: () -> Unit) {

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
isLoading = false
error = "Network error: ${e.message}"
(context as ComponentActivity).runOnUiThread {
isLoading = false
error = "Network error: ${e.message}"
}
}

override fun onResponse(call: Call, response: Response) {
isLoading = false
if (response.isSuccessful) {
val token = response.body?.string()?.trim() ?: ""
var authToken = response.headers.value(9).substringAfter("authorization=")
authToken = authToken.substringBefore(";")
val success = response.isSuccessful
val bodyString = response.body?.string()?.trim() ?: ""
var authToken = response.headers.value(9)?.substringAfter("authorization=")?.substringBefore(";") ?: ""

sessionManager.saveAuthToken(authToken.toString(), username)
if (success) {
sessionManager.saveAuthToken(authToken, username)
(context as ComponentActivity).runOnUiThread {
isLoading = false
onLoginSuccess()
}
} else {
error = "Login failed: ${response.code}"
(context as ComponentActivity).runOnUiThread {
isLoading = false
error = "Login failed: ${response.code}"
}
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -198,6 +201,8 @@ class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
fun HomeScreen(modifier: Modifier = Modifier, peerId: String, peers: List<String>) {
val context = LocalContext.current
val sessionManager = remember { SessionManager(context) }
// Refresh UI every 3 seconds
LaunchedEffect(peers) {
// This will trigger recomposition whenever peers update
Expand All @@ -209,6 +214,25 @@ class MainActivity : ComponentActivity() {
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(
onClick = {
sessionManager.logout()
val intent = Intent(context, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
context.startActivity(intent)
}
) {
Text("Log Out", color = MaterialTheme.colorScheme.primary)
}
}

Spacer(modifier = Modifier.height(8.dp))

PeerIdSection(peerId) // Displays the correct Peer ID

Column(
Expand Down

This file was deleted.

Loading
Loading