Skip to content

Commit

Permalink
feat: add cache for the thumbnails
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurChalard committed Dec 12, 2024
1 parent 594778f commit 8750eca
Show file tree
Hide file tree
Showing 21 changed files with 138 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package com.github.se.travelpouch.di
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityRepositoryFirebase
import com.github.se.travelpouch.model.authentication.AuthenticationService
import com.github.se.travelpouch.model.authentication.FirebaseAuthenticationService
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentRepositoryFirestore
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.events.EventRepository
import com.github.se.travelpouch.model.events.EventRepositoryFirebase
import com.github.se.travelpouch.model.notifications.NotificationRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,8 @@ class DocumentUpload {
Instrumentation.ActivityResult(
Activity.RESULT_OK, Intent().setData(Uri.fromFile(file))))
intending(hasAction(Intent.ACTION_OPEN_DOCUMENT_TREE))
.respondWith(
Instrumentation.ActivityResult(
Activity.RESULT_OK,
Intent().setData(Uri.EMPTY)))
.respondWith(
Instrumentation.ActivityResult(Activity.RESULT_OK, Intent().setData(Uri.EMPTY)))

// assert that login screen is displayed
composeTestRule.onNodeWithTag("appLogo").assertIsDisplayed()
Expand All @@ -166,7 +164,7 @@ class DocumentUpload {

// Skip onboarding
composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) {
composeTestRule.onNodeWithTag("OnboardingScreen", useUnmergedTree = true).isDisplayed()
composeTestRule.onNodeWithTag("OnboardingScreen", useUnmergedTree = true).isDisplayed()
}
composeTestRule.onNodeWithTag("SkipButton").performClick()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.documentfile.provider.DocumentFile
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.google.firebase.FirebaseApp
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
Expand Down Expand Up @@ -51,7 +52,7 @@ class DocumentsManagerTest {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.executeShellCommand("logcat -c")
`when`(mockDestinationFolder.createFile(any(), any())).thenReturn(null)
documentsManager.downloadFile(
documentsManager.getDocument(
"image/jpeg", "mountain.jpg", "hWwbmtbnfwX5yRhAwL3o", mockDestinationFolder)
verify(mockFirebaseStorage, never()).reference
val logs = device.executeShellCommand("logcat -d")
Expand Down Expand Up @@ -84,7 +85,7 @@ class DocumentsManagerTest {
taskSnapshot.result

DocumentsManager(contentResolver, storage)
.downloadFile("image/jpeg", "mountain.jpg", "hWwbmtbnfwX5yRhAwL3o", mockDestinationFolder)
.getDocument("image/jpeg", "mountain.jpg", "hWwbmtbnfwX5yRhAwL3o", mockDestinationFolder)
.join()
}
val downloadedData = Files.readAllBytes(tempFile.toPath())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SignInViewTest {
}
// Temporary solution
composeTestRule.waitForIdle()
//verify(mockNavigationActions).navigateTo(Screen.TRAVEL_LIST)
// verify(mockNavigationActions).navigateTo(Screen.TRAVEL_LIST)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.documents.DocumentContainer
import com.github.se.travelpouch.model.documents.DocumentFileFormat
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentVisibility
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.google.firebase.Timestamp
import com.google.firebase.firestore.DocumentReference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.core.net.toUri
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.documents.DocumentContainer
import com.github.se.travelpouch.model.documents.DocumentFileFormat
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentVisibility
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.travels.ListTravelViewModel
import com.github.se.travelpouch.model.travels.Location
import com.github.se.travelpouch.model.travels.Participant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import com.github.se.travelpouch.di.AppModule
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.documents.DocumentContainer
import com.github.se.travelpouch.model.documents.DocumentFileFormat
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentVisibility
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.google.firebase.FirebaseApp
import com.google.firebase.Timestamp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.core.app.ActivityOptionsCompat
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.ui.documents.StoreDocumentButton
import org.junit.Before
import org.junit.Rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.unit.dp
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityViewModel
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.events.EventRepository
import com.github.se.travelpouch.model.events.EventViewModel
import com.github.se.travelpouch.model.profile.ProfileModelView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.test.swipeRight
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityViewModel
import com.github.se.travelpouch.model.dashboard.CalendarViewModel
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.travels.ListTravelViewModel
import com.github.se.travelpouch.model.travels.TravelRepository
import org.junit.Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityViewModel
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.events.EventRepository
import com.github.se.travelpouch.model.events.EventViewModel
import com.github.se.travelpouch.model.notifications.Notification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onChildren
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityViewModel
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentViewModel
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.events.EventRepository
import com.github.se.travelpouch.model.events.EventViewModel
import com.github.se.travelpouch.model.notifications.Notification
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/java/com/github/se/travelpouch/di/AppModules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.R
import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityRepositoryFirebase
import com.github.se.travelpouch.model.authentication.AuthenticationService
import com.github.se.travelpouch.model.authentication.FirebaseAuthenticationService
import com.github.se.travelpouch.model.documents.DocumentRepository
import com.github.se.travelpouch.model.documents.DocumentRepositoryFirestore
import com.github.se.travelpouch.model.documents.DocumentsManager
import com.github.se.travelpouch.model.events.EventRepository
import com.github.se.travelpouch.model.events.EventRepositoryFirebase
import com.github.se.travelpouch.model.notifications.NotificationRepository
Expand Down Expand Up @@ -104,7 +105,11 @@ object AppModule {
storage: FirebaseStorage,
dataStore: DataStore<Preferences>
): DocumentsManager {
return DocumentsManager(context.contentResolver, storage, dataStore)
return DocumentsManager(
context.contentResolver,
storage,
dataStore,
context.getDir(context.getString(R.string.thumbs_dir_name), Context.MODE_PRIVATE))
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.functions.FirebaseFunctions
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageException

/** Interface for the DocumentRepository. */
interface DocumentRepository {
Expand All @@ -25,14 +24,6 @@ interface DocumentRepository {
onFailure: (Exception) -> Unit
)

fun getThumbnailUrl(
document: DocumentContainer,
width: Int,
onSuccess: (String) -> Unit,
onFailure: (Exception) -> Unit,
canFail: Boolean = true
)

fun uploadDocument(
travelId: String,
bytes: ByteArray,
Expand Down Expand Up @@ -156,48 +147,6 @@ class DocumentRepositoryFirestore(
}
}

/**
* Try to get the thumbnailUrl a document from the Firestore database.
*
* @param document The document to fetch the download URL for.
* @param width The width of the thumbnail.
* @param onSuccess Callback function to be called when the download URL is fetched successfully.
* @param onFailure Callback function to be called when an error occurs.
*/
override fun getThumbnailUrl(
document: DocumentContainer,
width: Int,
onSuccess: (String) -> Unit,
onFailure: (Exception) -> Unit,
canFail: Boolean
) {
storage
.getReference("${document.ref.id}-thumb-$width")
.downloadUrl
.addOnSuccessListener { uri -> onSuccess(uri.toString()) }
.addOnFailureListener { err ->
if (canFail &&
err is StorageException &&
err.errorCode == StorageException.ERROR_OBJECT_NOT_FOUND) {
Log.d(
"DocumentRepositoryFirestore",
"Thumbnail for document id=${document.ref.id},width=$width not found, generating one...",
err)
generateThumbnail(
document,
width,
{ getThumbnailUrl(document, width, onSuccess, onFailure, false) },
onFailure)
} else {
Log.e(
"DocumentRepositoryFirestore",
"Error getting thumbnail for document id=${document.ref.id},width=$width",
err)
onFailure(err)
}
}
}

override fun uploadDocument(
travelId: String,
bytes: ByteArray,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModel
import com.github.se.travelpouch.helper.DocumentsManager
import com.github.se.travelpouch.model.travels.TravelContainer
import dagger.hilt.android.lifecycle.HiltViewModel
import java.io.ByteArrayOutputStream
Expand Down Expand Up @@ -40,9 +39,9 @@ import kotlinx.coroutines.launch
open class DocumentViewModel
@Inject
constructor(
private val repository: DocumentRepository,
private val documentsManager: DocumentsManager,
private val dataStore: DataStore<Preferences>
private val repository: DocumentRepository,
private val documentsManager: DocumentsManager,
private val dataStore: DataStore<Preferences>
) : ViewModel() {

private val _isLoading = MutableStateFlow(false)
Expand All @@ -52,11 +51,12 @@ constructor(
private val _selectedDocument = MutableStateFlow<DocumentContainer?>(null)
var selectedDocument: StateFlow<DocumentContainer?> = _selectedDocument.asStateFlow()
private val _documentUri = mutableStateOf<Uri?>(null)
val documentUri: State<Uri?> get() = _documentUri
val documentUri: State<Uri?>
get() = _documentUri

private val _thumbnailUrls = mutableStateMapOf<String, String>()
val thumbnailUrls: Map<String, String>
get() = _thumbnailUrls
private val _thumbnailUris = mutableStateMapOf<String, Uri>()
val thumbnailUris: Map<String, Uri>
get() = _thumbnailUris

fun setIdTravel(travelId: String) {
repository.setIdTravel({ getDocuments() }, travelId)
Expand All @@ -82,15 +82,14 @@ constructor(
val ref = selectedDocument.value?.ref?.id

if (mimeType == null || title == null || ref == null) {
throw IllegalArgumentException("Some required fields are empty. Abort download")
throw IllegalArgumentException("Some required fields are empty. Abort download")
}

val result = documentsManager.downloadFile(mimeType, title, ref, documentFile)
val result = documentsManager.getDocument(mimeType, title, ref, documentFile)
result.invokeOnCompletion {
if (it != null) {
Log.e("DocumentViewModel", "Failed to download document", it)
}
else {
} else {
_documentUri.value = result.getCompleted()
Log.d("DocumentViewModel", "Document retrieved as ${result.getCompleted()}")
}
Expand Down Expand Up @@ -119,29 +118,38 @@ constructor(
if (uri == null) {
return@launch
}
dataStore.edit { preferences ->
preferences[SAVE_DOCUMENT_FOLDER] = uri.toString()
}
dataStore.edit { preferences -> preferences[SAVE_DOCUMENT_FOLDER] = uri.toString() }
}
}

fun getSaveDocumentFolder(): Deferred<Uri?> {
return CoroutineScope(Dispatchers.Default).async {
dataStore.data.map { parameters ->
parameters[SAVE_DOCUMENT_FOLDER]
}.first()?.let { Uri.parse(it) }
dataStore.data
.map { parameters -> parameters[SAVE_DOCUMENT_FOLDER] }
.first()
?.let { Uri.parse(it) }
}
}

/**
* If not already present, put the uri of a thumbnail of certain width in the active cache.
*
* @param document The document to get the thumbnail for.
* @param width The width of the requested thumbnail.
*/
@OptIn(ExperimentalCoroutinesApi::class)
fun getDocumentThumbnail(document: DocumentContainer, width: Int = 300) {
if (_thumbnailUrls.containsKey("${document.ref.id}-thumb-$width")) {
if (_thumbnailUris.containsKey("${document.ref.id}-$width")) {
return
}
repository.getThumbnailUrl(
document,
width,
onSuccess = { _thumbnailUrls["${document.ref.id}-thumb-$width"] = it },
onFailure = { Log.e("DocumentsViewModel", "Failed to get thumbnail uri", it) })
val deferredUri = documentsManager.getThumbnail(document.ref.id, width)
deferredUri.invokeOnCompletion {
if (it != null) {
Log.e("DocumentsViewModel", "Failed to get thumbnail", it)
} else {
_thumbnailUris["${document.ref.id}-$width"] = deferredUri.getCompleted()
}
}
}

fun uploadDocument(travelId: String, bytes: ByteArray, format: DocumentFileFormat) {
Expand Down
Loading

0 comments on commit 8750eca

Please sign in to comment.