[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor.xml\n/.idea/assetWizardSettings.xml\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\n\n# AndroidStudio 3\n.idea/caches/build_file_checksums.ser\n.idea/modules.xml\n\n# AndroidStudio Patch\n!/gradle/wrapper/gradle-wrapper.jar\ngen-external-apklibs\noutput.json\n\n# Directory-based project format\n.idea/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# User-specific configurations\n.idea/caches/\n.idea/libraries/\n.idea/shelf/\n.idea/.name\n.idea/compiler.xml\n.idea/copyright/profiles_settings.xml\n.idea/encodings.xml\n.idea/misc.xml\n.idea/scopes/scope_settings.xml\n.idea/vcs.xml\n.idea/jsLibraryMappings.xml\n.idea/datasources.xml\n.idea/dataSources.ids\n.idea/sqlDataSources.xml\n.idea/dynamic.xml\n.idea/uiDesigner.xml\n\n# virtual machine crash logs\nhs_err_pid*\n\n# Generated files\nbin/\ngen/\nout/\n\n# Built application files\n*.apk\n*.ap_\n*.aab\n\n# Package Files\n*.jar\n*.nar\n*.zip\n*.tar.gz\n*.rar\n*.war\n*.ear\n\n# lint\nlint/intermediates/\nlint/generated/\nlint/outputs/\nlint/tmp/\n\n# OS-specific files\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n# Mac OS\n.AppleDouble\n.LSOverride\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk"
  },
  {
    "path": "README.md",
    "content": "# ArtGallery\nA sample android app that shows how to use ViewModels and Room together with RxJava & Dagger2, in Kotlin by Clean Architecture.\n\n### Implemented by Clean Architecture\nThe following diagram shows the structure of this project with 3 layers:\n- Presentation\n- Domain\n- Data\n\n<br>\n<p align=\"center\">\n  <img src=\"https://github.com/ZahraHeydari/ArtGallery/blob/master/diagram.png\" width=\"750\"/>\n</p>\n<br>\n\n### Communication between layers\n\n1. UI calls method from ViewModel.\n2. ViewModel executes Use case.\n3. Use case combines data from Album and Photo Repositories.\n4. Each Repository returns data from a Data Source (Cached or Remote).\n5. Information flows back to the UI where we display the list of posts.\n\n\n\n### Scenario\nUsed https://jsonplaceholder.typicode.com/ as a public api to generate fake data for testing\n\nAt a glance:\n\n- Created a list of Album\n- In the Item of each Album, showed Album name.\n- When user taps on Album, new page will be shown which includes list of photos.\n- when user taps on photo, show image bigger through transition.\n- Were Written tests to completely cover Exceptions/Expectations\n- And: \n    - Supported orientation change\n    - Supported offline mode"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'dagger.hilt.android.plugin'\n}\n\nandroid {\n    compileSdkVersion 33\n\n    defaultConfig {\n        applicationId \"com.android.artgallery\"\n        minSdkVersion 21\n        targetSdkVersion 33\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        multiDexEnabled = true\n        vectorDrawables.useSupportLibrary = true\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    testOptions {\n        unitTests {\n            includeAndroidResources = true\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n    implementation project(':data')\n    implementation project(':domain')\n    implementation project(':presentation')\n\n    implementation 'androidx.multidex:multidex:2.0.1'\n    implementation 'androidx.core:core-ktx:1.10.1'\n\n    implementation \"com.google.dagger:hilt-android:2.44\"\n    kapt \"com.google.dagger:hilt-android-compiler:2.44\"\n\n    testImplementation 'junit:junit:4.13.2'\n}"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.android.artgallery\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n    <application\n        android:name=\".MainApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\"com.android.presentation.gallery.GalleryActivity\"\n            android:exported=\"true\"\n            android:theme=\"@style/NoTheme\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/android/artgallery/MainApplication.kt",
    "content": "package com.android.artgallery\n\nimport android.app.Application\nimport android.content.Context\nimport androidx.multidex.MultiDex\nimport dagger.hilt.android.HiltAndroidApp\n\n@HiltAndroidApp\nclass MainApplication : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n        MultiDex.install(this)\n    }\n\n    override fun attachBaseContext(base: Context) {\n        super.attachBaseContext(base)\n        MultiDex.install(this)\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n        <item name=\"android:windowContentTransitions\" >true</item>\n        <item name=\"android:fontFamily\" >@font/atlantic</item>\n    </style>\n\n\n    <style name=\"NoTheme\" parent=\"AppTheme\">\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/android/artgallery/ExampleUnitTest.kt",
    "content": "package com.android.artgallery\n\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    ext.kotlin_version = '1.7.21'\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            url \"https://maven.google.com\"\n        }\n        maven { url 'https://jitpack.io' }\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.2.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven {\n            url \"https://maven.google.com\"\n        }\n        maven { url 'https://jitpack.io' }\n    }\n}\n\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "data/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'org.jetbrains.kotlin.android'\n    id 'dagger.hilt.android.plugin'\n    id 'kotlin-kapt'\n}\n\nandroid {\n    namespace 'com.android.data'\n    compileSdkVersion 33\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 33\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n    implementation project(':domain')\n\n    api 'com.squareup.retrofit2:retrofit:2.9.0'\n    api 'com.squareup.retrofit2:converter-gson:2.9.0'\n    api \"com.squareup.retrofit2:adapter-rxjava2:2.9.0\"\n    api 'com.squareup.okhttp3:logging-interceptor:4.8.1'\n    api 'com.squareup.okhttp3:okhttp:4.8.1'\n    api 'com.google.code.gson:gson:2.9.1'\n\n    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.20'\n\n    implementation 'androidx.room:room-runtime:2.5.2'\n    implementation 'androidx.room:room-rxjava2:2.5.2'\n    kapt 'androidx.room:room-compiler:2.5.2'\n\n    implementation \"com.google.dagger:hilt-android:2.44\"\n    kapt \"com.google.dagger:hilt-android-compiler:2.44\"\n    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'\n\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'io.mockk:mockk:1.12.0'\n}"
  },
  {
    "path": "data/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.android.data\"/>"
  },
  {
    "path": "data/src/main/java/com/android/data/di/DatabaseModule.kt",
    "content": "package com.android.data.di\n\nimport android.app.Application\nimport androidx.room.Room\nimport com.android.data.source.local.AppDatabase\nimport com.android.data.source.local.dao.PhotoDao\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.components.SingletonComponent\nimport javax.inject.Singleton\n\n@InstallIn(SingletonComponent::class)\n@Module\nclass DatabaseModule {\n\n    @Provides\n    @Singleton\n    internal fun provideAppDatabase(application: Application): com.android.data.source.local.AppDatabase {\n        return Room.databaseBuilder(\n            application,\n            com.android.data.source.local.AppDatabase::class.java,\n            com.android.data.source.local.AppDatabase.DB_NAME\n        ).allowMainThreadQueries().build()\n    }\n\n    @Provides\n    internal fun providePhotoDao(appDatabase: com.android.data.source.local.AppDatabase): com.android.data.source.local.dao.PhotoDao {\n        return appDatabase.photoDao\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/di/NetworkModule.kt",
    "content": "package com.android.data.di\n\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport android.net.NetworkInfo\nimport com.android.data.repository.AlbumRepositoryImp\nimport com.android.data.repository.PhotoRepositoryImp\nimport com.android.data.source.local.AppDatabase\nimport com.android.data.source.remote.RetrofitService\nimport com.android.domain.repository.AlbumRepository\nimport com.android.domain.repository.PhotoRepository\nimport com.google.gson.Gson\nimport dagger.Module\nimport dagger.Provides\nimport dagger.hilt.InstallIn\nimport dagger.hilt.android.qualifiers.ApplicationContext\nimport dagger.hilt.components.SingletonComponent\nimport okhttp3.Cache\nimport okhttp3.OkHttpClient\nimport okhttp3.logging.HttpLoggingInterceptor\nimport retrofit2.Retrofit\nimport retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory\nimport retrofit2.converter.gson.GsonConverterFactory\nimport java.util.concurrent.TimeUnit\nimport javax.inject.Singleton\n\nconst val BASE_URL = \"https://jsonplaceholder.typicode.com/\"\n\n@InstallIn(SingletonComponent::class)\n@Module\nclass NetworkModule {\n\n    @Provides\n    @Singleton\n    fun providesRetrofit(\n        gsonConverterFactory: GsonConverterFactory,\n        rxJava2CallAdapterFactory: RxJava2CallAdapterFactory,\n        okHttpClient: OkHttpClient\n    ): Retrofit {\n        return Retrofit.Builder()\n            .baseUrl(BASE_URL)\n            .addConverterFactory(gsonConverterFactory)\n            .addCallAdapterFactory(rxJava2CallAdapterFactory)\n            .client(okHttpClient)\n            .build()\n    }\n\n    @Provides\n    @Singleton\n    fun providesOkHttpClient(\n        @ApplicationContext context: Context\n    ): OkHttpClient {\n        val cacheSize = (5 * 1024 * 1024).toLong()\n        val mCache = Cache(context.cacheDir, cacheSize)\n        val interceptor = HttpLoggingInterceptor()\n        interceptor.level = HttpLoggingInterceptor.Level.BODY\n        val client = OkHttpClient.Builder()\n            .cache(mCache) // make your app offline-friendly without a database!\n            .connectTimeout(60, TimeUnit.SECONDS)\n            .writeTimeout(60, TimeUnit.SECONDS)\n            .readTimeout(60, TimeUnit.SECONDS)\n            .addNetworkInterceptor(interceptor)\n            .addInterceptor { chain ->\n                var request = chain.request()\n                /* If there is Internet, get the cache that was stored 5 seconds ago.\n                 * If the cache is older than 5 seconds, then discard it,\n                 * and indicate an error in fetching the response.\n                 * The 'max-age' attribute is responsible for this behavior.\n                 */\n                request = if (true) request.newBuilder() // make default to true till i figure out how to inject network status\n                    .header(\"Cache-Control\", \"public, max-age=\" + 5).build()\n                /*If there is no Internet, get the cache that was stored 7 days ago.\n                 * If the cache is older than 7 days, then discard it,\n                 * and indicate an error in fetching the response.\n                 * The 'max-stale' attribute is responsible for this behavior.\n                 * The 'only-if-cached' attribute indicates to not retrieve new data; fetch the cache only instead.\n                 */\n                else request.newBuilder().header(\n                    \"Cache-Control\",\n                    \"public, only-if-cached, max-stale=\" + 60 * 60 * 24 * 7\n                ).build()\n                chain.proceed(request)\n            }\n        return client.build()\n    }\n\n    @Provides\n    @Singleton\n    fun providesGson(): Gson {\n        return Gson()\n    }\n\n    @Provides\n    @Singleton\n    fun providesGsonConverterFactory(): GsonConverterFactory {\n        return GsonConverterFactory.create()\n    }\n\n    @Provides\n    @Singleton\n    fun providesRxJavaCallAdapterFactory(): RxJava2CallAdapterFactory {\n        return RxJava2CallAdapterFactory.create()\n    }\n\n    @Provides\n    @Singleton\n    fun provideIsNetworkAvailable(@ApplicationContext context: Context): Boolean {\n        val connectivityManager =\n            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n        val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo\n        return activeNetwork != null && activeNetwork.isConnected\n    }\n\n    @Singleton\n    @Provides\n    fun provideService(retrofit: Retrofit): RetrofitService {\n        return retrofit.create(RetrofitService::class.java)\n    }\n\n    @Singleton\n    @Provides\n    fun provideAlbumRepository(\n        retrofitService: RetrofitService\n    ): AlbumRepository {\n        return AlbumRepositoryImp(retrofitService)\n    }\n\n    @Singleton\n    @Provides\n    fun providePhotoRepository(\n        appDatabase: AppDatabase,\n        retrofitService: RetrofitService\n    ): PhotoRepository {\n        return PhotoRepositoryImp(appDatabase, retrofitService)\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/mapper/EntityMapper.kt",
    "content": "package com.android.data.mapper\n\nimport com.android.data.source.local.entity.PhotoEntity\nimport com.android.domain.model.Photo\n\nfun Photo.toEntity() = PhotoEntity(\n    id = id,\n    title = title,\n    url = url,\n    thumbnailUrl = thumbnailUrl\n)\n"
  },
  {
    "path": "data/src/main/java/com/android/data/repository/AlbumRepositoryImp.kt",
    "content": "package com.android.data.repository\n\nimport com.android.data.source.remote.RetrofitService\nimport com.android.domain.model.Album\nimport com.android.domain.repository.AlbumRepository\nimport io.reactivex.Single\n\n/**\n * This repository is responsible for\n * fetching data[Album] from server or db\n * */\nclass AlbumRepositoryImp(\n    private val retrofitService: RetrofitService\n) : AlbumRepository {\n\n    override fun getAlbums(): Single<List<Album>> {\n        return retrofitService.getAlbums()\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/repository/PhotoRepositoryImp.kt",
    "content": "package com.android.data.repository\n\nimport com.android.data.mapper.toEntity\nimport com.android.data.source.local.AppDatabase\nimport com.android.data.source.remote.RetrofitService\nimport com.android.domain.model.Photo\nimport com.android.domain.repository.PhotoRepository\nimport io.reactivex.Single\n\n/**\n * This repository is responsible for\n * fetching data [photo] from server or db\n * */\nclass PhotoRepositoryImp(\n    private val database: AppDatabase,\n    private val retrofitService: RetrofitService\n) : PhotoRepository {\n\n    override fun isFavorite(photoId: Long): Boolean {\n        return database.photoDao.loadOneByPhotoId(photoId) != null\n    }\n\n    override fun deletePhoto(photo: Photo) {\n        database.photoDao.delete(photo.toEntity())\n    }\n\n    override fun addPhoto(photo: Photo) {\n        database.photoDao.insert(photo.toEntity())\n    }\n\n    override fun getPhotoDetail(photoId: Long?): Single<Photo> {\n        return retrofitService.getPhotoDetail(photoId!!)\n    }\n\n    override fun getPhotos(albumId: Long?): Single<List<Photo>> {\n        return retrofitService.getPhotos(albumId!!)\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/source/local/AppDatabase.kt",
    "content": "package com.android.data.source.local\n\nimport androidx.room.Database\nimport androidx.room.RoomDatabase\nimport com.android.data.source.local.dao.PhotoDao\nimport com.android.data.source.local.entity.PhotoEntity\n\n/**\n * To manage data items that can be accessed, updated\n * & maintain relationships between them\n *\n */\n@Database(entities = [PhotoEntity::class], version = 1, exportSchema = false)\nabstract class AppDatabase : RoomDatabase() {\n\n    abstract val photoDao: PhotoDao\n\n    companion object {\n        const val DB_NAME = \"ArtGalleryDatabase.db\"\n    }\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/source/local/dao/PhotoDao.kt",
    "content": "package com.android.data.source.local.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport androidx.room.Update\nimport com.android.data.source.local.entity.PhotoEntity\n\n/**\n * it provides access to [Photo] underlying database\n * */\n@Dao\ninterface PhotoDao {\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(photo: PhotoEntity): Long\n\n    @Query(\"SELECT * FROM Photo\")\n    fun loadAll(): MutableList<PhotoEntity>\n\n    @Delete\n    fun delete(photo: PhotoEntity)\n\n    @Query(\"DELETE FROM Photo\")\n    fun deleteAll()\n\n    @Query(\"SELECT * FROM Photo where id = :photoId\")\n    fun loadOneByPhotoId(photoId: Long): PhotoEntity?\n\n    @Query(\"SELECT * FROM Photo where title = :photoTitle\")\n    fun loadOneByPhotoTitle(photoTitle: String): PhotoEntity?\n\n    @Update\n    fun update(photo: PhotoEntity)\n}\n"
  },
  {
    "path": "data/src/main/java/com/android/data/source/local/entity/PhotoEntity.kt",
    "content": "package com.android.data.source.local.entity\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity(tableName = \"Photo\")\ndata class PhotoEntity(\n    @PrimaryKey var id: Long,\n    var title: String,\n    val url: String,\n    val thumbnailUrl: String?\n)\n"
  },
  {
    "path": "data/src/main/java/com/android/data/source/remote/RetrofitService.kt",
    "content": "package com.android.data.source.remote\n\nimport com.android.domain.model.Album\nimport com.android.domain.model.Photo\nimport io.reactivex.Single\nimport retrofit2.http.GET\nimport retrofit2.http.Path\n\ninterface RetrofitService {\n\n    @GET(\"albums/\")\n    fun getAlbums(): Single<List<Album>>\n\n    @GET(\"albums/{id}/photos\")\n    fun getPhotos(@Path(\"id\") id: Long): Single<List<Photo>>\n\n    @GET(\"photos/{id}\")\n    fun getPhotoDetail(@Path(\"id\") id: Long): Single<Photo>\n}\n"
  },
  {
    "path": "data/src/test/java/com/android/data/TestUtil.kt",
    "content": "package com.android.data\n\nimport com.android.data.source.local.entity.PhotoEntity\n\nobject TestUtil {\n\n    fun createPhoto(id: Long) = PhotoEntity(\n        id = id,\n        title = \"\",\n        url = \"\",\n        thumbnailUrl = \"\"\n    )\n\n    fun makePhotoList(size: Int): MutableList<PhotoEntity> {\n        val list = ArrayList<PhotoEntity>(size)\n        list.forEach {\n            it.title = \"Photo ${list.indexOf(it)}\"\n            it.id = (list.indexOf(it) + 1).toLong()\n            list.add(it)\n        }\n        return list\n    }\n}\n"
  },
  {
    "path": "data/src/test/java/com/android/data/source/local/dao/PhotoDaoTest.kt",
    "content": "package com.android.data.source.local.dao\n\nimport com.android.data.TestUtil\nimport com.android.data.source.local.entity.PhotoEntity\nimport io.mockk.every\nimport io.mockk.mockk\nimport org.hamcrest.CoreMatchers\nimport org.hamcrest.MatcherAssert\nimport org.junit.Assert\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.JUnit4\n\n@RunWith(JUnit4::class)\nclass PhotoDaoTest {\n\n    private var photoDao = mockk<PhotoDao>()\n\n    @Test\n    fun isPhotoListEmpty() {\n        every { photoDao.loadAll() } returns mutableListOf()\n        Assert.assertEquals(0, photoDao.loadAll().size)\n    }\n\n    @Test\n    fun insertPhoto() {\n        val photoEntity: PhotoEntity = TestUtil.createPhoto(7)\n\n        every { photoDao.insert(photoEntity) } returns 1L\n        val insertedPhoto = photoDao.insert(photoEntity)\n\n        Assert.assertNotNull(insertedPhoto)\n    }\n\n    @Test\n    fun insertPhotoAndLoadByTitle() {\n        val photoTitle = \"Art\"\n        val photoEntity: PhotoEntity = TestUtil.createPhoto(1).apply {\n            title = photoTitle\n        }\n        every { photoDao.insert(photoEntity) } returns 1L\n\n        every { photoDao.loadOneByPhotoTitle(photoTitle) } returns photoEntity\n        val photoLoadedByTitle = photoDao.loadOneByPhotoTitle(photoTitle)\n\n        MatcherAssert.assertThat(photoLoadedByTitle, CoreMatchers.equalTo(photoEntity))\n    }\n\n    @Test\n    fun retrievesPhotos() {\n        val photoEntities = TestUtil.makePhotoList(5)\n        photoEntities.forEach { photoDao.insert(it) }\n\n        every { photoDao.loadAll() } returns mutableListOf()\n        val loadedPhotos = photoDao.loadAll()\n\n        Assert.assertEquals(photoEntities, loadedPhotos)\n    }\n\n    @Test\n    fun deletePhoto() {\n        val photoId = 8L\n        val photoEntity = TestUtil.createPhoto(photoId)\n\n        every { photoDao.delete(photoEntity) } returns Unit\n        photoDao.delete(photoEntity)\n\n        every { photoDao.loadOneByPhotoId(photoId) } returns null\n        val loadOneByPhotoId = photoDao.loadOneByPhotoId(photoId)\n\n        Assert.assertNull(loadOneByPhotoId)\n    }\n\n    @Test\n    fun deleteAllPhotos() {\n        every { photoDao.deleteAll() } returns Unit\n        photoDao.deleteAll()\n\n        every { photoDao.loadAll() } returns mutableListOf()\n        val loadedAllPhotos = photoDao.loadAll()\n\n        assert(loadedAllPhotos.isEmpty())\n    }\n}"
  },
  {
    "path": "domain/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'org.jetbrains.kotlin.android'\n    id 'dagger.hilt.android.plugin'\n    id 'kotlin-kapt'\n}\n\nandroid {\n    namespace 'com.android.domain'\n    compileSdkVersion 33\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 33\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    \n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n    api(platform(\"org.jetbrains.kotlin:kotlin-bom:1.8.0\"))\n    api 'androidx.appcompat:appcompat:1.6.1'\n\n    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.20'\n    implementation \"com.squareup.retrofit2:adapter-rxjava2:2.9.0\"\n\n    implementation \"com.google.dagger:hilt-android:2.44\"\n    kapt \"com.google.dagger:hilt-android-compiler:2.44\"\n    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'\n\n    testImplementation 'junit:junit:4.13.2'\n}"
  },
  {
    "path": "domain/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.android.domain\"/>"
  },
  {
    "path": "domain/src/main/java/com/android/domain/model/Album.kt",
    "content": "package com.android.domain.model\n\ndata class Album(var userId: Long, var id: Long, var title: String)\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/model/Photo.kt",
    "content": "package com.android.domain.model\n\ndata class Photo(\n    var id: Long,\n    var title: String,\n    val url: String,\n    val thumbnailUrl: String?\n) {\n    fun setName(text: String) {\n        title = text\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/repository/AlbumRepository.kt",
    "content": "package com.android.domain.repository\n\nimport com.android.domain.model.Album\nimport io.reactivex.Single\n\n/**\n * To make an interaction between [AlbumRepositoryImp] & [GetAlbumsUseCase]\n * */\ninterface AlbumRepository {\n    fun getAlbums(): Single<List<Album>>\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/repository/PhotoRepository.kt",
    "content": "package com.android.domain.repository\n\nimport com.android.domain.model.Photo\nimport io.reactivex.Single\n\n/**\n * To make an interaction between [PhotoRepositoryImp] & [GetPhotosUseCase]\n * */\ninterface PhotoRepository {\n    fun getPhotos(albumId: Long?): Single<List<Photo>>\n    fun getPhotoDetail(photoId: Long?): Single<Photo>\n    fun deletePhoto(photo: Photo)\n    fun addPhoto(photo: Photo)\n    fun isFavorite(photoId: Long): Boolean\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/usecase/GetAlbumsUseCase.kt",
    "content": "package com.android.domain.usecase\n\nimport com.android.domain.model.Album\nimport com.android.domain.repository.AlbumRepository\nimport com.android.domain.usecase.base.SingleUseCase\nimport io.reactivex.Single\nimport javax.inject.Inject\n\n/**\n * An interactor that calls the actual implementation of [AlbumViewModel](which is injected by DI)\n * it handles the response that returns data &\n * contains a list of actions, event steps\n */\nclass GetAlbumsUseCase @Inject constructor(\n    private val repository: AlbumRepository\n) : SingleUseCase<List<Album>>() {\n\n    override fun buildUseCaseSingle(): Single<List<Album>> {\n        return repository.getAlbums()\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/usecase/GetPhotoDetailUseCase.kt",
    "content": "package com.android.domain.usecase\n\nimport com.android.domain.model.Photo\nimport com.android.domain.repository.PhotoRepository\nimport com.android.domain.usecase.base.SingleUseCase\nimport io.reactivex.Single\nimport javax.inject.Inject\n\n/**\n * An interactor that calls the actual implementation of [PhotoViewModel](which is injected by DI)\n * it handles the response that returns data & contains a list of actions, event steps\n */\nclass GetPhotoDetailUseCase @Inject constructor(\n    private val repository: PhotoRepository\n) : SingleUseCase<Photo>() {\n\n    private var photoId: Long? = null\n\n    fun savePhotoId(id: Long) {\n        photoId = id\n    }\n\n    override fun buildUseCaseSingle(): Single<Photo> {\n        return repository.getPhotoDetail(photoId)\n    }\n\n    fun deleteAsFavorite(photo: Photo) {\n        repository.deletePhoto(photo)\n    }\n\n    fun addAsFavorite(photo: Photo) {\n        repository.addPhoto(photo)\n    }\n\n    fun isFavorite(photoId: Long): Boolean {\n        return repository.isFavorite(photoId)\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/usecase/GetPhotosUseCase.kt",
    "content": "package com.android.domain.usecase\n\nimport com.android.domain.model.Photo\nimport com.android.domain.repository.PhotoRepository\nimport com.android.domain.usecase.base.SingleUseCase\nimport io.reactivex.Single\nimport javax.inject.Inject\n\n/**\n * An interactor that calls the actual implementation of [PhotosViewModel](which is injected by DI)\n * it handles the response that returns data &\n * contains a list of actions, event steps\n */\nclass GetPhotosUseCase @Inject constructor(\n    private val repository: PhotoRepository\n) : SingleUseCase<List<Photo>>() {\n\n    private var albumId: Long? = null\n\n    fun saveAlbumId(id: Long) {\n        albumId = id\n    }\n\n    override fun buildUseCaseSingle(): Single<List<Photo>> {\n        return repository.getPhotos(albumId)\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/usecase/base/SingleUseCase.kt",
    "content": "package com.android.domain.usecase.base\n\nimport io.reactivex.Single\nimport io.reactivex.android.schedulers.AndroidSchedulers\nimport io.reactivex.schedulers.Schedulers\n\n/**\n * This abstract class is shared among several closely related UseCase classes\n * that classes that extend this abstract class to use common methods & fields\n **/\nabstract class SingleUseCase<T> : UseCase() {\n\n    internal abstract fun buildUseCaseSingle(): Single<T>\n\n    fun execute(\n        onSuccess: ((t: T) -> Unit),\n        onError: ((t: Throwable) -> Unit),\n        onFinished: () -> Unit = {}\n    ) {\n        disposeLast()\n        lastDisposable = buildUseCaseSingle()\n            .subscribeOn(Schedulers.io())\n            .observeOn(AndroidSchedulers.mainThread())\n            .doAfterTerminate(onFinished)\n            .subscribe(onSuccess, onError)\n\n        lastDisposable?.let {\n            compositeDisposable.add(it)\n        }\n    }\n}\n"
  },
  {
    "path": "domain/src/main/java/com/android/domain/usecase/base/UseCase.kt",
    "content": "package com.android.domain.usecase.base\n\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.disposables.Disposable\n\n/**\n * This class is extended by SingleUseCase classes\n * to use common methods & fields\n **/\nabstract class UseCase {\n\n    protected var lastDisposable: Disposable? = null\n    protected val compositeDisposable = CompositeDisposable()\n\n    fun disposeLast() {\n        lastDisposable?.let {\n            if (!it.isDisposed) {\n                it.dispose()\n            }\n        }\n    }\n\n    fun dispose() {\n        compositeDisposable.clear()\n    }\n}\n"
  },
  {
    "path": "domain/src/test/java/com/android/domain/ExampleUnitTest.kt",
    "content": "package com.android.domain\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jun 23 18:13:22 IRDT 2023\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.3.3-bin.zip\ndistributionPath=wrapper/dists\nzipStorePath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nandroid.useAndroidX=true\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\nandroid.enableJetifier=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "presentation/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'org.jetbrains.kotlin.android'\n    id 'dagger.hilt.android.plugin'\n    id 'kotlin-kapt'\n}\n\nandroid {\n    namespace 'com.android.presentation'\n    compileSdk 33\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 33\n    }\n\n    buildFeatures {\n        viewBinding true\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n}\n\ndependencies {\n    implementation project(':domain')\n\n    implementation 'androidx.core:core-ktx:1.10.1'\n    implementation 'com.google.android.material:material:1.9.0'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n\n    implementation \"androidx.fragment:fragment-ktx:1.6.0\"\n\n    implementation \"io.coil-kt:coil:0.7.0\"\n\n    implementation \"com.google.dagger:hilt-android:2.44\"\n    kapt \"com.google.dagger:hilt-android-compiler:2.44\"\n    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'\n\n    testImplementation 'junit:junit:4.13.2'\n}"
  },
  {
    "path": "presentation/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.android.presentation\"/>"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/Extensions.kt",
    "content": "package com.android.artgallery.presentation\n\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.ProgressBar\nimport coil.api.load\nimport coil.decode.DataSource\nimport coil.request.Request\n\nfun ImageView.loadImage(url: String?) = this.load(url)\n\nfun ImageView.loadImage(url: String, progressBar: ProgressBar) = this.load(url) {\n    crossfade(true)\n    listener(object : Request.Listener {\n        override fun onSuccess(data: Any, source: DataSource) {\n            super.onSuccess(data, source)\n            progressBar.visibility = View.GONE\n        }\n    })\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/album/AlbumsAdapter.kt",
    "content": "package com.android.presentation.album\n\nimport android.view.LayoutInflater\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.android.domain.model.Album\nimport com.android.presentation.album.AlbumsAdapter.AlbumViewHolder\nimport com.android.presentation.databinding.HolderAlbumBinding\n\n/**\n * This class is responsible for converting each data entry [Album]\n * into [AlbumViewHolder] that can then be added to the AdapterView.\n *\n */\ninternal class AlbumsAdapter(val onAlbumClick: (Album) -> Unit) :\n    RecyclerView.Adapter<RecyclerView.ViewHolder>() {\n\n    private val albums: MutableList<Album> = ArrayList()\n\n    /*\n     * This method is called right when adapter is created &\n     * is used to initialize ViewHolders\n     * */\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {\n        val holderAlbumBinding = HolderAlbumBinding.inflate(\n            LayoutInflater.from(parent.context), parent, false\n        )\n        return AlbumViewHolder(holderAlbumBinding)\n    }\n\n    /* It is called for each ViewHolder to bind it to the adapter &\n     * This is where we pass data to ViewHolder\n     * */\n    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {\n        (holder as AlbumViewHolder).onBind(getItem(position))\n    }\n\n    private fun getItem(position: Int): Album = albums[position]\n\n    /*\n     * This method returns the size of collection that contains the items we want to display\n     * */\n    override fun getItemCount() = albums.size\n\n    fun addData(list: List<Album>) {\n        this.albums.clear()\n        this.albums.addAll(list)\n        notifyDataSetChanged()\n    }\n\n    inner class AlbumViewHolder(private val binding: HolderAlbumBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n\n        fun onBind(album: Album) {\n            binding.holderAlbumTextView.text = album.title\n\n            itemView.setOnClickListener {\n                onAlbumClick.invoke(album)\n            }\n        }\n    }\n}"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/album/AlbumsFragment.kt",
    "content": "package com.android.presentation.album\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport com.android.presentation.R\nimport com.android.presentation.databinding.FragmentAlbumsBinding\nimport com.android.presentation.photo.PhotosFragment\nimport dagger.hilt.android.AndroidEntryPoint\n\n@AndroidEntryPoint\nclass AlbumsFragment : Fragment() {\n\n    private lateinit var binding: FragmentAlbumsBinding\n    private var adapter: AlbumsAdapter? = null\n    private val viewModel: AlbumsViewModel by viewModels()\n\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?\n    ): View {\n        binding = FragmentAlbumsBinding.inflate(inflater, container, false)\n        adapter = AlbumsAdapter { navigateToPhotosPage(it) }\n        binding.albumsRecyclerView.adapter = adapter\n        viewModel.loadAlbums()\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        with(viewModel) {\n            isLoad.observe(viewLifecycleOwner) {\n                it?.let { visibility ->\n                    binding.albumsProgressBar.visibility =\n                        if (visibility) View.GONE else View.VISIBLE\n                }\n            }\n\n            albumsReceivedLiveData.observe(viewLifecycleOwner) {\n                it?.let {\n                    adapter?.addData(it)\n                }\n            }\n        }\n    }\n\n    private fun navigateToPhotosPage(album: com.android.domain.model.Album) {\n        activity?.supportFragmentManager?.beginTransaction()?.replace(\n            R.id.gallery_container,\n            PhotosFragment.newInstance(album.id),\n            PhotosFragment.FRAGMENT_NAME\n        )?.addToBackStack(PhotosFragment.FRAGMENT_NAME)?.commitAllowingStateLoss()\n    }\n\n    override fun onDetach() {\n        super.onDetach()\n        adapter = null\n    }\n\n    companion object {\n        val FRAGMENT_NAME: String = AlbumsFragment::class.java.name\n\n        @JvmStatic\n        fun newInstance() = AlbumsFragment()\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/album/AlbumsViewModel.kt",
    "content": "package com.android.presentation.album\n\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport com.android.domain.model.Album\nimport com.android.domain.usecase.GetAlbumsUseCase\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport javax.inject.Inject\n\n/**To store & manage UI-related data in a lifecycle conscious way!\n * this class allows data to survive configuration changes such as screen rotation\n * by interacting with [GetAlbumsUseCase]\n *\n * */\n@HiltViewModel\nclass AlbumsViewModel @Inject constructor(private val getAlbumListUseCase: com.android.domain.usecase.GetAlbumsUseCase) :\n    ViewModel() {\n\n    val albumsReceivedLiveData = MutableLiveData<List<com.android.domain.model.Album>>()\n    val isLoad = MutableLiveData<Boolean>()\n    val albumData = MutableLiveData<com.android.domain.model.Album>()\n\n    init {\n        isLoad.value = false\n    }\n\n    val album: com.android.domain.model.Album? get() = albumData.value\n\n    fun set(album: com.android.domain.model.Album) = run { albumData.value = album }\n\n    fun loadAlbums() {\n        getAlbumListUseCase.execute(\n            onSuccess = {\n                isLoad.value = true\n                albumsReceivedLiveData.value = it\n            },\n            onError = {\n                it.printStackTrace()\n            }\n        )\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/detailphoto/PhotoDetailFragment.kt",
    "content": "package com.android.presentation.detailphoto\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport com.android.artgallery.presentation.loadImage\nimport com.android.presentation.R\nimport com.android.presentation.databinding.FragmentPhotoDetailBinding\nimport dagger.hilt.android.AndroidEntryPoint\n\n@AndroidEntryPoint\nclass PhotoDetailFragment : Fragment() {\n\n    private lateinit var binding: FragmentPhotoDetailBinding\n    private val viewModel: PhotoDetailViewModel by viewModels()\n\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?\n    ): View {\n        binding = FragmentPhotoDetailBinding.inflate(layoutInflater, container, false)\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        val photoId = arguments?.getLong(KEY_PHOTO_ID) ?: return\n        with(viewModel) {\n            getDetail(photoId)\n            checkFavoriteStatus(photoId)\n\n            photoData.observe(viewLifecycleOwner) {\n                binding.detailTitleTextView.text = it?.title\n                binding.detailToolbarImageView.loadImage(it?.url)\n            }\n\n            isFavorite.observe(viewLifecycleOwner) {\n                it?.let {\n                    binding.detailFab.setImageResource(\n                        if (it) R.drawable.ic_star_full_vector else R.drawable.ic_star_empty_white_vector\n                    )\n                }\n            }\n\n            binding.detailFab.setOnClickListener {\n                updateFavoriteStatus()\n            }\n        }\n    }\n\n    companion object {\n        val FRAGMENT_NAME: String = PhotoDetailFragment::class.java.name\n        private const val KEY_PHOTO_ID = \"KEY_PHOTO_ID\"\n\n        fun newInstance(photoId: Long) = PhotoDetailFragment().apply {\n            arguments = Bundle().apply {\n                putLong(KEY_PHOTO_ID, photoId)\n            }\n        }\n    }\n}"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/detailphoto/PhotoDetailViewModel.kt",
    "content": "package com.android.presentation.detailphoto\n\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport com.android.domain.model.Photo\nimport com.android.domain.usecase.GetPhotoDetailUseCase\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport javax.inject.Inject\n\n/**\n * A helper class for the UI controller that is responsible for\n * preparing data for [PhotoDetailFragment]\n *\n * @author ZARA\n * */\n@HiltViewModel\nclass PhotoDetailViewModel @Inject constructor(\n    private val getPhotoDetailUseCase: com.android.domain.usecase.GetPhotoDetailUseCase\n) : ViewModel() {\n\n    val photoData = MutableLiveData<com.android.domain.model.Photo>()\n    val isLoad = MutableLiveData<Boolean>()\n    val isFavorite = MutableLiveData<Boolean>()\n\n    init {\n        isLoad.value = false\n    }\n\n    fun getDetail(id: Long?) {\n        if (id == null) return\n        getPhotoDetailUseCase.savePhotoId(id)\n        getPhotoDetailUseCase.execute(\n            onSuccess = {\n                isLoad.value = true\n                photoData.value = it\n            },\n            onError = {\n                it.printStackTrace()\n            }\n        )\n    }\n\n    fun updateFavoriteStatus() {\n        photoData.value?.let { photo ->\n            if (isFavorite.value == true) {\n                getPhotoDetailUseCase.deleteAsFavorite(photo)\n            } else {\n                getPhotoDetailUseCase.addAsFavorite(photo)\n            }\n            isFavorite.value?.let {\n                isFavorite.value = !it\n            }\n        }\n    }\n\n    fun checkFavoriteStatus(photoId: Long) {\n        isFavorite.value = getPhotoDetailUseCase.isFavorite(photoId)\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/gallery/GalleryActivity.kt",
    "content": "package com.android.presentation.gallery\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport com.android.presentation.R\nimport com.android.presentation.album.AlbumsFragment\nimport com.android.presentation.databinding.ActivityGalleryBinding\nimport dagger.hilt.android.AndroidEntryPoint\n\n@AndroidEntryPoint\nclass GalleryActivity : AppCompatActivity() {\n\n    private lateinit var binding: ActivityGalleryBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        binding = ActivityGalleryBinding.inflate(layoutInflater)\n        setContentView(binding.root)\n\n        if (savedInstanceState == null) {\n            supportFragmentManager.beginTransaction().replace(\n                R.id.gallery_container, AlbumsFragment.newInstance(), AlbumsFragment.FRAGMENT_NAME\n            ).commitAllowingStateLoss()\n        }\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/photo/PhotosAdapter.kt",
    "content": "package com.android.presentation.photo\n\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport com.android.domain.model.Photo\nimport com.android.artgallery.presentation.loadImage\nimport com.android.presentation.databinding.HolderPhotoBinding\nimport com.android.presentation.photo.PhotosAdapter.PhotoViewHolder\n\n/**\n * [android.support.v7.widget.RecyclerView.Adapter] to adapt\n * [Photo] items into [RecyclerView] via [PhotoViewHolder]\n *\n */\ninternal class PhotosAdapter(val onPhotoClick: (Long) -> Unit) :\n    RecyclerView.Adapter<RecyclerView.ViewHolder>() {\n\n    private val photos: MutableList<Photo> = ArrayList()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {\n        val holderPhotoBinding = HolderPhotoBinding.inflate(\n            LayoutInflater.from(parent.context), parent, false\n        )\n        return PhotoViewHolder(holderPhotoBinding)\n    }\n\n    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {\n        (holder as PhotoViewHolder).onBind(getItem(position))\n    }\n\n    private fun getItem(position: Int): Photo = photos[position]\n\n    override fun getItemCount(): Int = photos.size\n\n    fun addData(list: List<Photo>) {\n        this.photos.clear()\n        this.photos.addAll(list)\n        notifyDataSetChanged()\n    }\n\n    inner class PhotoViewHolder(private val binding: HolderPhotoBinding) :\n        RecyclerView.ViewHolder(binding.root) {\n\n        fun onBind(photo: Photo) {\n            with(binding) {\n                photoProgressBar.visibility = View.VISIBLE\n                photoTextView.text = photo.title\n                photoImageView.loadImage(photo.url, binding.photoProgressBar)\n            }\n\n            itemView.setOnClickListener {\n                onPhotoClick(photo.id)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/photo/PhotosFragment.kt",
    "content": "package com.android.presentation.photo\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport com.android.presentation.R\nimport com.android.presentation.databinding.FragmentPhotosBinding\nimport com.android.presentation.detailphoto.PhotoDetailFragment\nimport dagger.hilt.android.AndroidEntryPoint\n\n@AndroidEntryPoint\nclass PhotosFragment : Fragment() {\n\n    private lateinit var binding: FragmentPhotosBinding\n    private var adapter: PhotosAdapter? = null\n    private val viewModel: PhotosViewModel by viewModels()\n\n    override fun onCreateView(\n        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?\n    ): View {\n        binding = FragmentPhotosBinding.inflate(inflater, container, false)\n        adapter = PhotosAdapter { gotoDetailPageByPhotoId(it) }\n        binding.photosRecyclerView.adapter = adapter\n        viewModel.loadPhotos(arguments?.getLong(KEY_ALBUM_ID))\n        return binding.root\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n        with(viewModel) {\n\n            isLoad.observe(viewLifecycleOwner) {\n                it?.let { visibility ->\n                    binding.photosProgressBar.visibility =\n                        if (visibility) View.GONE else View.VISIBLE\n                }\n            }\n\n            photoListReceivedLiveData.observe(viewLifecycleOwner) {\n                it?.let {\n                    adapter?.addData(it)\n                }\n            }\n        }\n    }\n\n    private fun gotoDetailPageByPhotoId(id: Long) {\n        activity?.supportFragmentManager?.beginTransaction()?.replace(\n            R.id.gallery_container,\n            PhotoDetailFragment.newInstance(id),\n            PhotoDetailFragment.FRAGMENT_NAME\n        )?.addToBackStack(PhotoDetailFragment.FRAGMENT_NAME)?.commitAllowingStateLoss()\n    }\n\n    override fun onDetach() {\n        super.onDetach()\n        adapter = null\n    }\n\n    companion object {\n        val FRAGMENT_NAME: String = PhotosFragment::class.java.name\n        private const val KEY_ALBUM_ID = \"KEY_ALBUM_ID\"\n\n        @JvmStatic\n        fun newInstance(id: Long) = PhotosFragment().apply {\n            arguments = Bundle().apply {\n                putLong(KEY_ALBUM_ID, id)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/java/com/android/presentation/photo/PhotosViewModel.kt",
    "content": "package com.android.presentation.photo\n\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport com.android.domain.model.Photo\nimport com.android.domain.usecase.GetPhotosUseCase\nimport dagger.hilt.android.lifecycle.HiltViewModel\nimport javax.inject.Inject\n\n/**A helper class for the UI controller that is responsible for\n * preparing data for the UI [PhotosFragment]\n *\n * @author ZARA\n * */\n@HiltViewModel\nclass PhotosViewModel @Inject constructor(\n    private val getPhotosUseCase: com.android.domain.usecase.GetPhotosUseCase\n) : ViewModel() {\n\n    val photoListReceivedLiveData = MutableLiveData<List<com.android.domain.model.Photo>>()\n    val isLoad = MutableLiveData<Boolean>()\n\n    init {\n        isLoad.value = false\n    }\n\n    fun loadPhotos(id: Long?) {\n        if (id == null) return\n        getPhotosUseCase.saveAlbumId(id)\n        getPhotosUseCase.execute(\n            onSuccess = {\n                isLoad.value = true\n                photoListReceivedLiveData.value = it\n            },\n            onError = {\n                it.printStackTrace()\n            }\n        )\n    }\n}\n"
  },
  {
    "path": "presentation/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "presentation/src/main/res/drawable-nodpi/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:height=\"108dp\"\n        android:width=\"108dp\"\n        android:viewportHeight=\"108\"\n        android:viewportWidth=\"108\">\n    <path android:fillColor=\"#008577\"\n          android:pathData=\"M0,0h108v108h-108z\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M9,0L9,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,0L19,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,0L29,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,0L39,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,0L49,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,0L59,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,0L69,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,0L79,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M89,0L89,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M99,0L99,108\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,9L108,9\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,19L108,19\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,29L108,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,39L108,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,49L108,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,59L108,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,69L108,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,79L108,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,89L108,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M0,99L108,99\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,29L89,29\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,39L89,39\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,49L89,49\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,59L89,59\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,69L89,69\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M19,79L89,79\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M29,19L29,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M39,19L39,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M49,19L49,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M59,19L59,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M69,19L69,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n    <path android:fillColor=\"#00000000\" android:pathData=\"M79,19L79,89\"\n          android:strokeColor=\"#33FFFFFF\" android:strokeWidth=\"0.8\"/>\n</vector>\n"
  },
  {
    "path": "presentation/src/main/res/drawable-nodpi/ic_paint_vector.xml",
    "content": "<vector android:height=\"48dp\" android:viewportHeight=\"431.985\"\n    android:viewportWidth=\"431.985\" android:width=\"48dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#0336ff\" android:pathData=\"M423.257,51.829c-0.808,-2.045 -2.67,-3.484 -4.853,-3.751c-2.177,-0.266 -4.335,0.682 -5.612,2.472c-7.581,10.629 -17.529,14.172 -29.053,18.275c-9.292,3.31 -18.901,6.73 -29.286,14.186c-14.687,10.544 -21.405,24.917 -18.055,38.54l-0.358,0.459c-6.133,-8.897 -12.806,-17.126 -19.848,-24.474c-32.947,-34.378 -78.984,-55.046 -126.311,-56.703c-2.085,-0.073 -4.204,-0.11 -6.298,-0.11c-52.846,0 -103.428,23.624 -138.775,64.813C9.646,146.512 -5.939,199.853 2.051,251.882c0.668,4.349 1.504,8.743 2.487,13.063c12.996,57.202 46.189,100.717 91.069,119.383c11.063,4.602 22.222,6.934 33.163,6.934c27.183,0 50.926,-14.539 65.143,-39.889c5.404,-9.646 8.891,-19.621 10.36,-29.651c0.866,-5.92 0.274,-11.835 -0.3,-17.567c-0.591,-5.9 -1.149,-11.476 -0.256,-17.09c2.047,-12.869 11.036,-20.553 24.047,-20.553c3.701,0 7.483,0.609 11.26,1.812c-4.422,8.11 -8.438,15.854 -11.947,23.032c-7.437,15.212 -12.567,27.81 -15.252,37.44c-1.655,5.939 -6.052,21.722 4.67,29.164c3.405,2.363 7.722,3.197 12.215,2.361c4.049,-0.752 16.369,-3.041 51.378,-42.896c9.396,-10.695 19.521,-23.072 30.104,-36.794c27.168,-9.15 48.31,-31.921 53.903,-58.087c1.4,-6.541 1.984,-13.541 1.735,-20.812c10.172,-15.72 19.094,-30.388 28.072,-46.156c0.172,-0.304 0.342,-0.628 0.51,-0.96c13.031,-3.569 24.254,-13.71 30.842,-27.891C434.872,106.028 434.163,79.428 423.257,51.829zM276.385,149.834c-4.713,7.485 -12.814,11.954 -21.673,11.954c-4.81,0 -9.515,-1.361 -13.605,-3.937c-5.782,-3.642 -9.803,-9.317 -11.316,-15.98s-0.345,-13.518 3.298,-19.301c4.714,-7.485 12.816,-11.954 21.675,-11.954c4.811,0 9.515,1.361 13.604,3.938c5.782,3.64 9.802,9.315 11.316,15.979C281.197,137.197 280.026,144.051 276.385,149.834zM309.592,196.187c12.934,-19.057 26.612,-38 39.604,-54.85c2.106,1.902 4.461,3.76 7.012,5.53c4.227,2.933 8.648,5.201 13.164,6.754c-10.969,18.758 -22.763,37.342 -37.043,58.375c-23.463,34.571 -47.859,66.684 -68.695,90.424c-11.638,13.26 -21.823,23.498 -29.671,29.839c3.029,-9.69 8.818,-22.989 16.875,-38.746C265.245,265.336 286.111,230.772 309.592,196.187zM82.506,196.023c-4.811,0 -9.516,-1.361 -13.607,-3.938c-5.782,-3.641 -9.801,-9.314 -11.315,-15.979c-1.514,-6.664 -0.342,-13.519 3.301,-19.302c4.711,-7.484 12.813,-11.953 21.671,-11.953c4.812,0 9.517,1.361 13.607,3.938c11.936,7.518 15.532,23.345 8.019,35.279C99.466,191.554 91.363,196.023 82.506,196.023zM55.688,252.358c4.713,-7.486 12.814,-11.955 21.673,-11.955c4.81,0 9.514,1.362 13.606,3.938c5.782,3.641 9.801,9.315 11.315,15.979c1.515,6.662 0.343,13.516 -3.301,19.301c-4.711,7.483 -12.813,11.953 -21.671,11.953c-4.811,0 -9.517,-1.361 -13.609,-3.938c-5.782,-3.642 -9.8,-9.315 -11.313,-15.979C50.876,264.995 52.049,258.14 55.688,252.358zM137.62,100.414c4.713,-7.485 12.815,-11.954 21.673,-11.954c4.809,0 9.514,1.361 13.604,3.937c11.937,7.516 15.533,23.344 8.019,35.28c-4.715,7.486 -12.817,11.955 -21.675,11.955c-4.81,0 -9.515,-1.361 -13.605,-3.938c-5.781,-3.64 -9.799,-9.314 -11.313,-15.979C132.807,113.052 133.979,106.198 137.62,100.414z\"/>\n</vector>\n"
  },
  {
    "path": "presentation/src/main/res/drawable-nodpi/ic_star_empty_white_vector.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:width=\"24dp\"\r\n    android:height=\"24dp\"\r\n    android:viewportHeight=\"16.0\"\r\n    android:viewportWidth=\"16.0\">\r\n    <path\r\n        android:fillColor=\"@color/white\"\r\n        android:pathData=\"M16,6.204l-5.528,-0.803 -2.472,-5.009 -2.472,5.009 -5.528,0.803 4,3.899 -0.944,5.505 4.944,-2.599 4.944,2.599 -0.944,-5.505 4,-3.899zM8,11.773l-3.492,1.836 0.667,-3.888 -2.825,-2.753 3.904,-0.567 1.746,-3.537 1.746,3.537 3.904,0.567 -2.825,2.753 0.667,3.888 -3.492,-1.836z\" />\r\n</vector>\r\n"
  },
  {
    "path": "presentation/src/main/res/drawable-nodpi/ic_star_full_vector.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"16.0\"\n    android:viewportWidth=\"16.0\">\n    <path\n        android:fillColor=\"@color/golden\"\n        android:pathData=\"M16,6.204l-5.528,-0.803 -2.472,-5.009 -2.472,5.009 -5.528,0.803 4,3.899 -0.944,5.505 4.944,-2.599 4.944,2.599 -0.944,-5.505 4,-3.899z\" />\n</vector>\n"
  },
  {
    "path": "presentation/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:aapt=\"http://schemas.android.com/aapt\"\n        android:width=\"108dp\"\n        android:height=\"108dp\"\n        android:viewportHeight=\"108\"\n        android:viewportWidth=\"108\">\n    <path\n            android:fillType=\"evenOdd\"\n            android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n            android:strokeColor=\"#00000000\"\n            android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                    android:endX=\"78.5885\"\n                    android:endY=\"90.9159\"\n                    android:startX=\"48.7653\"\n                    android:startY=\"61.0927\"\n                    android:type=\"linear\">\n                <item\n                        android:color=\"#44000000\"\n                        android:offset=\"0.0\"/>\n                <item\n                        android:color=\"#00000000\"\n                        android:offset=\"1.0\"/>\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n            android:fillColor=\"#FFFFFF\"\n            android:fillType=\"nonZero\"\n            android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n            android:strokeColor=\"#00000000\"\n            android:strokeWidth=\"1\"/>\n</vector>\n"
  },
  {
    "path": "presentation/src/main/res/layout/activity_gallery.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/gallery_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/whiteSmoke\"\n    tools:context=\"com.android.presentation.gallery.GalleryActivity\" />"
  },
  {
    "path": "presentation/src/main/res/layout/fragment_albums.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ImageView\n        android:id=\"@+id/gallery_header_icon_image_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        app:srcCompat=\"@drawable/ic_paint_vector\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/albums_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        app:layoutManager=\"androidx.recyclerview.widget.GridLayoutManager\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/gallery_header_icon_image_view\"\n        app:spanCount=\"1\" />\n\n    <ProgressBar\n        android:id=\"@+id/albums_progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/fragment_photo_detail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"false\"\n    tools:context=\"com.android.presentation.detailphoto.PhotoDetailFragment\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/detail_app_bar_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/detail_image_height_size\"\n        android:fitsSystemWindows=\"true\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/detail_collapsing_toolbar_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:fitsSystemWindows=\"true\"\n            app:contentScrim=\"?attr/colorPrimary\"\n            app:expandedTitleMarginEnd=\"64dp\"\n            app:expandedTitleMarginStart=\"48dp\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed\">\n\n            <RelativeLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\">\n\n                <ImageView\n                    android:id=\"@+id/detail_toolbar_image_view\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:fitsSystemWindows=\"true\"\n                    android:scaleType=\"centerCrop\"\n                    android:transitionName=\"image\"\n                    app:layout_collapseMode=\"parallax\" />\n\n            </RelativeLayout>\n\n            <androidx.appcompat.widget.Toolbar\n                android:id=\"@+id/detail_toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                app:layout_collapseMode=\"pin\"\n                app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\" />\n\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.core.widget.NestedScrollView\n        android:id=\"@+id/detail_nested_scroll_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:descendantFocusability=\"blocksDescendants\"\n            android:orientation=\"vertical\">\n\n            <!--Description-->\n            <androidx.cardview.widget.CardView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_margin=\"8dp\"\n                android:elevation=\"2dp\"\n                app:cardCornerRadius=\"16dp\"\n                app:cardPreventCornerOverlap=\"false\"\n                app:cardUseCompatPadding=\"true\"\n                tools:ignore=\"RtlCompat\">\n\n                <TextView\n                    android:id=\"@+id/detail_title_text_view\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_margin=\"16dp\"\n                    android:gravity=\"center\"\n                    android:textColor=\"@color/colorTextSecondary\"\n                    android:textSize=\"@dimen/subheading1\" />\n\n            </androidx.cardview.widget.CardView>\n\n        </LinearLayout>\n\n    </androidx.core.widget.NestedScrollView>\n\n    <com.google.android.material.floatingactionbutton.FloatingActionButton\n        android:id=\"@+id/detail_fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\n        app:layout_anchor=\"@id/detail_app_bar_layout\"\n        app:layout_anchorGravity=\"bottom|end\"\n        app:srcCompat=\"@drawable/ic_star_empty_white_vector\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/fragment_photos.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/photos_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        app:layoutManager=\"androidx.recyclerview.widget.GridLayoutManager\"\n        app:spanCount=\"2\" />\n\n    <ProgressBar\n        android:id=\"@+id/photos_progress_bar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n</FrameLayout>\n"
  },
  {
    "path": "presentation/src/main/res/layout/holder_album.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.cardview.widget.CardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/album_card_width_size\"\n    app:cardCornerRadius=\"8dp\"\n    app:cardElevation=\"4dp\"\n    app:cardUseCompatPadding=\"true\">\n\n    <TextView\n        android:id=\"@+id/holderAlbumTextView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_margin=\"8dp\"\n        android:gravity=\"center\"\n        android:textColor=\"@color/colorTextSecondary\"\n        android:textSize=\"@dimen/title\" />\n\n</androidx.cardview.widget.CardView>"
  },
  {
    "path": "presentation/src/main/res/layout/holder_photo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.cardview.widget.CardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    app:cardCornerRadius=\"8dp\"\n    app:cardElevation=\"4dp\"\n    app:cardUseCompatPadding=\"true\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <ImageView\n            android:id=\"@+id/photo_image_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/photo_image_height_size\"\n            android:scaleType=\"centerCrop\"\n            android:transitionName=\"image\"\n            tools:targetApi=\"lollipop\" />\n\n        <TextView\n            android:id=\"@+id/photo_text_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/photo_image_view\"\n            android:layout_margin=\"8dp\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxLength=\"10\"\n            android:maxLines=\"1\"\n            android:textColor=\"@color/colorTextPrimary\"\n            android:textSize=\"@dimen/subheading2\" />\n\n        <ProgressBar\n            android:id=\"@+id/photo_progress_bar\"\n            style=\"@style/Base.Widget.AppCompat.ProgressBar\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\" />\n\n    </RelativeLayout>\n\n</androidx.cardview.widget.CardView>"
  },
  {
    "path": "presentation/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "presentation/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "presentation/src/main/res/mipmap-anydpi-v33/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "presentation/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#48a999</color>\n    <color name=\"colorPrimaryDark\">#004c40</color>\n    <color name=\"colorAccent\">#FFDE54</color>\n    <color name=\"blue\">#0336FF</color>\n    <color name=\"colorTextPrimary\">#202020</color>\n    <color name=\"colorTextSecondary\">#737373</color>\n    <color name=\"gainsBoro\">#b9b9b9</color>\n    <color name=\"whiteSmoke\">#f4f4f4</color>\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"golden\">#ffc800</color>\n    <color name=\"lightGray\">#eaeaea</color>\n    <color name=\"transparent\">#5a000000</color>\n</resources>"
  },
  {
    "path": "presentation/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"text_margin\">16dp</dimen>\n\n    <dimen name=\"display4\">112sp</dimen><!--Black 54%-->\n    <dimen name=\"display3\">56sp</dimen><!--Black 54%-->\n    <dimen name=\"display2\">45sp</dimen><!--Black 54% & leading 48dp-->\n    <dimen name=\"display1\">34sp</dimen><!--Black 54% & leading 40dp-->\n\n    <dimen name=\"headline\">24sp</dimen><!--Black 87% & leading 32dp-->\n    <dimen name=\"title\">20sp</dimen><!--Black 87%-->\n\n    <dimen name=\"subheading2\">16sp</dimen><!--Black 87% & leading 28dp-->\n    <dimen name=\"subheading1\">15sp</dimen><!--Black 87% & leading 24dp-->\n    <dimen name=\"body2\">14sp</dimen><!--Black 87% & leading 24dp-->\n    <dimen name=\"body1\">13sp</dimen><!--Black 87% & leading 20dp-->\n\n    <dimen name=\"caption\">12sp</dimen><!--Black 54%-->\n    <dimen name=\"button\">14sp</dimen>\n    <dimen name=\"album_card_width_size\">160dp</dimen>\n    <dimen name=\"photo_card_height_size\">180dp</dimen>\n    <dimen name=\"photo_image_height_size\">140dp</dimen>\n    <dimen name=\"detail_image_height_size\">260dp</dimen><!--Black 87%-->\n\n</resources>\n"
  },
  {
    "path": "presentation/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ArtGallery</string>\n</resources>"
  },
  {
    "path": "presentation/src/main/res/values/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- light mode styles -->\n</resources>"
  },
  {
    "path": "presentation/src/main/res/values-night/themes.xml",
    "content": "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n                <!-- dark mode styles -->\n</resources>"
  },
  {
    "path": "presentation/src/test/java/com/android/presentation/ExampleUnitTest.kt",
    "content": "package com.android.presentation\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\ninclude ':domain'\ninclude ':data'\ninclude ':presentation'\n"
  }
]