Skip to content

Commit

Permalink
implemented basic feature
Browse files Browse the repository at this point in the history
  • Loading branch information
anmolvermamm committed Feb 13, 2022
1 parent 8e27df1 commit 8737d41
Show file tree
Hide file tree
Showing 19 changed files with 412 additions and 36 deletions.
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
android:theme="@style/Theme.App.SplashScreenTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".root.MainActivity"
android:name=".root.OnboardingActivity"
android:exported="true"
android:theme="@style/Theme.App.SplashScreenTheme">
<intent-filter>
Expand All @@ -24,6 +24,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity-alias
android:name="leakcanary.internal.activity.LeakLauncherActivity"
android:exported="true"
Expand Down
21 changes: 21 additions & 0 deletions app/src/main/java/com/mutualmobile/praxis/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.mutualmobile.praxis.di

import android.app.Application
import android.content.Context
import dagger.Binds
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
@InstallIn(SingletonComponent::class) class AppModule {

@Provides
@Singleton
fun provideAppContext(@ApplicationContext application: Context): Context {
return application
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.praxis.feat.authentication.nav.authNavGraph
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
class OnboardingActivity : AppCompatActivity() {

@Inject
lateinit var composeNavigator: ComposeNavigator
Expand Down
31 changes: 31 additions & 0 deletions app/src/main/res/layout/view_random_photos.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical">

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/photoView"
android:layout_width="@dimen/random_photo_size"
android:layout_height="@dimen/random_photo_size"
android:background="@android:color/black"
android:padding="@dimen/padding_small" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/progressText"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />


<androidx.appcompat.widget.AppCompatButton
android:id="@+id/randomPhotoButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_small"
android:text="@string/random_photo">

</androidx.appcompat.widget.AppCompatButton>
</LinearLayout>
5 changes: 5 additions & 0 deletions app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="random_photo_size">160dp</dimen>
<dimen name="padding_small">8dp</dimen>
</resources>
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<resources>
<string name="title_activity_main">MainActivity</string>
<string name="random_photo">Random Photo</string>
<string name="except_file_not_found">Could not save the image, FileNotFound!</string>
<string name="downloaded_glue">Downloaded Size %d bytes</string>
<string name="downloading_glue">Downloading %d bytes</string>
<string name="except_no_network">No network available!</string>
<string name="except_generic">Some error occurred</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.mutualmobile.praxis.data

object AppConstants {
const val PIC_SUM_URL = "https://picsum.photos/800"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mutualmobile.praxis.data.injection

import com.mutualmobile.praxis.domain.repositories.PhotoFetchRepository
import com.mutualmobile.praxis.domain.usecases.FetchRandomPhotoUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
class RandomPhotosModule {

@Provides
@Singleton
fun provideFetchRandomPhotoUseCase(photoFetchRepository: PhotoFetchRepository) =
FetchRandomPhotoUseCase(photoFetchRepository)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.mutualmobile.praxis.data.injection

import com.mutualmobile.praxis.data.repositories.PicSumPhotoFetchRepositoryImpl
import com.mutualmobile.praxis.data.repositories.RandomFileServiceImpl
import com.mutualmobile.praxis.domain.repositories.PhotoFetchRepository
import com.mutualmobile.praxis.domain.repositories.RandomFileService
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
abstract class RandomPhotosRepositoryModule {

@Binds
abstract fun bindRandomFileService(randomFileServiceImpl: RandomFileServiceImpl): RandomFileService

@Binds
abstract fun bindPhotoFetchRepository(photoFetchRepositoryImpl: PicSumPhotoFetchRepositoryImpl): PhotoFetchRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.mutualmobile.praxis.data.repositories

import com.mutualmobile.praxis.domain.model.StreamingFile
import com.mutualmobile.praxis.domain.repositories.PhotoFetchListener
import com.mutualmobile.praxis.domain.repositories.PhotoFetchRepository
import com.mutualmobile.praxis.domain.repositories.RandomFileService
import com.mutualmobile.praxis.injection.dispatcher.CoroutineDispatcherProvider
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.features.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
import javax.inject.Inject

class PicSumPhotoFetchRepositoryImpl @Inject constructor(
private val coroutineContext: CoroutineDispatcherProvider,
private val networkClient: HttpClient,
private val fileCreationService: RandomFileService
) :
PhotoFetchRepository {
companion object {
private const val PIC_SUM_URL = "https://picsum.photos/800"
}

private var fileDownloadListener: PhotoFetchListener? = null

override fun setListener(listener: PhotoFetchListener) {
fileDownloadListener = listener
}

override fun removeListener(listener: PhotoFetchListener) {
fileDownloadListener = null
}

override suspend fun fetchPhoto(): Unit =
withContext(coroutineContext.io) {
try {
val file = fileCreationService.getTempFile()
val response: HttpResponse = responseWithListener(PIC_SUM_URL, file)
val bytes = response.receive<ByteArray>()
file.writeBytes(bytes)
notifyFileDownloaded(file)
} catch (ex: Exception) {
fileDownloadListener?.onFailed(ex)
}
}

private fun notifyFileDownloaded(file: File) {
fileDownloadListener?.onReceive(
StreamingFile(
file.length(),
file, isComplete = true
)
)
fileDownloadListener?.onComplete()
}

private suspend fun responseWithListener(
url: String,
file: File
): HttpResponse = networkClient.get(url) {
onDownload { bytesSentTotal, contentLength ->
Timber.d("${bytesSentTotal}/${contentLength}")
prepareCallback(bytesSentTotal, file)
}
}

private fun prepareCallback(
bytesSentTotal: Long,
file: File
) {
fileDownloadListener?.onReceive(
StreamingFile(
bytesSentTotal,
file, isComplete = false
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.mutualmobile.praxis.data.repositories

import android.content.Context
import com.mutualmobile.praxis.domain.repositories.RandomFileService
import java.io.File
import javax.inject.Inject

class RandomFileServiceImpl @Inject constructor(private val context: Context) : RandomFileService {
override fun getTempFile(): File {
return File(context.filesDir, "random")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mutualmobile.praxis.domain.model

import java.io.File

data class StreamingFile(
var progress: Long,
var file: File,
var isComplete: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mutualmobile.praxis.domain.repositories

import com.mutualmobile.praxis.domain.model.StreamingFile

interface PhotoFetchListener {
fun onReceive(streamingFile: StreamingFile)
fun onFailed(throwable: Throwable)
fun onComplete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mutualmobile.praxis.domain.repositories

interface PhotoFetchRepository {
suspend fun fetchPhoto()

fun setListener(listener: PhotoFetchListener)
fun removeListener(listener: PhotoFetchListener)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mutualmobile.praxis.domain.repositories

import java.io.File

interface RandomFileService {
fun getTempFile(): File
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.mutualmobile.praxis.domain.usecases

import com.mutualmobile.praxis.domain.model.StreamingFile
import com.mutualmobile.praxis.domain.repositories.PhotoFetchListener
import com.mutualmobile.praxis.domain.repositories.PhotoFetchRepository
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.onFailure
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlin.coroutines.cancellation.CancellationException

class FetchRandomPhotoUseCase(private val photoFetchRepository: PhotoFetchRepository) :
BaseUseCase< StreamingFile,String> {

override fun performStreaming(input: String?): Flow<StreamingFile> {
return callbackFlow {
val listener = photoFetchListener()
photoFetchRepository.setListener(listener)
photoFetchRepository.fetchPhoto()
awaitClose {
photoFetchRepository.removeListener(listener)
}
}
}

private fun ProducerScope<StreamingFile>.photoFetchListener() =
object : PhotoFetchListener {
override fun onReceive(streamingFile: StreamingFile) {
trySend(streamingFile).onFailure {
cancel(CancellationException("Download Error", it))
}
}

override fun onFailed(throwable: Throwable) {
cancel(CancellationException("Download Error", throwable))
}

override fun onComplete(){
channel.close()
}
}
}
Loading

0 comments on commit 8737d41

Please sign in to comment.