Skip to content

Commit 92e3ee5

Browse files
committed
Refactor UI and improve diff visualization
This commit introduces a new `DiffCheckerScreen.kt` to encapsulate the main UI logic, previously in `MainActivity.kt`. It also refactors the diff visualization by creating two new composables: `CharDiffText.kt` and `InlineCharDiffText.kt`. Key changes: - **UI Refactoring:** - Moved UI logic from `MainActivity.kt` to a new `DiffCheckerScreen.kt`. - `MainActivity.kt` now simply sets up `DiffCheckerScreen`. - **Improved Diff Visualization:** - Introduced `CharDiffText.kt` composable to display the side-by-side diff view using `LazyColumn` for both old and new text. - Lines with differences (ADDED, DELETED, CHANGED) are now highlighted with background colors (reddish for deletions/changes in old, greenish for additions/changes in new). - Introduced `InlineCharDiffText.kt` composable to render character-level differences within a line. - Deleted characters are highlighted with a `delete` color background. - Inserted characters are highlighted with an `added` color background. - Updated `Color.kt` to include `delete` and `added` color definitions. - **Test Cleanup:** - Removed unused instrumented test class `ExampleInstrumentedTest.kt`.
1 parent fa777a1 commit 92e3ee5

6 files changed

Lines changed: 232 additions & 176 deletions

File tree

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,11 @@
11
package dev.jahidhasanco.diffly
22

3-
import androidx.test.platform.app.InstrumentationRegistry
4-
import androidx.test.ext.junit.runners.AndroidJUnit4
5-
6-
import org.junit.Test
7-
import org.junit.runner.RunWith
8-
9-
import org.junit.Assert.*
103

114
/**
125
* Instrumented test, which will execute on an Android device.
136
*
147
* See [testing documentation](http://d.android.com/tools/testing).
158
*/
16-
@RunWith(AndroidJUnit4::class)
179
class ExampleInstrumentedTest {
18-
@Test
19-
fun useAppContext() {
20-
// Context of the app under test.
21-
val appContext =
22-
InstrumentationRegistry.getInstrumentation().targetContext
23-
assertEquals("dev.jahidhasanco.diffchecker", appContext.packageName)
24-
}
10+
2511
}

app/src/main/java/dev/jahidhasanco/diffly/presentation/MainActivity.kt

Lines changed: 1 addition & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,8 @@ package dev.jahidhasanco.diffly.presentation
33
import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
6-
import androidx.compose.foundation.layout.fillMaxSize
7-
import androidx.compose.foundation.layout.padding
8-
import androidx.compose.material3.Text
9-
import androidx.compose.runtime.Composable
10-
import androidx.compose.foundation.background
11-
import androidx.compose.foundation.border
12-
import androidx.compose.foundation.layout.*
13-
import androidx.compose.foundation.lazy.LazyColumn
14-
import androidx.compose.foundation.lazy.items
15-
import androidx.compose.foundation.lazy.rememberLazyListState
16-
import androidx.compose.foundation.rememberScrollState
17-
import androidx.compose.foundation.verticalScroll
18-
import androidx.compose.material3.*
19-
import androidx.compose.runtime.*
20-
import androidx.compose.ui.Alignment
21-
import androidx.compose.ui.Modifier
22-
import androidx.compose.ui.graphics.Color
23-
import androidx.compose.ui.graphics.Color.Companion.Transparent
24-
import androidx.compose.ui.text.SpanStyle
25-
import androidx.compose.ui.text.buildAnnotatedString
26-
import androidx.compose.ui.text.withStyle
27-
import androidx.compose.ui.unit.dp
286
import dev.jahidhasanco.diffly.di.AppModule
29-
import dev.jahidhasanco.diffly.domain.model.CharDiff
30-
import dev.jahidhasanco.diffly.domain.model.CharDiffType
31-
import dev.jahidhasanco.diffly.domain.model.DiffType
7+
import dev.jahidhasanco.diffly.presentation.screen.DiffCheckerScreen
328

