diff --git a/app/build.gradle b/app/build.gradle index 864fd88..04216c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,16 +1,20 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id "com.google.dagger.hilt.android" + id 'kotlinx-serialization' + id "kotlin-kapt" + id 'com.google.protobuf' version '0.9.4' } android { namespace 'be.re.writand' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "be.re.writand" - minSdk 24 - targetSdk 33 + minSdk 26 + targetSdk 34 versionCode 1 versionName "1.0" @@ -27,37 +31,92 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.2.0' + kotlinCompilerExtensionVersion '1.5.0' } packagingOptions { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + testOptions { + unitTests.all { + useJUnitPlatform() + } + unitTests.returnDefaultValues = true + } } dependencies { - - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' - implementation 'androidx.activity:activity-compose:1.3.1' + implementation 'androidx.core:core-ktx:1.13.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.3' + implementation 'androidx.activity:activity-compose:1.9.0' implementation "androidx.compose.ui:ui:$compose_ui_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" - implementation 'androidx.compose.material:material:1.2.0' + implementation 'androidx.compose.material:material:1.6.8' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + // Testing androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.1" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.1" + + // Mockito testing + testImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0" + testImplementation "org.robolectric:robolectric:3.4.2" + testImplementation 'org.powermock:powermock-api-mockito:1.4.12' + testImplementation 'org.powermock:powermock-module-junit4:1.6.2' + + testImplementation "org.mockito:mockito-core:4.2.0" + testImplementation "org.mockito:mockito-inline:4.1.0" + testImplementation "org.mockito:mockito-android:4.1.0" + + // Hilt + implementation "com.google.dagger:hilt-android:2.48" + kapt "com.google.dagger:hilt-compiler:2.48" + + // Room + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.6.1' + implementation 'androidx.room:room-ktx:2.6.1' + + // Proto DataStore + implementation 'androidx.datastore:datastore:1.1.1' + implementation 'com.google.protobuf:protobuf-javalite:3.21.7' + debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" +} + +kapt { + correctErrorTypes true +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.21.7" + } +// Generates the java Protobuf-lite code for the Protobufs in this project. +// see https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation +// for more information. + generateProtoTasks { + all().each { task -> + task.builtins { + java { + option 'lite' + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/local/db/ProjectEntity.kt b/app/src/main/java/be/re/writand/data/local/db/ProjectEntity.kt new file mode 100644 index 0000000..6ccf42e --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/db/ProjectEntity.kt @@ -0,0 +1,19 @@ +package be.re.writand.data.local.db + +import androidx.room.Entity +import androidx.room.PrimaryKey +import be.re.writand.data.local.models.OpenFilePath + +/** + * Entity for recent projects to keep in memory, used in the ProjectsDAO. + * @param[id] the unique id of a project. + * @param[path] the absolute path to the directory/file. + * A file in case of a project consisting of 1 file only. + * @param[openedFiles] a list of objects to specify more about each last opened file. + */ +@Entity(tableName = "projects") +data class ProjectEntity ( + @PrimaryKey val id: Int, + val path: String, + val openedFiles: List +) \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/local/db/ProjectsDao.kt b/app/src/main/java/be/re/writand/data/local/db/ProjectsDao.kt new file mode 100644 index 0000000..ce54be0 --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/db/ProjectsDao.kt @@ -0,0 +1,37 @@ +package be.re.writand.data.local.db + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import be.re.writand.data.local.models.Project +import kotlinx.coroutines.flow.Flow + +/** + * A DAO to handle recent opened projects when the Project Picker is presented. + */ +@Dao +interface ProjectsDao { + + /** + * Get all the recent files that were opened. + */ + @Query("SELECT * FROM projects") + fun getAllProjects(): Flow> + + /** + * Insert a project into the database that was just opened. + * @param[project] the project to add in the database. + */ + @Insert(entity = ProjectEntity::class) + suspend fun insertProject(project: Project) + + /** + * Delete a project from the recent opened projects. + * Should be called when the limit of saving projects is reached. + * @param[project] the project to be deleted from the database. + */ + @Delete(entity = ProjectEntity::class) + suspend fun deleteProject(project: Project) + +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/local/db/ProjectsDatabase.kt b/app/src/main/java/be/re/writand/data/local/db/ProjectsDatabase.kt new file mode 100644 index 0000000..2b2c57e --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/db/ProjectsDatabase.kt @@ -0,0 +1,25 @@ +package be.re.writand.data.local.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +/** + * Database for all the recent projects. + */ +@Database(entities = [ProjectEntity::class], version = 1) +abstract class ProjectsDatabase: RoomDatabase() { + + abstract fun projectsDao(): ProjectsDao + + companion object { + @Volatile private var instance: ProjectsDatabase? = null + + fun getInstance(context: Context) = instance ?: synchronized(this) { + return Room.databaseBuilder( + context.applicationContext, ProjectsDatabase::class.java, "projects-db" + ).build().also { instance = it } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/local/models/.keep b/app/src/main/java/be/re/writand/data/local/models/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/be/re/writand/data/local/models/Project.kt b/app/src/main/java/be/re/writand/data/local/models/Project.kt new file mode 100644 index 0000000..063eb24 --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/models/Project.kt @@ -0,0 +1,16 @@ +package be.re.writand.data.local.models + +/** + * Data class used in the ProjectsDAO to store information about a recent project. + * @param[id] a unique id for the project. + * @param[path] the absolute path to the directory/file location of the project. + */ +data class Project(val id: Int, val path: String) + +/** + * Data class used in ProjectEntity to store information about a recent file of a project. + * @param[projectId] the id of the project the file belongs to. + * @param[path] the relative path to the file starting from the root fo the project. + * @param[isSaved] info whether the file was saved before closing the app (for expansion of unsaved files). + */ +data class OpenFilePath(val projectId: Int, val path: String, var isSaved: Boolean) \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/repos/.keep b/app/src/main/java/be/re/writand/data/repos/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/be/re/writand/data/repos/IProjectsRepository.kt b/app/src/main/java/be/re/writand/data/repos/IProjectsRepository.kt new file mode 100644 index 0000000..d29344e --- /dev/null +++ b/app/src/main/java/be/re/writand/data/repos/IProjectsRepository.kt @@ -0,0 +1,29 @@ +package be.re.writand.data.repos + +import be.re.writand.data.local.models.Project +import kotlinx.coroutines.flow.Flow + + +/** + * Repository that handles everything for the local ProjectsDatabase. + * @param[dao] the dao provides get/add/delete functionality of the recent projects. + */ +interface IProjectsRepository { + + /** + * Gets all the projects from the local storage. + */ + fun getAllProjects(): Flow> + + /** + * Adds a project to the local storage of recent projects. + * @param[project] the project to be added to the database. + */ + suspend fun addProject(project: Project) + + /** + * Deletes a project from the local storage of recent projects. + * @param[project] the project to be deleted from the database. + */ + suspend fun deleteProject(project: Project) +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/repos/ProjectsRepository.kt b/app/src/main/java/be/re/writand/data/repos/ProjectsRepository.kt new file mode 100644 index 0000000..d8b6671 --- /dev/null +++ b/app/src/main/java/be/re/writand/data/repos/ProjectsRepository.kt @@ -0,0 +1,23 @@ +package be.re.writand.data.repos + +import be.re.writand.data.local.db.ProjectsDao +import be.re.writand.data.local.models.Project +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ProjectsRepository @Inject constructor(private val dao: ProjectsDao): IProjectsRepository { + + override fun getAllProjects(): Flow> { + return dao.getAllProjects() + } + + override suspend fun addProject(project: Project) { + dao.insertProject(project) + } + + override suspend fun deleteProject(project: Project) { + dao.deleteProject(project) + } +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/di/.keep b/app/src/main/java/be/re/writand/di/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/be/re/writand/di/DatabaseModule.kt b/app/src/main/java/be/re/writand/di/DatabaseModule.kt new file mode 100644 index 0000000..1b321e2 --- /dev/null +++ b/app/src/main/java/be/re/writand/di/DatabaseModule.kt @@ -0,0 +1,29 @@ +package be.re.writand.di + +import android.content.Context +import be.re.writand.data.local.db.ProjectsDao +import be.re.writand.data.local.db.ProjectsDatabase +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** + * Module necessary to enable dependency injection for databases and DAO's. + */ +@InstallIn(SingletonComponent::class) +@Module +class DatabaseModule { + @Singleton + @Provides + fun provideProjectsDatabase(@ApplicationContext context: Context): ProjectsDatabase { + return ProjectsDatabase.getInstance(context) + } + @Provides + fun provideQuotesDao(projectsDatabase: ProjectsDatabase): ProjectsDao { + return projectsDatabase.projectsDao() + } + +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 29c5abd..343fae8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,13 @@ buildscript { ext { - compose_ui_version = '1.2.0' + kotlin_version = '1.9.0' + compose_ui_version = '1.6.8' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.2' apply false - id 'com.android.library' version '7.4.2' apply false - id 'org.jetbrains.kotlin.android' version '1.7.0' apply false + id 'com.android.application' version '8.5.1' apply false + id 'com.android.library' version '8.5.1' apply false + id 'org.jetbrains.kotlin.android' version '1.9.0' apply false + id 'com.google.dagger.hilt.android' version '2.44' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.21' } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3c5031e..f19c7b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,5 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 559eff6..bc9a016 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Jul 16 09:37:51 CEST 2024 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME