Skip to content

Commit 76a7f11

Browse files
committed
feat: Add settings screen with import and export
1 parent c259374 commit 76a7f11

File tree

11 files changed

+242
-28
lines changed

11 files changed

+242
-28
lines changed

.idea/workspace.xml

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

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222
<category android:name="android.intent.category.LAUNCHER" />
2323
</intent-filter>
2424
</activity>
25+
26+
<provider
27+
android:name="androidx.core.content.FileProvider"
28+
android:authorities="com.opennotes.fileprovider"
29+
android:exported="false"
30+
android:grantUriPermissions="true">
31+
<meta-data
32+
android:name="android.support.FILE_PROVIDER_PATHS"
33+
android:resource="@xml/provider_paths" />
34+
</provider>
2535
</application>
2636

2737
</manifest>

app/src/main/java/com/opennotes/di/AppModule.kt

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package com.opennotes.di
33
import android.app.Application
44
import androidx.room.Room
55
import com.opennotes.feature_node.data.data_source.NoteDatabase
6+
import com.opennotes.feature_node.data.repository.AndroidFileHandler
7+
import com.opennotes.feature_node.data.repository.FileHandler
8+
import com.opennotes.feature_node.data.repository.GsonJsonHandler
69
import com.opennotes.feature_node.data.repository.JsonHandler
710
import com.opennotes.feature_node.data.repository.NoteRepositoryImpl
8-
import com.opennotes.feature_node.data.repository.jsonHandler
911
import com.opennotes.feature_node.domain.repository.NoteRepository
1012
import com.opennotes.feature_node.domain.use_case.AddNote
1113
import com.opennotes.feature_node.domain.use_case.DeleteNote
@@ -15,7 +17,6 @@ import com.opennotes.feature_node.domain.use_case.GetNotes
1517
import com.opennotes.feature_node.domain.use_case.ImportUseCases
1618
import com.opennotes.feature_node.domain.use_case.NoteUseCases
1719
import com.opennotes.feature_node.domain.use_case.SearchNotesUseCase
18-
import com.opennotes.feature_node.presentation.notes.NotesEvent
1920
import dagger.Module
2021
import dagger.Provides
2122
import dagger.hilt.InstallIn
@@ -34,7 +35,6 @@ object AppModule {
3435
app,
3536
NoteDatabase::class.java,
3637
NoteDatabase.DATABASE_NAME
37-
3838
).build()
3939
}
4040

@@ -44,23 +44,37 @@ object AppModule {
4444
return NoteRepositoryImpl(db.noteDao)
4545
}
4646