339
class MainActivity : ComponentActivity() {
3410
private val viewModel by lazy {
@@ -43,139 +19,4 @@ class MainActivity : ComponentActivity() {
4319
}
4420
}
4521

46-
@Composable
47-
fun DiffCheckerScreen(viewModel: MainViewModel) {
48-
var oldText by remember { mutableStateOf("") }
49-
var newText by remember { mutableStateOf("") }
50-
val diffResult by viewModel.diffResult.collectAsState()
5122

52-
Column(
53-
modifier = Modifier
54-
.fillMaxSize()
55-
.padding(16.dp)
56-
) {
57-
Text("Old Text")
58-
TextField(
59-
value = oldText,
60-
onValueChange = { oldText = it },
61-
modifier = Modifier
62-
.fillMaxWidth()
63-
.height(150.dp)
64-
)
65-
66-
Spacer(modifier = Modifier.height(8.dp))
67-
68-
Text("New Text")
69-
TextField(
70-
value = newText,
71-
onValueChange = { newText = it },
72-
modifier = Modifier
73-
.fillMaxWidth()
74-
.height(150.dp)
75-
)
76-
77-
Spacer(modifier = Modifier.height(16.dp))
78-
79-
Button(
80-
onClick = { viewModel.calculateDiff(oldText, newText) },
81-
modifier = Modifier.align(Alignment.CenterHorizontally)
82-
) {
83-
Text("Compare")
84-
}
85-
86-
Spacer(modifier = Modifier.height(16.dp))
87-
88-
// Use Box with weight to give LazyColumns height
89-
Box(modifier = Modifier.weight(1f)) {
90-
Row(modifier = Modifier.fillMaxSize()) {
91-
val scrollStateOld = rememberLazyListState()
92-
val scrollStateNew = rememberLazyListState()
93-
94-
// Old Text Column
95-
LazyColumn(
96-
state = scrollStateOld,
97-
modifier = Modifier
98-
.weight(1f)
99-
.fillMaxHeight()
100-
.border(1.dp, Color.Gray)
101-
) {
102-
item {
103-
Text(
104-
"Old Text",
105-
modifier = Modifier.padding(8.dp),
106-
style = MaterialTheme.typography.titleMedium
107-
)
108-
}
109-
items(diffResult) { entry ->
110-
entry.oldLine?.let {
111-
val charDiffs = entry.charDiffs?.filter { it.type != CharDiffType.INSERTED }
112-
Box(
113-
modifier = Modifier
114-
.fillMaxWidth()
115-
.padding(4.dp)
116-
) {
117-
if (!charDiffs.isNullOrEmpty()) {
118-
InlineCharDiffText(charDiffs = charDiffs)
119-
} else {
120-
Text(it)
121-
}
122-
}
123-
}
124-
}
125-
}
126-
127-
Spacer(modifier = Modifier.width(8.dp))
128-
129-
// New Text Column
130-
LazyColumn(
131-
state = scrollStateNew,
132-
modifier = Modifier
133-
.weight(1f)
134-
.fillMaxHeight()
135-
.border(1.dp, Color.Gray)
136-
) {
137-
item {
138-
Text(
139-
"New Text",
140-
modifier = Modifier.padding(8.dp),
141-
style = MaterialTheme.typography.titleMedium
142-
)
143-
}
144-
items(diffResult) { entry ->
145-
entry.newLine?.let {
146-
val charDiffs = entry.charDiffs?.filter { it.type != CharDiffType.DELETED }
147-
Box(
148-
modifier = Modifier
149-
.fillMaxWidth()
150-
.padding(4.dp)
151-
) {
152-
if (!charDiffs.isNullOrEmpty()) {
153-
InlineCharDiffText(charDiffs = charDiffs)
154-
} else {
155-
Text(it)
156-
}
157-
}
158-
}
159-
}
160-
}
161-
}
162-
}
163-
}
164-
}
165-
166-
@Composable
167-
fun InlineCharDiffText(charDiffs: List<CharDiff>) {
168-
val annotatedString = buildAnnotatedString {
169-
charDiffs.forEach { cd ->
170-
val color = when (cd.type) {
171-
CharDiffType.UNCHANGED -> Color.Unspecified
172-
CharDiffType.INSERTED -> Color(0xFF00C281) // green
173-
CharDiffType.DELETED -> Color(0xFFFF6B6B) // red
174-
}
175-
withStyle(SpanStyle(color = color)) {
176-
append(cd.char)
177-
}
178-
}
179-
}
180-
Text(annotatedString)
181-
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package dev.jahidhasanco.diffly.presentation.component
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.border
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.fillMaxHeight
9+
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.width
13+
import androidx.compose.foundation.lazy.LazyColumn
14+
import androidx.compose.foundation.lazy.items
15+
import androidx.compose.foundation.lazy.rememberLazyListState
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.Text
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.remember
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.graphics.Color
22+
import androidx.compose.ui.unit.dp
23+
import dev.jahidhasanco.diffly.domain.model.CharDiffType
24+
import dev.jahidhasanco.diffly.domain.model.DiffEntry
25+
import dev.jahidhasanco.diffly.domain.model.DiffType
26+
import dev.jahidhasanco.diffly.presentation.theme.added
27+
import dev.jahidhasanco.diffly.presentation.theme.delete
28+
29+
@Composable
30+
fun CharDiffText(diffResult: List<DiffEntry>) {
31+
val scrollStateOld = rememberLazyListState()
32+
val scrollStateNew = rememberLazyListState()
33+
34+
Row(modifier = Modifier.fillMaxSize()) {
35+
LazyColumn(
36+
state = scrollStateOld,
37+
modifier = Modifier
38+
.weight(1f)
39+
.fillMaxHeight()
40+
) {
41+
item {
42+
Text(
43+
"Old Text",
44+
modifier = Modifier.padding(8.dp),
45+
style = MaterialTheme.typography.titleMedium
46+
)
47+
}
48+
items(diffResult) { entry ->
49+
entry.oldLine?.let { line ->
50+
val charDiffs = remember(entry.charDiffs) {
51+
entry.charDiffs?.filter { it.type != CharDiffType.INSERTED }
52+
}
53+
val color = entry.type
54+
.takeIf { it != DiffType.UNCHANGED }
55+
?.let {
56+
when (it) {
57+
DiffType.ADDED -> delete.copy(alpha = 0.3f)
58+
DiffType.DELETED -> delete.copy(alpha = 0.3f)
59+
DiffType.CHANGED -> delete.copy(alpha = 0.3f)
60+
else -> Color.Unspecified
61+
}
62+
} ?: Color.Unspecified
63+
Box(
64+
modifier = Modifier
65+
.fillMaxWidth()
66+
.padding(4.dp)
67+
.background(color = color)
68+
) {
69+
if (!charDiffs.isNullOrEmpty()) {
70+
InlineCharDiffText(charDiffs = charDiffs)
71+
} else {
72+
Text(line)
73+
}
74+
}
75+
}
76+
}
77+
}
78+
79+
Spacer(modifier = Modifier.width(8.dp))
80+
81+
// New text column
82+
LazyColumn(
83+
state = scrollStateNew,
84+
modifier = Modifier
85+
.weight(1f)
86+
.fillMaxHeight()
87+
) {
88+
item {
89+
Text(
90+
"New Text",
91+
modifier = Modifier.padding(8.dp),
92+
style = MaterialTheme.typography.titleMedium
93+
)
94+
}
95+
items(diffResult) { entry ->
96+
entry.newLine?.let { line ->
97+
val charDiffs = remember(entry.charDiffs) {
98+
entry.charDiffs?.filter { it.type != CharDiffType.DELETED }
99+
}
100+
val color = entry.type
101+
.takeIf { it != DiffType.UNCHANGED }
102+
?.let {
103+
when (it) {
104+
DiffType.ADDED -> added.copy(alpha = 0.3f)
105+
DiffType.DELETED -> added.copy(alpha = 0.3f)
106+
DiffType.CHANGED -> added.copy(alpha = 0.3f)
107+
else -> Color.Unspecified
108+
}
109+
} ?: Color.Unspecified
110+
Box(
111+
modifier = Modifier
112+
.fillMaxWidth()
113+
.padding(4.dp)
114+
.background(color)
115+
116+
) {
117+
if (!charDiffs.isNullOrEmpty()) {
118+
InlineCharDiffText(charDiffs = charDiffs)
119+
} else {
120+
Text(line)
121+
}
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dev.jahidhasanco.diffly.presentation.component
2+
3+
import androidx.compose.material3.Text
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.graphics.Color
6+
import androidx.compose.ui.text.SpanStyle
7+
import androidx.compose.ui.text.buildAnnotatedString
8+
import androidx.compose.ui.text.withStyle
9+
import dev.jahidhasanco.diffly.domain.model.CharDiff
10+
import dev.jahidhasanco.diffly.domain.model.CharDiffType
11+
import dev.jahidhasanco.diffly.presentation.theme.added
12+
import dev.jahidhasanco.diffly.presentation.theme.delete
13+
14+
@Composable
15+
fun InlineCharDiffText(charDiffs: List<CharDiff>) {
16+
val annotatedString = buildAnnotatedString {
17+
charDiffs.forEach { cd ->
18+
19+
val backgroundColor = when (cd.type) {
20+
CharDiffType.UNCHANGED -> Color.Unspecified
21+
CharDiffType.INSERTED -> added
22+
CharDiffType.DELETED -> delete
23+
}
24+
withStyle(
25+
SpanStyle(background = backgroundColor)
26+
) {
27+
append(cd.char)
28+
}
29+
}
30+
}
31+
Text(annotatedString)
32+
}

0 commit comments

Comments
 (0)