Skip to content

Commit

Permalink
Rei 07 - Manuals module implementation (#14)
Browse files Browse the repository at this point in the history
* Add Room databese and create all the necessary implementations and methods

* add hilt viewModel injector to compose

* Fix room and add ui state

* Implement PDF view

* Fix issues and remove annecessary code

* Delete ManualService.kt

---------

Co-authored-by: Carlos Macaneta <[email protected]>
  • Loading branch information
NelsonChad and CarlosMacaneta authored Oct 17, 2024
1 parent bea5d05 commit a8c0f64
Show file tree
Hide file tree
Showing 22 changed files with 480 additions and 48 deletions.
9 changes: 8 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ navigationCompose = "2.8.2"
lifecycleRuntimeKtx = "2.8.6"
composeBom = "2024.04.01"
androidxHiltNavigation = "1.2.0"
room_version = "2.6.1"
bouquet-version = "1.1.2"
[libraries]

io-github-grizzi91 = { group = "io.github.grizzi91", name = "bouquet", version.ref = "bouquet-version" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigation" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room_version" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "gradle" }
kotlinPlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinSerialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
Expand Down Expand Up @@ -154,7 +162,6 @@ dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", versi
dagger-hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
dagger-hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
dagger-hilt-compiler-new = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigation" }
rx-java = { group = "io.reactivex.rxjava2", name = "rxjava", version.ref = "rxjava" }
rx-android = { group = "io.reactivex.rxjava2", name = "rxandroid", version.ref = "rxandroid" }
rx-binding-compat = { group = "com.jakewharton.rxbinding2", name = "rxbinding-appcompat-v7", version.ref = "rxbindings" }
Expand Down
21 changes: 21 additions & 0 deletions support-module/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
plugins {
id("com.android.library")
kotlin("android")
kotlin("kapt")
id("kotlinx-serialization")
id("kotlin-parcelize")
id("dagger.hilt.android.plugin")
alias(libs.plugins.kotlin.compose.compiler)
}

Expand Down Expand Up @@ -49,6 +53,19 @@ android {
}

dependencies {
// HILT
implementation(libs.androidx.compose.lifecycle)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.dagger.hilt.android)

// ROOM
implementation(libs.room.runtime)
annotationProcessor(libs.room.compiler)
implementation(libs.room.ktx)

// PDF VIEWER
implementation(libs.io.github.grizzi91)

implementation(project(":commons"))
implementation(libs.androidx.coreKtx)
implementation(libs.androidx.appcompat)
Expand All @@ -71,4 +88,8 @@ dependencies {
coreLibraryDesugaring(libs.desugar)
debugImplementation(libs.androidx.compose.uitooling)
debugImplementation(libs.test.ui.test.manifest)

kapt(libs.room.compiler)
kapt(libs.dagger.compiler)
kapt(libs.dagger.hilt.android.compiler)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import com.saudigitus.support_module.ui.Screen
import com.saudigitus.support_module.ui.manualScreen.ManualScreen
import com.saudigitus.support_module.ui.theme.SupportUiTheme
import com.saudigitus.support_module.utils.Constants
import dagger.hilt.android.AndroidEntryPoint
import org.dhis2.ui.theme.Dhis2Theme

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.saudigitus.support_module.data.local

import android.content.Context
import com.saudigitus.support_module.data.models.manuals.ManualItem
import kotlinx.coroutines.flow.Flow
import okhttp3.ResponseBody
import java.io.File

interface ManualsRepository {
fun getManualsDataStore(): Flow<List<ManualItem>>
suspend fun openManual(context: Context, url: String, fileName: String): File?
suspend fun downloadManualToLocal(
context: Context,
url: String,
fileName: String
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.saudigitus.support_module.data.local.database


import androidx.room.Database
import androidx.room.*
import com.saudigitus.support_module.data.models.manuals.ManualItem

@Database(
entities = [ManualItem::class],
version = 1
)
abstract class AppDatabase: RoomDatabase() {
abstract fun manualsDAO(): ManualsDao

companion object {
const val DB_NAME = "manuals_db"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.saudigitus.support_module.data.local.database

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.saudigitus.support_module.data.models.manuals.ManualItem

@Dao
interface ManualsDao {
@Query("SELECT * FROM manualItem")
fun getAll(): List<ManualItem>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun create(mediaDetails: ManualItem)

@Query("SELECT * FROM manualItem WHERE uid LIKE :id")
fun getDetailsById(id: String): ManualItem

@Delete
fun delete(manual: ManualItem)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.saudigitus.support_module.data.local.repository

import android.app.DownloadManager
import android.content.Context
import android.net.Uri
import android.os.Environment
import com.saudigitus.support_module.data.local.ManualsRepository
import com.saudigitus.support_module.data.local.database.ManualsDao
import com.saudigitus.support_module.data.models.manuals.Manual
import com.saudigitus.support_module.data.models.manuals.ManualItem
import com.saudigitus.support_module.utils.Constants
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.withContext
import org.hisp.dhis.android.core.D2
import java.io.File

class ManualsRepositoryImp(
private val manualsDAO: ManualsDao,
private val d2: D2
): ManualsRepository {
override fun getManualsDataStore(): Flow<List<ManualItem>> {
val dataStoreValue = d2.dataStoreModule().dataStore().byKey()
.eq(Constants.MEDIA_DATA_STORE_KEY).blockingGet()
.getOrNull(0)?.value()
val dataStore = dataStoreValue?.let { Manual.fromJson(it) }
return if (dataStore != null) {
flowOf(dataStore)
} else {
flowOf(emptyList())
}
}

override suspend fun downloadManualToLocal(context: Context, url: String, fileName: String): Unit
= withContext(Dispatchers.IO) {
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val uri = Uri.parse(url)
val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS),
"$fileName.pdf"
)
if (!file.exists()) {
val request = DownloadManager.Request(uri).apply {
setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
// Store the file in the app's private external files directory
setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOCUMENTS, "$fileName.pdf")
}
downloadManager.enqueue(request)
}
return@withContext
}


override suspend fun openManual(context: Context, url: String, fileName: String): File?
= withContext(Dispatchers.IO) {
// Get the file from the app's private storage
val file = File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "$fileName.pdf")
if (file.exists()) {
return@withContext file
}
return@withContext null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.saudigitus.support_module.data.models.manuals

import com.fasterxml.jackson.databind.ObjectMapper
import com.saudigitus.support_module.utils.Mapper.translateJsonToObject

class Manual {
private fun toJson(): String = translateJsonToObject().writeValueAsString(this)

companion object {
fun fromJson(json: String?): List<ManualItem>? = if (json != null) {
val mapper = ObjectMapper()

translateJsonToObject()
.readValue(
json,
mapper.typeFactory.constructCollectionType(
List::class.java,
ManualItem::class.java,
),
)
} else {
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.saudigitus.support_module.data.models.manuals


import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty

@JsonIgnoreProperties(ignoreUnknown = true)
@Entity
data class ManualItem(
@JsonProperty("uid")
@PrimaryKey val uid: String,

@JsonProperty("title")
@ColumnInfo(name = "title") val title: String,

@JsonProperty("subtitle")
@ColumnInfo(name = "subtitle") val subtitle: String?,

@JsonProperty("path")
@ColumnInfo(name = "path") val path: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.saudigitus.support_module.di

import android.app.Application
import androidx.room.Room
import com.saudigitus.support_module.data.local.ManualsRepository
import com.saudigitus.support_module.data.local.database.AppDatabase
import com.saudigitus.support_module.data.local.repository.ManualsRepositoryImp
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.hisp.dhis.android.core.D2
import org.hisp.dhis.android.core.D2Manager
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun manualsDatabase(app: Application): AppDatabase =
Room.databaseBuilder(
app,
AppDatabase::class.java,
AppDatabase.DB_NAME
).build()

/**
* Inject ManualsRepository
*/

@Provides
@Singleton fun providesManualRepository(appDatabase: AppDatabase, d2: D2): ManualsRepository {
return ManualsRepositoryImp(appDatabase.manualsDAO(), d2 = d2)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.saudigitus.support_module.ui

import com.rizzi.bouquet.ResourceType
import com.rizzi.bouquet.VerticalPdfReaderState
import com.saudigitus.support_module.data.models.manuals.ManualItem
import java.io.File

data class ManualsUiState(
val isLoading: Boolean = false,
val hasFileLoaded: Boolean = false,
val isDownloading: Boolean = false,
val manualItems : List<ManualItem> = emptyList(),
val pdf: File? = null,
val pdfVerticalReaderState: VerticalPdfReaderState? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ sealed class Screen(val route: String) {
object Menu : Screen("menu")
object Manuals : Screen("manuals")
object Support : Screen("support")
object ViewPdf : Screen("pdf_view/{path}")
}


Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ fun CustomCard(imageResId: Int, title: String, onClick: () -> Unit) {
.clickable { onClick() },
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(50.dp),


) {
Column(
modifier = Modifier.fillMaxSize(),
Expand Down
Loading

0 comments on commit a8c0f64

Please sign in to comment.