├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── gradient.xml │ │ │ │ ├── ic_user.xml │ │ │ │ ├── ic_error.xml │ │ │ │ ├── ic_search.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── menu │ │ │ │ └── menu_galler.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── unsplash_photo_load_state_footer.xml │ │ │ │ ├── item_unsplash_photo.xml │ │ │ │ ├── fragment_details.xml │ │ │ │ └── fragment_gallery.xml │ │ │ ├── navigation │ │ │ │ └── nav_graph.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── codinginflow │ │ │ │ └── imagesearchapp │ │ │ │ ├── api │ │ │ │ ├── UnsplashResponse.kt │ │ │ │ └── UnsplashApi.kt │ │ │ │ ├── ImageSearchApplication.kt │ │ │ │ ├── data │ │ │ │ ├── UnsplashRepository.kt │ │ │ │ ├── UnsplashPhoto.kt │ │ │ │ └── UnsplashPagingSource.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ └── ui │ │ │ │ ├── gallery │ │ │ │ ├── GalleryViewModel.kt │ │ │ │ ├── UnsplashPhotoLoadStateAdapter.kt │ │ │ │ ├── UnsplashPhotoAdapter.kt │ │ │ │ └── GalleryFragment.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── details │ │ │ │ └── DetailsFragment.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── codinginflow │ │ │ └── imagesearchapp │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── codinginflow │ │ └── imagesearchapp │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── .idea ├── .name ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── compiler.xml ├── misc.xml ├── deploymentTargetDropDown.xml ├── gradle.xml └── jarRepositories.xml ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Image Search App -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name = "Image Search App" -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rimshadpcs/Unsplash/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MVVM Image Search App with Architecture Components & Retrofit 2 | 3 | ## Tech stack 4 | 1. Retrofit 5 | 2. Paging3 6 | 3. Dagger Hilt 7 | 4. Navigation controller 8 | 5. Load State Listener 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/api/UnsplashResponse.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.api 2 | 3 | import com.codinginflow.imagesearchapp.data.UnsplashPhoto 4 | 5 | data class UnsplashResponse( 6 | val results: List 7 | ) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Aug 21 12:22:20 CEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/ImageSearchApplication.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class ImageSearchApplication : Application(){ 8 | 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Image Search App 3 | Retry 4 | cannot be loaded 5 | no results found 6 | results cannot be loaded 7 | search 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_galler.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/codinginflow/imagesearchapp/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_user.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codinginflow/imagesearchapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.codinginflow.imagesearchapp", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/data/UnsplashRepository.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.data 2 | 3 | import androidx.paging.Pager 4 | import androidx.paging.PagingConfig 5 | import androidx.paging.liveData 6 | import com.codinginflow.imagesearchapp.api.UnsplashApi 7 | import retrofit2.http.Query 8 | import javax.inject.Inject 9 | import javax.inject.Singleton 10 | 11 | @Singleton 12 | class UnsplashRepository @Inject constructor(private val unsplashApi: UnsplashApi) { 13 | 14 | fun getSearchResult(query: String) = 15 | Pager( 16 | config = PagingConfig( 17 | pageSize = 20, 18 | maxSize = 100, 19 | enablePlaceholders = false 20 | 21 | ), 22 | pagingSourceFactory = {UnsplashPagingSource(unsplashApi,query)} 23 | ).liveData 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/data/UnsplashPhoto.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.data 2 | 3 | import android.os.Parcelable 4 | import kotlinx.android.parcel.Parcelize 5 | 6 | @Parcelize 7 | data class UnsplashPhoto( 8 | val id: String, 9 | val description: String, 10 | val urls: UnsplashPhotoUrls, 11 | val user: UnsplashUser 12 | ) : Parcelable { 13 | @Parcelize 14 | data class UnsplashPhotoUrls( 15 | val raw: String, 16 | val full: String, 17 | val regular: String, 18 | val small: String, 19 | val thumb: String 20 | ) : Parcelable 21 | 22 | @Parcelize 23 | data class UnsplashUser( 24 | val name: String, 25 | val username: String 26 | ) : Parcelable { 27 | val attributeUrl get() = "https://unsplash.com/$username?utm_source=ImageSearchApp&utm_medium=referral" 28 | 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.di 2 | 3 | import com.codinginflow.imagesearchapp.api.UnsplashApi 4 | import dagger.Module 5 | import dagger.Provides 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.android.components.ApplicationComponent 8 | import retrofit2.Retrofit 9 | import retrofit2.converter.gson.GsonConverterFactory 10 | import javax.inject.Singleton 11 | 12 | @Module 13 | @InstallIn(ApplicationComponent::class) 14 | object AppModule { 15 | //creating an object 16 | @Provides 17 | @Singleton 18 | fun provideRetrofit(): Retrofit = 19 | Retrofit.Builder() 20 | .baseUrl(UnsplashApi.BASE_URL) 21 | .addConverterFactory(GsonConverterFactory.create()) 22 | .build() 23 | 24 | @Provides 25 | @Singleton 26 | fun UnsplashApi(retrofit: Retrofit): UnsplashApi = 27 | retrofit.create(UnsplashApi::class.java) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/api/UnsplashApi.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.api 2 | 3 | import com.codinginflow.imagesearchapp.BuildConfig 4 | import retrofit2.http.GET 5 | import retrofit2.http.Headers 6 | import retrofit2.http.Query 7 | 8 | interface UnsplashApi { 9 | //getting api key from gradle local properties 10 | companion object{ 11 | const val BASE_URL= "https://api.unsplash.com/" 12 | const val CLIENT_ID = BuildConfig.UNSPLASH_ACCESS_KEY 13 | } 14 | 15 | //this case the header is mandatory for a request since api demands it, here its and api version and acces key 16 | @Headers("Accept-Version: v1", "Authorization: Client-ID $CLIENT_ID") 17 | @GET("search/photos") 18 | suspend fun searchPhotos( 19 | @Query("query")query: String, 20 | @Query("page")page: Int, 21 | @Query("per_page")perPage: Int 22 | ): UnsplashResponse 23 | //:UnsplashResponse is what we get from this request which is one object of photo 24 | 25 | 26 | 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 15 | 16 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/ui/gallery/GalleryViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.ui.gallery 2 | 3 | import androidx.hilt.Assisted 4 | import androidx.hilt.lifecycle.ViewModelInject 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.SavedStateHandle 7 | import androidx.lifecycle.ViewModel 8 | import androidx.lifecycle.switchMap 9 | import androidx.lifecycle.viewModelScope 10 | import androidx.paging.LoadState 11 | import androidx.paging.cachedIn 12 | import com.codinginflow.imagesearchapp.data.UnsplashRepository 13 | import retrofit2.http.Query 14 | 15 | class GalleryViewModel @ViewModelInject constructor( 16 | private val repository: UnsplashRepository, 17 | @Assisted state: SavedStateHandle 18 | ) :ViewModel(){ 19 | private val currentQuery = state.getLiveData(CURRENT_QUERY,DEFAULT_QUERY) 20 | 21 | 22 | val photos = currentQuery.switchMap { queryString-> 23 | repository.getSearchResult(queryString).cachedIn(viewModelScope) 24 | } 25 | 26 | 27 | fun searchPhotos(query: String){ 28 | currentQuery.value = query 29 | } 30 | companion object{ 31 | private const val CURRENT_QUERY = "current_query" 32 | private const val DEFAULT_QUERY = "nature" 33 | } 34 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | unsplash_access_key = "SI9nGlDuo4kYfQdUWCQLKdUZDHc9F59bFIXJGCFWazU" -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.ui 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import androidx.navigation.NavController 6 | import androidx.navigation.fragment.NavHostFragment 7 | import androidx.navigation.fragment.findNavController 8 | import androidx.navigation.ui.AppBarConfiguration 9 | import androidx.navigation.ui.setupActionBarWithNavController 10 | import com.codinginflow.imagesearchapp.R 11 | import dagger.hilt.android.AndroidEntryPoint 12 | 13 | @AndroidEntryPoint 14 | class MainActivity : AppCompatActivity() { 15 | 16 | private lateinit var navController: NavController 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContentView(R.layout.activity_main) 21 | 22 | val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_main) as NavHostFragment 23 | navController = navHostFragment.findNavController() 24 | 25 | val appBarConfiguration = AppBarConfiguration(navController.graph) 26 | setupActionBarWithNavController(navController,appBarConfiguration) 27 | 28 | } 29 | 30 | override fun onSupportNavigateUp(): Boolean { 31 | return navController.navigateUp()|| super.onSupportNavigateUp() 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/codinginflow/imagesearchapp/data/UnsplashPagingSource.kt: -------------------------------------------------------------------------------- 1 | package com.codinginflow.imagesearchapp.data 2 | 3 | import androidx.paging.PagingSource 4 | import com.codinginflow.imagesearchapp.api.UnsplashApi 5 | import retrofit2.HttpException 6 | import retrofit2.http.Query 7 | import java.io.IOException 8 | 9 | private const val UNSPLASH_STARTING_PAGE_INDEX =1 10 | class UnsplashPagingSource( 11 | private val unsplashApi: UnsplashApi, 12 | private val query: String 13 | ): PagingSource() { 14 | 15 | override suspend fun load(params: LoadParams): LoadResult { 16 | //current page 17 | val position = params.key ?: UNSPLASH_STARTING_PAGE_INDEX 18 | 19 | return try { 20 | //api call better to call in a try catch 21 | val response = unsplashApi.searchPhotos(query, position, params.loadSize) 22 | val photos = response.results 23 | LoadResult.Page( 24 | data = photos, prevKey = if(position== UNSPLASH_STARTING_PAGE_INDEX) null else position-1, 25 | nextKey = if(photos.isEmpty()) null else position+1 26 | ) 27 | } 28 | catch (exception: IOException){ 29 | //for no internet connection 30 | LoadResult.Error(exception) 31 | } 32 | catch (exception: HttpException){ 33 | //for server errors 34 | LoadResult.Error(exception) 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/unsplash_photo_load_state_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 15 | 23 |