Skip to content

Commit 7e216f0

Browse files
Merge pull request #52 from a2i2/stuart/share-all
Added share button to parent event logs screens
2 parents 6be5aec + fdc8d71 commit 7e216f0

File tree

6 files changed

+159
-42
lines changed

6 files changed

+159
-42
lines changed

EEFRT Demo Android/app/src/main/java/ai/a2i2/conductor/effrtdemoandroid/ui/EefrtTrialDetailView.kt

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@ import androidx.compose.material3.Text
2020
import androidx.compose.material3.TopAppBar
2121
import androidx.compose.runtime.Composable
2222
import androidx.compose.ui.Modifier
23-
import androidx.compose.ui.platform.LocalClipboardManager
2423
import androidx.compose.ui.platform.LocalContext
2524
import android.content.Intent
2625
import androidx.compose.ui.res.painterResource
27-
import androidx.compose.ui.text.AnnotatedString
2826
import androidx.compose.ui.unit.dp
2927

3028
@OptIn(ExperimentalMaterial3Api::class)
@@ -35,16 +33,6 @@ fun EefrtTrialDetailView(
3533
) {
3634
val scrollViewState = rememberScrollState()
3735

38-
fun formatPracticeTrialString(): String {
39-
var practiceTrialString = eefrtTaskAttempt.toString()
40-
practiceTrialString = practiceTrialString
41-
.replace("(", "(\n ")
42-
.replace(",", "\n")
43-
.replace(")", "\n)")
44-
45-
return practiceTrialString
46-
}
47-
4836
Scaffold(
4937
topBar = {
5038
TopAppBar(
@@ -60,7 +48,7 @@ fun EefrtTrialDetailView(
6048
actions = {
6149
val context = LocalContext.current
6250
IconButton(onClick = {
63-
val shareText = formatPracticeTrialString()
51+
val shareText = EventLogsFormatter.formatTaskAttempt(eefrtTaskAttempt)
6452
val shareIntent = Intent().apply {
6553
action = Intent.ACTION_SEND
6654
putExtra(Intent.EXTRA_TEXT, shareText)
@@ -82,7 +70,7 @@ fun EefrtTrialDetailView(
8270
) {
8371
SelectionContainer {
8472
Text(
85-
text = formatPracticeTrialString(),
73+
text = EventLogsFormatter.formatTaskAttempt(eefrtTaskAttempt),
8674
modifier = Modifier
8775
.padding(horizontal = 16.dp)
8876
)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package ai.a2i2.conductor.effrtdemoandroid.ui
2+
3+
import ai.a2i2.conductor.effrtdemoandroid.ui.data.EefrtTaskAttempt
4+
5+
object EventLogsFormatter {
6+
7+
/**
8+
* Formats an EEFRT task attempt using the standard formatting logic
9+
* that replaces parentheses and commas with newlines for better readability
10+
*/
11+
fun formatTaskAttempt(taskAttempt: EefrtTaskAttempt): String {
12+
return taskAttempt.toString()
13+
.replace("(", "(\n ")
14+
.replace(",", "\n")
15+
.replace(")", "\n)")
16+
}
17+
18+
/**
19+
* Formats all event logs (practice and main trials) into a single sharable string
20+
*/
21+
fun formatAllEventLogs(
22+
practiceTaskAttempts: List<ai.a2i2.conductor.effrtdemoandroid.persistence.PracticeTaskAttempt>,
23+
actualTaskAttempts: List<ai.a2i2.conductor.effrtdemoandroid.persistence.TaskAttempt>
24+
): String {
25+
val stringBuilder = StringBuilder()
26+
27+
// Add practice trials section
28+
stringBuilder.append("=== PRACTICE TRIALS ===\n\n")
29+
practiceTaskAttempts.forEachIndexed { index, practiceTaskAttempt ->
30+
stringBuilder.append("Practice Trial ${index + 1}:\n")
31+
stringBuilder.append(formatTaskAttempt(practiceTaskAttempt))
32+
stringBuilder.append("\n\n")
33+
}
34+
35+
// Add actual trials section
36+
stringBuilder.append("=== MAIN TRIALS ===\n\n")
37+
actualTaskAttempts.forEachIndexed { index, taskAttempt ->
38+
stringBuilder.append("Main Trial ${index + 1}:\n")
39+
stringBuilder.append(formatTaskAttempt(taskAttempt))
40+
stringBuilder.append("\n\n")
41+
}
42+
43+
return stringBuilder.toString()
44+
}
45+
}

EEFRT Demo Android/app/src/main/java/ai/a2i2/conductor/effrtdemoandroid/ui/EventLogsView.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ai.a2i2.conductor.effrtdemoandroid.ui
33
import ai.a2i2.conductor.effrtdemoandroid.R
44
import ai.a2i2.conductor.effrtdemoandroid.ui.data.EefrtScreenViewModel
55
import android.annotation.SuppressLint
6+
import android.content.Intent
67
import android.os.Handler
78
import android.os.Looper
89
import androidx.compose.foundation.Image
@@ -16,10 +17,12 @@ import androidx.compose.foundation.layout.padding
1617
import androidx.compose.foundation.rememberScrollState
1718
import androidx.compose.foundation.verticalScroll
1819
import androidx.compose.material.icons.Icons
20+
import androidx.compose.material.icons.filled.Share
1921
import androidx.compose.material.icons.outlined.Delete
2022
import androidx.compose.material3.AlertDialog
2123
import androidx.compose.material3.Button
2224
import androidx.compose.material3.ExperimentalMaterial3Api
25+
import androidx.compose.material3.Icon
2326
import androidx.compose.material3.IconButton
2427
import androidx.compose.material3.Scaffold
2528
import androidx.compose.material3.Text
@@ -29,6 +32,7 @@ import androidx.compose.runtime.mutableStateOf
2932
import androidx.compose.runtime.remember
3033
import androidx.compose.ui.Modifier
3134
import androidx.compose.ui.graphics.Color
35+
import androidx.compose.ui.platform.LocalContext
3236
import androidx.compose.ui.res.painterResource
3337
import androidx.compose.ui.text.font.FontWeight
3438
import androidx.compose.ui.unit.dp
@@ -46,6 +50,7 @@ fun EventLogsView(
4650
val actualTaskAttempts = remember { eefrtScreenViewModel.getActualTaskAttempts() }
4751
val shouldShowDialog = remember { mutableStateOf(false) }
4852
val scrollState = rememberScrollState()
53+
val context = LocalContext.current
4954

5055
Scaffold(
5156
topBar = {
@@ -66,6 +71,7 @@ fun EventLogsView(
6671
actions = {
6772
Spacer(modifier = Modifier.weight(1f))
6873

74+
// Delete all button
6975
IconButton(
7076
onClick = {
7177
shouldShowDialog.value = true
@@ -79,6 +85,27 @@ fun EventLogsView(
7985
.background(Color.Transparent)
8086
)
8187
}
88+
89+
// Share button
90+
IconButton(
91+
onClick = {
92+
val shareText = EventLogsFormatter.formatAllEventLogs(
93+
practiceTaskAttempts.value,
94+
actualTaskAttempts.value
95+
)
96+
val shareIntent = Intent().apply {
97+
action = Intent.ACTION_SEND
98+
putExtra(Intent.EXTRA_TEXT, shareText)
99+
type = "text/plain"
100+
}
101+
context.startActivity(Intent.createChooser(shareIntent, "Share Event Logs"))
102+
}
103+
) {
104+
Icon(
105+
imageVector = Icons.Filled.Share,
106+
contentDescription = "Share Event Logs"
107+
)
108+
}
82109
}
83110
)
84111
},

EEFRT Demo iOS/EEFRT Demo/EventLogsView.swift

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ struct EventLogsView: View {
99
@Environment(\.modelContext) private var modelContext
1010

1111
@State private var shouldShowAlert = false
12+
@State private var isSharePresented = false
1213

1314
var body: some View {
1415
List {
@@ -17,12 +18,7 @@ struct EventLogsView: View {
1718
VStack {
1819
NavigationLink(
1920
destination: {
20-
do {
21-
let jsonString = try JsonHelpers.stringify(result)
22-
return TaskResultDetailsView(jsonString: jsonString)
23-
} catch {
24-
fatalError("Couldn't stringifiy event log we already stringified...")
25-
}
21+
TaskResultDetailsView(taskResult: result)
2622
},
2723
label: {
2824
Text(result.createdAt?.description ?? "Something went wrong")
@@ -38,12 +34,7 @@ struct EventLogsView: View {
3834
VStack {
3935
NavigationLink(
4036
destination: {
41-
do {
42-
let jsonString = try JsonHelpers.stringify(result)
43-
return TaskResultDetailsView(jsonString: jsonString)
44-
} catch {
45-
fatalError("Couldn't stringifiy event log we already stringified...")
46-
}
37+
TaskResultDetailsView(taskResult: result)
4738
},
4839
label: {
4940
Text(result.createdAt?.description ?? "Something went wrong")
@@ -56,10 +47,18 @@ struct EventLogsView: View {
5647
}
5748
.toolbar {
5849
ToolbarItem(placement: .topBarTrailing) {
59-
Button {
60-
shouldShowAlert = true
61-
} label: {
62-
Image(systemName: "trash")
50+
HStack {
51+
Button {
52+
isSharePresented = true
53+
} label: {
54+
Image(systemName: "square.and.arrow.up")
55+
}
56+
57+
Button {
58+
shouldShowAlert = true
59+
} label: {
60+
Image(systemName: "trash")
61+
}
6362
}
6463
}
6564
}
@@ -73,6 +72,13 @@ struct EventLogsView: View {
7372
secondaryButton: .cancel {}
7473
)
7574
}
75+
.sheet(isPresented: $isSharePresented) {
76+
let shareText = EventLogsFormatter.formatAllEventLogs(
77+
practiceTaskResults: practiceTaskResults,
78+
taskResults: taskResults
79+
)
80+
ActivityView(activityItems: [shareText])
81+
}
7682
}
7783

7884
private func deletePracticeTaskResult(at offsets: IndexSet) {
@@ -122,3 +128,11 @@ struct EventLogsView: View {
122128
}
123129
}
124130
}
131+
132+
fileprivate struct ActivityView: UIViewControllerRepresentable {
133+
let activityItems: [Any]
134+
func makeUIViewController(context: Context) -> UIActivityViewController {
135+
UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
136+
}
137+
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
138+
}

EEFRT Demo iOS/EEFRT Demo/TaskResultDetailsView.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,18 @@ import SwiftData
22
import SwiftUI
33
import UIKit
44

5-
struct TaskResultDetailsView: View {
5+
struct TaskResultDetailsView<T: Encodable>: View {
66
@State private var isSharePresented = false
7-
private var jsonString: String
7+
private var formattedString: String
88

9-
init(jsonString: String) {
10-
let formattedString = jsonString
11-
.replacingOccurrences(of: ",", with: ",\n")
12-
.replacingOccurrences(of: "{", with: "")
13-
.replacingOccurrences(of: "}", with: "")
14-
15-
self.jsonString = formattedString
9+
init(taskResult: T) {
10+
self.formattedString = EventLogsFormatter.formatTaskResult(taskResult)
1611
}
1712

1813
var body: some View {
1914
ScrollView {
2015
VStack(alignment: .leading) {
21-
Text(jsonString)
16+
Text(formattedString)
2217
.multilineTextAlignment(.leading)
2318
.textSelection(.enabled)
2419
.frame(width: UIScreen.main.bounds.width)
@@ -34,12 +29,12 @@ struct TaskResultDetailsView: View {
3429
}
3530
}
3631
.sheet(isPresented: $isSharePresented) {
37-
ActivityView(activityItems: [jsonString])
32+
ActivityView(activityItems: [formattedString])
3833
}
3934
}
4035
}
4136

42-
struct ActivityView: UIViewControllerRepresentable {
37+
fileprivate struct ActivityView: UIViewControllerRepresentable {
4338
let activityItems: [Any]
4439
func makeUIViewController(context: Context) -> UIActivityViewController {
4540
UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
3+
struct EventLogsFormatter {
4+
5+
/**
6+
* Formats a task result using the standard formatting logic
7+
* that replaces parentheses and commas with newlines for better readability
8+
*/
9+
static func formatTaskResult<T: Encodable>(_ taskResult: T) -> String {
10+
do {
11+
let jsonString = try JsonHelpers.stringify(taskResult)
12+
return jsonString
13+
.replacingOccurrences(of: "(", with: "(\n ")
14+
.replacingOccurrences(of: ",", with: "\n")
15+
.replacingOccurrences(of: ")", with: "\n)")
16+
} catch {
17+
return "Error formatting task result: \(error.localizedDescription)"
18+
}
19+
}
20+
21+
/**
22+
* Formats all event logs (practice and main trials) into a single sharable string
23+
*/
24+
static func formatAllEventLogs(
25+
practiceTaskResults: [PracticeTaskResult],
26+
taskResults: [TaskResult]
27+
) -> String {
28+
var stringBuilder = ""
29+
30+
// Add practice trials section
31+
stringBuilder += "=== PRACTICE TRIALS ===\n\n"
32+
for (index, practiceTaskResult) in practiceTaskResults.enumerated() {
33+
stringBuilder += "Practice Trial \(index + 1):\n"
34+
stringBuilder += formatTaskResult(practiceTaskResult)
35+
stringBuilder += "\n\n"
36+
}
37+
38+
// Add actual trials section
39+
stringBuilder += "=== MAIN TRIALS ===\n\n"
40+
for (index, taskResult) in taskResults.enumerated() {
41+
stringBuilder += "Main Trial \(index + 1):\n"
42+
stringBuilder += formatTaskResult(taskResult)
43+
stringBuilder += "\n\n"
44+
}
45+
46+
return stringBuilder
47+
}
48+
}

0 commit comments

Comments
 (0)