Skip to content

Commit d0cf8ee

Browse files
committed
Add implementation
1 parent ca5fcfa commit d0cf8ee

File tree

66 files changed

+1965
-61
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1965
-61
lines changed

app/build.gradle.kts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
plugins {
22
alias(libs.plugins.android.application)
33
alias(libs.plugins.jetbrains.kotlin.android)
4+
alias(libs.plugins.jetbrains.kotlin.kapt)
5+
alias(libs.plugins.google.kotlin.ksp)
6+
alias(libs.plugins.compose.compiler)
7+
alias(libs.plugins.hilt.android)
48
}
59

610
android {
@@ -40,7 +44,7 @@ android {
4044
compose = true
4145
}
4246
composeOptions {
43-
kotlinCompilerExtensionVersion = "1.5.1"
47+
kotlinCompilerExtensionVersion = "1.5.15"
4448
}
4549
packaging {
4650
resources {
@@ -59,11 +63,36 @@ dependencies {
5963
implementation(libs.androidx.ui.graphics)
6064
implementation(libs.androidx.ui.tooling.preview)
6165
implementation(libs.androidx.material3)
66+
67+
// Navigation for Jetpack Compose
68+
implementation(libs.androidx.navigation.compose)
69+
implementation(libs.androidx.navigation.runtime.ktx)
70+
implementation(libs.androidx.hilt.navigation.compose)
71+
implementation(libs.androidx.hilt.common)
72+
73+
// ViewModel integration with Jetpack Compose
74+
implementation(libs.androidx.lifecycle.viewmodel.compose)
75+
76+
// WorkManager
77+
implementation(libs.androidx.work.runtime)
78+
79+
// Dependency Injection - Hilt
80+
implementation(libs.google.dagger.hilt)
81+
kapt(libs.google.dagger.hilt.compiler)
82+
83+
// Database - Room
84+
implementation(libs.androidx.room.runtime)
85+
implementation(libs.androidx.room.ktx)
86+
ksp(libs.androidx.room.compiler)
87+
88+
// Testing
6289
testImplementation(libs.junit)
6390
androidTestImplementation(libs.androidx.junit)
6491
androidTestImplementation(libs.androidx.espresso.core)
6592
androidTestImplementation(platform(libs.androidx.compose.bom))
6693
androidTestImplementation(libs.androidx.ui.test.junit4)
94+
95+
// Debugging
6796
debugImplementation(libs.androidx.ui.tooling)
6897
debugImplementation(libs.androidx.ui.test.manifest)
6998
}

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
6+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
7+
58
<application
9+
android:name=".app.HeliNoteApplication"
610
android:allowBackup="true"
711
android:dataExtractionRules="@xml/data_extraction_rules"
812
android:fullBackupContent="@xml/backup_rules"
@@ -13,7 +17,7 @@
1317
android:theme="@style/Theme.HeliNote"
1418
tools:targetApi="31">
1519
<activity
16-
android:name=".MainActivity"
20+
android:name=".app.ui.MainActivity"
1721
android:exported="true"
1822
android:label="@string/app_name"
1923
android:theme="@style/Theme.HeliNote">
@@ -23,6 +27,11 @@
2327
<category android:name="android.intent.category.LAUNCHER" />
2428
</intent-filter>
2529
</activity>
30+
31+
<receiver
32+
android:name=".feature.notification.receiver.ReminderBroadcastReceiver"
33+
android:enabled="true"
34+
android:exported="false" />
2635
</application>
2736

2837
</manifest>

app/src/main/java/technology/heli/helinote/MainActivity.kt

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package technology.heli.helinote.app
2+
3+
import android.app.Application
4+
import android.content.Context
5+
import androidx.work.ExistingPeriodicWorkPolicy
6+
import androidx.work.PeriodicWorkRequest
7+
import androidx.work.PeriodicWorkRequestBuilder
8+
import androidx.work.WorkManager
9+
import dagger.hilt.android.HiltAndroidApp
10+
import technology.heli.helinote.feature.notification.worker.RemovePastRemindersWorker
11+
import java.util.concurrent.TimeUnit
12+
13+
@HiltAndroidApp
14+
class HeliNoteApplication : Application() {
15+
16+
override fun onCreate() {
17+
super.onCreate()
18+
19+
schedulePeriodicWork(this)
20+
}
21+
22+
private fun schedulePeriodicWork(context: Context) {
23+
val workRequest: PeriodicWorkRequest =
24+
PeriodicWorkRequestBuilder<RemovePastRemindersWorker>(1, TimeUnit.DAYS)
25+
.build()
26+
27+
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
28+
"MyPeriodicWork",
29+
ExistingPeriodicWorkPolicy.KEEP,
30+
workRequest
31+
)
32+
}
33+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package technology.heli.helinote.app.ui
2+
3+
import android.content.Intent
4+
import android.os.Build
5+
import android.os.Bundle
6+
import android.provider.Settings
7+
import androidx.activity.ComponentActivity
8+
import androidx.activity.compose.setContent
9+
import androidx.activity.enableEdgeToEdge
10+
import androidx.compose.runtime.Composable
11+
import androidx.navigation.NavHostController
12+
import androidx.navigation.NavType
13+
import androidx.navigation.compose.NavHost
14+
import androidx.navigation.compose.composable
15+
import androidx.navigation.compose.rememberNavController
16+
import androidx.navigation.navArgument
17+
import dagger.hilt.android.AndroidEntryPoint
18+
import technology.heli.helinote.core.ui.theme.HeliNoteTheme
19+
import technology.heli.helinote.feature.addedit.AddEditNoteScreen
20+
import technology.heli.helinote.feature.list.NotesScreen
21+
import technology.heli.helinote.feature.navigation.Screen
22+
23+
@AndroidEntryPoint
24+
class MainActivity : ComponentActivity() {
25+
override fun onCreate(savedInstanceState: Bundle?) {
26+
super.onCreate(savedInstanceState)
27+
enableEdgeToEdge()
28+
setContent {
29+
HeliNoteTheme {
30+
AppNavHost(onNavigateToExactAlarmSettings = { navigateToExactAlarmSettings() })
31+
}
32+
}
33+
}
34+
35+
private fun navigateToExactAlarmSettings() {
36+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
37+
Intent().apply { action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM }
38+
.also { startActivity(it) }
39+
}
40+
}
41+
}
42+
43+
@Composable
44+
fun AppNavHost(
45+
navController: NavHostController = rememberNavController(),
46+
onNavigateToExactAlarmSettings: () -> Unit
47+
) {
48+
NavHost(navController = navController, startDestination = Screen.NoteListScreen.route) {
49+
composable(route = Screen.NoteListScreen.route) {
50+
NotesScreen(navController = navController)
51+
}
52+
composable(
53+
route = Screen.NoteAddEditScreen.route,
54+
arguments = listOf(
55+
navArgument(name = "noteId") {
56+
type = NavType.LongType
57+
defaultValue = 0L
58+
}
59+
)
60+
) { backStackEntry ->
61+
val noteId = backStackEntry.arguments?.getLong("noteId") ?: 0L
62+
AddEditNoteScreen(
63+
navController = navController,
64+
noteId = noteId,
65+
onNavigateToExactAlarmSettings = { onNavigateToExactAlarmSettings() })
66+
}
67+
}
68+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package technology.heli.helinote.core.data.di
2+
3+
import dagger.Module
4+
import dagger.Provides
5+
import dagger.hilt.InstallIn
6+
import dagger.hilt.components.SingletonComponent
7+
import technology.heli.helinote.core.data.mapper.NoteEntityMapper
8+
import technology.heli.helinote.core.data.mapper.ReminderEntityMapper
9+
import technology.heli.helinote.core.database.entity.NoteEntity
10+
import technology.heli.helinote.core.database.entity.ReminderEntity
11+
import technology.heli.helinote.core.domain.mapper.DataMapper
12+
import technology.heli.helinote.core.domain.model.Note
13+
import technology.heli.helinote.core.domain.model.Reminder
14+
import javax.inject.Singleton
15+
16+
@Module
17+
@InstallIn(SingletonComponent::class)
18+
object DataMapperModule {
19+
20+
@Provides
21+
@Singleton
22+
fun provideNoteMapper(): DataMapper<NoteEntity, Note> = NoteEntityMapper()
23+
24+
@Provides
25+
@Singleton
26+
fun provideReminderMapper(): DataMapper<ReminderEntity, Reminder> = ReminderEntityMapper()
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package technology.heli.helinote.core.data.di
2+
3+
import dagger.Binds
4+
import dagger.Module
5+
import dagger.hilt.InstallIn
6+
import dagger.hilt.components.SingletonComponent
7+
import technology.heli.helinote.core.data.repository.DefaultNoteRepository
8+
import technology.heli.helinote.core.data.repository.DefaultReminderRepository
9+
import technology.heli.helinote.core.domain.repository.NoteRepository
10+
import technology.heli.helinote.core.domain.repository.ReminderRepository
11+
import javax.inject.Singleton
12+
13+
@Module
14+
@InstallIn(SingletonComponent::class)
15+
abstract class RepositoryModule {
16+
17+
@Binds
18+
@Singleton
19+
abstract fun bindNoteRepository(noteRepository: DefaultNoteRepository): NoteRepository
20+
21+
@Binds
22+
@Singleton
23+
abstract fun bindReminderRepository(
24+
reminderRepository: DefaultReminderRepository
25+
): ReminderRepository
26+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package technology.heli.helinote.core.data.mapper
2+
3+
import technology.heli.helinote.core.domain.model.Note
4+
import technology.heli.helinote.core.database.entity.NoteEntity
5+
import technology.heli.helinote.core.domain.mapper.DataMapper
6+
7+
class NoteEntityMapper : DataMapper<NoteEntity, Note> {
8+
9+
override fun mapTo(input: NoteEntity) = Note(
10+
id = input.id,
11+
title = input.title,
12+
content = input.content,
13+
timestamp = input.timestamp,
14+
reminders = emptyList(),
15+
)
16+
17+
override fun mapFrom(output: Note) = NoteEntity(
18+
id = output.id,
19+
title = output.title,
20+
content = output.content,
21+
timestamp = output.timestamp,
22+
)
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package technology.heli.helinote.core.data.mapper
2+
3+
import technology.heli.helinote.core.domain.model.Reminder
4+
import technology.heli.helinote.core.database.entity.ReminderEntity
5+
import technology.heli.helinote.core.domain.mapper.DataMapper
6+
7+
class ReminderEntityMapper : DataMapper<ReminderEntity, Reminder> {
8+
9+
override fun mapTo(input: ReminderEntity) = Reminder(
10+
id = input.id,
11+
timestamp = input.timestamp,
12+
repeatType = input.repeatType,
13+
noteId = input.noteId,
14+
)
15+
16+
override fun mapFrom(output: Reminder) = ReminderEntity(
17+
id = output.id,
18+
timestamp = output.timestamp,
19+
repeatType = output.repeatType,
20+
noteId = output.noteId,
21+
)
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package technology.heli.helinote.core.data.repository
2+
3+
import kotlinx.coroutines.flow.Flow
4+
import kotlinx.coroutines.flow.map
5+
import technology.heli.helinote.core.database.dao.NoteDao
6+
import technology.heli.helinote.core.database.entity.NoteEntity
7+
import technology.heli.helinote.core.domain.mapper.DataMapper
8+
import technology.heli.helinote.core.domain.model.Note
9+
import technology.heli.helinote.core.domain.repository.NoteRepository
10+
import javax.inject.Inject
11+
12+
class DefaultNoteRepository @Inject constructor(
13+
private val noteDao: NoteDao,
14+
private val noteMapper: DataMapper<NoteEntity, Note>
15+
) : NoteRepository {
16+
17+
override fun getNotes(): Flow<List<Note>> =
18+
noteDao.getNotes().map { notes -> notes.map { noteMapper.mapTo(it) } }
19+
20+
override fun getNoteById(id: Long): Flow<Note?> =
21+
noteDao.getNoteById(id).map { note -> note?.let { noteMapper.mapTo(it) } }
22+
23+
override suspend fun insertNote(note: Note) =
24+
noteDao.insertNote(noteMapper.mapFrom(note))
25+
26+
override suspend fun deleteNoteById(id: Long) =
27+
noteDao.deleteNoteById(id)
28+
}

0 commit comments

Comments
 (0)