47+
// NEW: Provides the FileHandler implementation
48+
@Provides
49+
@Singleton
50+
fun provideFileHandler(app: Application): FileHandler {
51+
return AndroidFileHandler(app)
52+
}
53+
54+
// CORRECTED: Provides the concrete GsonJsonHandler implementation
4755
@Provides
4856
@Singleton
49-
fun provideJsonHandler(): jsonHandler {
50-
return JsonHandler()
57+
fun provideJsonHandler(): JsonHandler {
58+
return GsonJsonHandler()
5159
}
60+
61+
// CORRECTED: Now takes all necessary dependencies as parameters
5262
@Provides
5363
@Singleton
54-
fun provideNoteUseCaseId(repository: NoteRepository): NoteUseCases {
64+
fun provideNoteUseCaseId(
65+
repository: NoteRepository,
66+
jsonHandler: JsonHandler,
67+
fileHandler: FileHandler
68+
): NoteUseCases {
5569
return NoteUseCases(
56-
getNotes= GetNotes(repository),
57-
deleteNote= DeleteNote(repository),
70+
getNotes = GetNotes(repository),
71+
deleteNote = DeleteNote(repository),
5872
addNote = AddNote(repository),
5973
getNote = GetNote(repository),
60-
searchNotes= SearchNotesUseCase(repository),
61-
importNotes= ImportUseCases(repository,jsonHandler,fileHandler),
62-
exportNotes= ExportUseCases(repository,jsonHandler,fileHandler)
74+
searchNotes = SearchNotesUseCase(repository),
75+
76+
importNotes = ImportUseCases(repository, fileHandler,jsonHandler),
77+
exportNotes = ExportUseCases(repository, fileHandler,jsonHandler)
6378
)
6479
}
65-
6680
}

app/src/main/java/com/opennotes/feature_node/domain/use_case/ExportUseCases.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,35 @@ package com.opennotes.feature_node.domain.use_case
22

33
import com.opennotes.feature_node.data.repository.FileHandler
44
import com.opennotes.feature_node.data.repository.GsonJsonHandler
5+
import com.opennotes.feature_node.data.repository.JsonHandler
56

67
import com.opennotes.feature_node.domain.repository.NoteRepository
78
import com.opennotes.feature_node.domain.util.ExportResult
89

910
class ExportUseCases(
1011
private val repository: NoteRepository,
1112
private val fileHandler: FileHandler,
12-
private val jsonHandler:GsonJsonHandler
13+
private val jsonHandler: JsonHandler
1314
) {
1415
suspend operator fun invoke(): ExportResult {
1516
return try {
16-
// 1. Get all notes from the database
17+
18+
19+
20+
1721
val allNotes = repository.getNotes()
1822

19-
// 2. Placeholder: Convert the list of notes to a JSON string
23+
2024
val notesJson = jsonHandler.toJson(allNotes)
2125

2226

2327
val fileUri=fileHandler.saveToFile ("notes_backup.json", notesJson)
2428

29+
30+
if(fileUri==null){
31+
return ExportResult.Error("Could not save the file to a URI")
32+
}
33+
2534
ExportResult.Success
2635
} catch (e: Exception) {
2736
ExportResult.Error("Failed to export notes: ${e.message}")

app/src/main/java/com/opennotes/feature_node/domain/use_case/ImportUseCases.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,28 @@ package com.opennotes.feature_node.domain.use_case
33
import android.net.Uri
44
import com.google.gson.reflect.TypeToken
55
import com.opennotes.feature_node.data.repository.FileHandler
6-
import com.opennotes.feature_node.data.repository.JsonHandler // Correct import for JsonHandler
6+
import com.opennotes.feature_node.data.repository.JsonHandler
77
import com.opennotes.feature_node.domain.model.Note
88
import com.opennotes.feature_node.domain.repository.NoteRepository
99
import com.opennotes.feature_node.domain.util.ImportResult
1010

1111
class ImportUseCases(
1212
private val repository: NoteRepository,
1313
private val fileHandler: FileHandler,
14-
private val jsonHandler: JsonHandler // Correct class name
14+
private val jsonHandler: JsonHandler
1515
) {
1616
suspend operator fun invoke(fileUri: Uri): ImportResult {
1717
return try {
18-
// Fix 1: Call the method on the fileHandler object
18+
1919
val notesJson = fileHandler.readTextFromUri(fileUri)
2020

21-
// Fix 2: Use TypeToken to get the type of List<Note> for Gson
21+
2222
val noteListType = object : TypeToken<List<Note>>() {}.type
2323

24-
// Fix 3: Call the method on the jsonHandler object and pass the type
24+
2525
val notesToImport = jsonHandler.fromJson<List<Note>>(notesJson, noteListType)
2626

27-
// Fix 4: The repository method is likely insertNotes, not insertNote
27+
2828
repository.insertNotes(notesToImport)
2929

3030
ImportResult.Success

app/src/main/java/com/opennotes/feature_node/presentation/MainActivity.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import androidx.navigation.compose.rememberNavController
1414
import androidx.navigation.navArgument
1515
import com.opennotes.feature_node.presentation.add_edit_note.AddEditNoteScreen
1616
import com.opennotes.feature_node.presentation.notes.NotesScreen
17+
import com.opennotes.feature_node.presentation.settings.SettingsScreen
1718
import com.opennotes.feature_node.presentation.util.Screen
1819
import com.opennotes.ui.theme.OpenNotesTheme
1920
import dagger.hilt.android.AndroidEntryPoint
@@ -57,6 +58,11 @@ class MainActivity : ComponentActivity() {
5758
noteColor = color
5859
)
5960
}
61+
62+
63+
composable(route=Screen.SettingsScreen.route){
64+
SettingsScreen(navController=navController)
65+
}
6066
}
6167
}
6268
}

app/src/main/java/com/opennotes/feature_node/presentation/notes/NotesScreen.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
99
import androidx.compose.material.icons.Icons
1010
import androidx.compose.material.icons.filled.Add
1111
import androidx.compose.material.icons.filled.Search
12+
import androidx.compose.material.icons.filled.Settings
1213
import androidx.compose.material.icons.filled.Sort
1314

1415

@@ -99,6 +100,19 @@ fun NotesScreen(
99100
imageVector = Icons.Default.Sort,
100101
contentDescription = "Sort"
101102
)
103+
104+
105+
106+
}
107+
IconButton(
108+
onClick={
109+
navController.navigate(Screen.SettingsScreen.route)
110+
}
111+
) {
112+
Icon(
113+
imageVector=Icons.Default.Settings,
114+
contentDescription = "Settings"
115+
)
102116
}
103117
}
104118
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
package com.opennotes.feature_node.presentation.settings
3+
4+
import android.net.Uri
5+
import androidx.activity.compose.rememberLauncherForActivityResult
6+
import androidx.activity.result.contract.ActivityResultContracts
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.material3.Button
13+
import androidx.compose.material3.Scaffold
14+
import androidx.compose.material3.SnackbarHost
15+
import androidx.compose.material3.SnackbarHostState
16+
import androidx.compose.material3.Text
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.runtime.LaunchedEffect
19+
import androidx.compose.runtime.remember
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.unit.dp
23+
import androidx.hilt.navigation.compose.hiltViewModel
24+
import androidx.navigation.NavController
25+
import kotlinx.coroutines.flow.collectLatest
26+
27+
@Composable
28+
fun SettingsScreen(
29+
navController:NavController,
30+
viewModel: SettingsViewModel = hiltViewModel()) {
31+
32+
val snackbarHostState = remember { SnackbarHostState() }
33+
34+
35+
val filePickerLauncher = rememberLauncherForActivityResult(
36+
contract = ActivityResultContracts.OpenDocument(),
37+
onResult = { uri: Uri? ->
38+
if (uri != null) {
39+
viewModel.onImportClick(uri)
40+
}
41+
}
42+
)
43+
44+
45+
LaunchedEffect(key1 = true) {
46+
viewModel.uiEvent.collectLatest { event ->
47+
when (event) {
48+
is SettingsViewModel.UiEvent.ShowSnackbar -> {
49+
snackbarHostState.showSnackbar(message = event.message)
50+
}
51+
}
52+
}
53+
}
54+
55+
Scaffold(
56+
snackbarHost = { SnackbarHost(snackbarHostState) }
57+
) { paddingValues ->
58+
Column(
59+
modifier = Modifier
60+
.fillMaxSize()
61+
.padding(paddingValues)
62+
.padding(16.dp),
63+
horizontalAlignment = Alignment.CenterHorizontally
64+
) {
65+
Button(onClick = { viewModel.onExportClick() }) {
66+
Text("Export Notes")
67+
}
68+
Spacer(Modifier.height(16.dp))
69+
Button(onClick = {
70+
filePickerLauncher.launch(arrayOf("application/json"))
71+
}) {
72+
Text("Import Notes")
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)