diff --git a/Android/pawan/IntuitiveCats/.gitignore b/Android/pawan/IntuitiveCats/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/Android/pawan/IntuitiveCats/.idea/.gitignore b/Android/pawan/IntuitiveCats/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Android/pawan/IntuitiveCats/.idea/compiler.xml b/Android/pawan/IntuitiveCats/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/.idea/encodings.xml b/Android/pawan/IntuitiveCats/.idea/encodings.xml new file mode 100644 index 0000000..24237a8 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/.idea/gradle.xml b/Android/pawan/IntuitiveCats/.idea/gradle.xml new file mode 100644 index 0000000..503772c --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/.idea/misc.xml b/Android/pawan/IntuitiveCats/.idea/misc.xml new file mode 100644 index 0000000..d2fe310 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/misc.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/.idea/vcs.xml b/Android/pawan/IntuitiveCats/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/Android/pawan/IntuitiveCats/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/.gitignore b/Android/pawan/IntuitiveCats/PagedRecyclerView/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/build.gradle b/Android/pawan/IntuitiveCats/PagedRecyclerView/build.gradle new file mode 100644 index 0000000..9ebb4cc --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 32 + + defaultConfig { + minSdk 21 + targetSdk 32 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.7.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.4' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + + implementation 'javax.inject:javax.inject:1' +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/consumer-rules.pro b/Android/pawan/IntuitiveCats/PagedRecyclerView/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/proguard-rules.pro b/Android/pawan/IntuitiveCats/PagedRecyclerView/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/src/androidTest/java/com/example/pagedrecyclerview/ExampleInstrumentedTest.kt b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/androidTest/java/com/example/pagedrecyclerview/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..cbebda8 --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/androidTest/java/com/example/pagedrecyclerview/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.pagedrecyclerview + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.pagedrecyclerview.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/src/main/AndroidManifest.xml b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f8ed2b8 --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/PagedRecyclerView/src/test/java/com/example/pagedrecyclerview/ExampleUnitTest.kt b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/test/java/com/example/pagedrecyclerview/ExampleUnitTest.kt new file mode 100644 index 0000000..4ad75e7 --- /dev/null +++ b/Android/pawan/IntuitiveCats/PagedRecyclerView/src/test/java/com/example/pagedrecyclerview/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.example.pagedrecyclerview + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/.gitignore b/Android/pawan/IntuitiveCats/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/build.gradle b/Android/pawan/IntuitiveCats/app/build.gradle new file mode 100644 index 0000000..cc08ebf --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/build.gradle @@ -0,0 +1,156 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' + id 'com.google.dagger.hilt.android' + id 'androidx.navigation.safeargs.kotlin' + id 'kotlin-parcelize' +} + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.example.intuitivecats" + minSdk 21 + targetSdk 32 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "com.example.intuitivecats.TestRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + + viewBinding { + enabled = true + } + + testOptions { + unitTests { + all { + useJUnitPlatform() + testLogging { + events = ["passed", "skipped", "failed"] + showExceptions = true + showCauses = true + showStackTraces = true + afterSuite { descriptor, result -> + if (!descriptor.parent) { // will match the outermost suite + println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" + } + } + } + } + includeAndroidResources = true + returnDefaultValues = true + } + } +} + +dependencies { + implementation project(path: ':pagerv') + + def lifecycle_version = "2.5.1" + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.7.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + + //dagger + implementation 'com.google.dagger:hilt-android:2.44' + kapt 'com.google.dagger:hilt-compiler:2.44' + + //retrofit + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + + //ktx + implementation 'androidx.activity:activity-ktx:1.3.1' + + //ViewModel + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + + //LiveData + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + + // Lifecycles only (without ViewModel or LiveData) + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" + + // Saved state module for ViewModel + implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" + implementation 'androidx.navigation:navigation-compose:2.5.0' + + //glide + implementation 'com.github.bumptech.glide:glide:4.14.2' + kapt 'com.github.bumptech.glide:compiler:4.14.2' + + //okhttplogger + implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' + + //paging + implementation 'androidx.paging:paging-common-ktx:3.1.1' + implementation 'androidx.paging:paging-runtime-ktx:3.1.1' + + //navigation + def nav_version = "2.5.3" + def junit5Version = "5.9.1" + + implementation("androidx.navigation:navigation-fragment-ktx:$nav_version") + implementation("androidx.navigation:navigation-ui-ktx:$nav_version") + + /*Unit testing Dependencies*/ + testImplementation "org.junit.jupiter:junit-jupiter-api:${junit5Version}" + testImplementation "org.junit.jupiter:junit-jupiter-params:${junit5Version}" + testImplementation "org.junit.jupiter:junit-jupiter-migrationsupport:${junit5Version}" + + testImplementation "org.mockito:mockito-core:3.0.0" + testImplementation "io.mockk:mockk:1.10.5" + testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0" + testImplementation 'org.robolectric:robolectric:4.5.1' + testImplementation 'androidx.test:core:1.4.0' + testImplementation 'androidx.test.ext:junit:1.1.3' + testImplementation 'androidx.test:runner:1.4.0' + testImplementation "androidx.test:core-ktx:1.4.0" + testImplementation 'org.robolectric:robolectric:4.5.1' + + testImplementation "org.assertj:assertj-core:3.18.0" + + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit5Version}" + testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit5Version}" + + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' + + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + def hiltVersion = "2.36" + + androidTestImplementation "com.google.dagger:hilt-android-testing:$hiltVersion" + kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hiltVersion" + androidTestImplementation "androidx.navigation:navigation-testing:2.3.5" + androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2' + + implementation 'androidx.fragment:fragment:1.5.4' + debugImplementation 'androidx.fragment:fragment-testing:1.5.4' + +} + +kapt { + arguments { + arg("dagger.hilt.shareTestComponents", "true") + } + correctErrorTypes true +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/proguard-rules.pro b/Android/pawan/IntuitiveCats/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/HiltExt.kt b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/HiltExt.kt new file mode 100644 index 0000000..b10ce55 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/HiltExt.kt @@ -0,0 +1,65 @@ +package com.example.intuitivecats + +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.ComponentName +import android.content.Intent +import android.os.Bundle +import androidx.core.util.Preconditions +import androidx.fragment.app.Fragment +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider + +/** + * launchFragmentInContainer from the androidx.fragment:fragment-testing library + * is NOT possible to use right now as it uses a hardcoded Activity under the hood + * (i.e. [EmptyFragmentActivity]) which is not annotated with @AndroidEntryPoint. + * + * As a workaround, use this function that is equivalent. It requires you to add + * [HiltTestActivity] in the debug folder and include it in the debug AndroidManifest.xml file + * as can be found in this project. + */ +inline fun launchFragmentInHiltContainer( + fragmentArgs: Bundle? = null, + //@StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme, + crossinline action: Fragment.() -> Unit = {} +) { + val startActivityIntent = Intent.makeMainActivity( + ComponentName( + ApplicationProvider.getApplicationContext(), + HiltTestActivity::class.java + ) + ) +// ).putExtra( +// "androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY", +// themeResId +// ) + + ActivityScenario.launch(startActivityIntent).onActivity { activity -> + val fragment: Fragment = activity.supportFragmentManager.fragmentFactory.instantiate( + Preconditions.checkNotNull(T::class.java.classLoader), + T::class.java.name + ) + fragment.arguments = fragmentArgs + activity.supportFragmentManager + .beginTransaction() + .add(android.R.id.content, fragment, "") + .commitNow() + + fragment.action() + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/TestRunner.kt b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/TestRunner.kt new file mode 100644 index 0000000..309f82f --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/TestRunner.kt @@ -0,0 +1,29 @@ +package com.example.intuitivecats + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + +//import android.app.Application +//import android.content.Context +//import androidx.test.runner.AndroidJUnitRunner +//import dagger.hilt.android.testing.HiltTestApplication +// +//class TestRunner : AndroidJUnitRunner() { +// override fun newApplication( +// cl: ClassLoader, +// appName: String, +// context: Context +// ): Application { +// return super.newApplication( +// cl, HiltTestApplication::class.java.name, context +// ) +// } +//} + +class TestRunner : AndroidJUnitRunner() { + override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragmentTest.kt b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragmentTest.kt new file mode 100644 index 0000000..14fd19e --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragmentTest.kt @@ -0,0 +1,61 @@ +package com.example.intuitivecats.breeds.ui + + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.example.intuitivecats.R +import com.example.intuitivecats.breeds.model.Image +import com.example.intuitivecats.breeds.model.Weight +import com.example.intuitivecats.factory.BreedFactory +import com.example.intuitivecats.launchFragmentInHiltContainer +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +@MediumTest +@RunWith(AndroidJUnit4::class) +@HiltAndroidTest +class BreedDetailsFragmentTest { + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Before + fun setup() { + hiltRule.inject() + } + + @Test + fun givenBreedInArgsDisplaysBreedDetails() { + val breedItem = BreedFactory.create( + id = "Good breed", + image = Image( + 1445, + + "0XYvRd7oD", + "https://cdn2.thecatapi.com/images/0XYvRd7oD.jpg", + 1204 + ), + description = "Native to the Greek islands known as the Cyclades in the Aegean Sea, these are natural cats, meaning they developed without humans getting involved in their breeding. As a breed, Aegean Cats are rare, although they are numerous on their home islands. They are generally friendly toward people and can be excellent cats for families with children.", + origin = "Greece", + weight = Weight( + "7 - 10", + "3 - 5" + ), + wikipedia_url = "https://en.wikipedia.org/wiki/American_Bobtail" + ) + val bundle = BreedDetailsFragmentArgs(breedItem).toBundle() + launchFragmentInHiltContainer(bundle) + onView(withId(R.id.btnMoreinfo)) + .check(matches(isDisplayed())) + onView(withText(breedItem.origin)).check(matches(isDisplayed())) + onView(withText(breedItem.description)).check(matches(isDisplayed())) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedsFragmentTest.kt b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedsFragmentTest.kt new file mode 100644 index 0000000..52c7f5d --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/breeds/ui/BreedsFragmentTest.kt @@ -0,0 +1,42 @@ +package com.example.intuitivecats.breeds.ui + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.example.intuitivecats.R +import com.example.intuitivecats.launchFragmentInHiltContainer +import com.example.pagerv.PagerDataAdapter +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import javax.inject.Inject + +@MediumTest +@RunWith(AndroidJUnit4::class) +@HiltAndroidTest +class BreedsFragmentTest { + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var adapter: PagerDataAdapter + + @Before + fun setup() { + hiltRule.inject() + } + + @Test + fun verifyBreedsDisplayed() { + launchFragmentInHiltContainer(null) + Thread.sleep(2000) + onView(withId(R.id.recyclerViewCats)).check(matches(isDisplayed())) + onView(withText("Aegean")).check(matches(isDisplayed())) + } +} diff --git a/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/factory/BreedFactory.kt b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/factory/BreedFactory.kt new file mode 100644 index 0000000..2690170 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/androidTest/java/com/example/intuitivecats/factory/BreedFactory.kt @@ -0,0 +1,92 @@ +package com.example.intuitivecats.factory + +import com.example.intuitivecats.breeds.model.BreedItem +import com.example.intuitivecats.breeds.model.Image +import com.example.intuitivecats.breeds.model.Weight + +object BreedFactory { + + fun create( + adaptability: Int = 1, + affection_level: Int = 2, + alt_names: String? = "", + bidability: Int = 3, + cat_friendly: Int = 4, + cfa_url: String = "", + child_friendly: Int = 5, + country_code: String? = "", + country_codes: String? = "", + description: String? = "", + dog_friendly: Int = 6, + energy_level: Int = 7, + experimental: Int = 8, + grooming: Int = 9, + hairless: Int = 10, + health_issues: Int = 11, + hypoallergenic: Int = 12, + id: String? = "", + image: Image? = Image(1, "", "", 1), + indoor: Int = 13, + intelligence: Int = 14, + lap: Int = 15, + life_span: String? = "", + name: String? = "", + natural: Int = 16, + origin: String? = "", + rare: Int = 17, + reference_image_id: String? = "", + rex: Int = 18, + shedding_level: Int = 19, + short_legs: Int = 20, + social_needs: Int = 21, + stranger_friendly: Int = 22, + suppressed_tail: Int = 23, + temperament: String? = "", + vcahospitals_url: String? = "", + vetstreet_url: String? = "", + vocalisation: Int = 24, + weight: Weight? = Weight("", ""), + wikipedia_url: String? = "" + ): BreedItem = BreedItem( + adaptability = adaptability, + affection_level = affection_level, + alt_names = alt_names, + bidability = bidability, + cat_friendly = cat_friendly, + cfa_url = cfa_url, + child_friendly = child_friendly, + country_code = country_code, + country_codes = country_codes, + description = description, + dog_friendly = dog_friendly, + energy_level = energy_level, + experimental = experimental, + grooming = grooming, + hairless = hairless, + health_issues = health_issues, + hypoallergenic = hypoallergenic, + id = id, + image = image, + indoor = indoor, + intelligence = intelligence, + lap = lap, + life_span = life_span, + name = name, + natural = natural, + origin = origin, + rare = rare, + reference_image_id = reference_image_id, + rex = rex, + shedding_level = shedding_level, + short_legs = short_legs, + social_needs = social_needs, + stranger_friendly = stranger_friendly, + suppressed_tail = suppressed_tail, + temperament = temperament, + vcahospitals_url = vcahospitals_url, + vetstreet_url = vetstreet_url, + vocalisation = vocalisation, + weight = weight, + wikipedia_url = wikipedia_url + ) +} diff --git a/Android/pawan/IntuitiveCats/app/src/debug/AndroidManifest.xml b/Android/pawan/IntuitiveCats/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..ec5700f --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/debug/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/HiltTestActivity.kt b/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/HiltTestActivity.kt new file mode 100644 index 0000000..f4b2e2f --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/HiltTestActivity.kt @@ -0,0 +1,12 @@ +package com.example.intuitivecats + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class HiltTestActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/TestApplication.kt b/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/TestApplication.kt new file mode 100644 index 0000000..f9c2369 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/debug/java/com/example/intuitivecats/TestApplication.kt @@ -0,0 +1,6 @@ +package com.example.intuitivecats + +import android.app.Application + +class TestApplication : Application() { +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/debug/res/values/strings.xml b/Android/pawan/IntuitiveCats/app/src/debug/res/values/strings.xml new file mode 100644 index 0000000..73862c4 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/debug/res/values/strings.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/AndroidManifest.xml b/Android/pawan/IntuitiveCats/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b1db85a --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/IntuitiveCatsApplication.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/IntuitiveCatsApplication.kt new file mode 100644 index 0000000..57872c2 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/IntuitiveCatsApplication.kt @@ -0,0 +1,8 @@ +package com.example.intuitivecats + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class IntuitiveCatsApplication : Application() { +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/MainActivity.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/MainActivity.kt new file mode 100644 index 0000000..11bc63e --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/MainActivity.kt @@ -0,0 +1,41 @@ +package com.example.intuitivecats + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.NavigationUI +import com.example.intuitivecats.databinding.ActivityMainBinding +import dagger.hilt.android.AndroidEntryPoint + + +@AndroidEntryPoint +class MainActivity : AppCompatActivity() { + + lateinit var binding: ActivityMainBinding + private lateinit var navController: NavController + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + setupNavHostFragment() + } + + private fun setupNavHostFragment() { + setSupportActionBar(binding.toolbar) + supportActionBar?.setDisplayShowTitleEnabled(false) + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment + navController = navHostFragment.navController + binding.toolbar.title = getString(R.string.app_name) + val appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build() + NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration) + } + + override fun onSupportNavigateUp(): Boolean { + return navController.navigateUp() || super.onSupportNavigateUp() + } +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/CatsViewmodel.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/CatsViewmodel.kt new file mode 100644 index 0000000..384e5d6 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/CatsViewmodel.kt @@ -0,0 +1,18 @@ +package com.example.intuitivecats.breeds + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn +import com.example.intuitivecats.network.CatRepository + +import com.example.pagerv.paging.PageRv +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class BreedsViewModel @Inject constructor(private val catRepository: CatRepository) : ViewModel() { + + fun getBreedById(id: String?) = catRepository.getBreedById(id) + + val dataList = PageRv(catRepository,25).data.cachedIn(viewModelScope) +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/BreedItem.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/BreedItem.kt new file mode 100644 index 0000000..5189673 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/BreedItem.kt @@ -0,0 +1,48 @@ +package com.example.intuitivecats.breeds.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class BreedItem( + val adaptability: Int, + val affection_level: Int, + val alt_names: String?, + val bidability: Int, + val cat_friendly: Int, + val cfa_url: String?, + val child_friendly: Int, + val country_code: String?, + val country_codes: String?, + val description: String?, + val dog_friendly: Int, + val energy_level: Int, + val experimental: Int, + val grooming: Int, + val hairless: Int, + val health_issues: Int, + val hypoallergenic: Int, + val id: String?, + val image: Image?, + val indoor: Int, + val intelligence: Int, + val lap: Int, + val life_span: String?, + val name: String?, + val natural: Int, + val origin: String?, + val rare: Int, + val reference_image_id: String?, + val rex: Int, + val shedding_level: Int, + val short_legs: Int, + val social_needs: Int, + val stranger_friendly: Int, + val suppressed_tail: Int, + val temperament: String?, + val vcahospitals_url: String?, + val vetstreet_url: String?, + val vocalisation: Int, + val weight: Weight?, + val wikipedia_url: String? +) : Parcelable \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Image.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Image.kt new file mode 100644 index 0000000..5095d44 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Image.kt @@ -0,0 +1,12 @@ +package com.example.intuitivecats.breeds.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class Image( + val height: Int, + val id: String, + val url: String, + val width: Int +) : Parcelable \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Weight.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Weight.kt new file mode 100644 index 0000000..d8fc4ec --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/model/Weight.kt @@ -0,0 +1,10 @@ +package com.example.intuitivecats.breeds.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class Weight( + val imperial: String?, + val metric: String? +) : Parcelable \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragment.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragment.kt new file mode 100644 index 0000000..2168699 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedDetailsFragment.kt @@ -0,0 +1,71 @@ +package com.example.intuitivecats.breeds.ui + +import android.annotation.SuppressLint +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs +import com.bumptech.glide.Glide +import com.example.intuitivecats.MainActivity +import com.example.intuitivecats.R +import com.example.intuitivecats.databinding.FragmentBreedDetailsBinding + + +class BreedDetailsFragment : Fragment() { + + private val args: BreedDetailsFragmentArgs by navArgs() + private lateinit var binding: FragmentBreedDetailsBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentBreedDetailsBinding.inflate( + layoutInflater + ) + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setData() + } + + @SuppressLint("SetTextI18n") + private fun setData() { + binding.apply { + val catBreed = args.breed + breed.text = catBreed.name + country.text = catBreed.origin + weight.text = " ${catBreed.weight?.metric} ${getString(R.string.kilograms)}" + lifespan.text = " ${catBreed.life_span} ${getString(R.string.years)}" + description.text = catBreed.description + + btnMoreinfo.setOnClickListener { + sendingToWikipedia() + } + + Glide.with(binding.root.context) + .load(catBreed.image?.url) + .placeholder(R.drawable.ic_baseline_pets_24) + .error(R.drawable.ic_baseline_pets_24) + .centerCrop() + .into(binding.catpic) + } + } + + private fun sendingToWikipedia() { + val url = args.breed.wikipedia_url + url?.let { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(url) + startActivity(intent) + } + } +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedsFragment.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedsFragment.kt new file mode 100644 index 0000000..a704fda --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/breeds/ui/BreedsFragment.kt @@ -0,0 +1,78 @@ +package com.example.intuitivecats.breeds.ui + + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.Navigation +import androidx.paging.LoadState +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.intuitivecats.breeds.BreedsViewModel +import com.example.intuitivecats.databinding.FragmentBreedsBinding +import com.example.pagerv.LoadMoreAdapter +import com.example.pagerv.PagerDataAdapter +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class BreedsFragment : Fragment() { + + private lateinit var binding: FragmentBreedsBinding + private val viewModel: BreedsViewModel by viewModels() + + @Inject + lateinit var catsAdapter: PagerDataAdapter + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + super.onCreateView(inflater, container, savedInstanceState) + binding = FragmentBreedsBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initUi() + } + + private fun initUi() { + catsAdapter.setOnItemClickListener { dataItem -> + val breed = viewModel.getBreedById(dataItem.id) + val action = + BreedsFragmentDirections.actionBreedsFragmentToBreedDetailsFragment(breed) + Navigation.findNavController(binding.root).navigate(action) + } + + binding.apply { + recyclerViewCats.apply { + layoutManager = LinearLayoutManager(context) + adapter = catsAdapter.withLoadStateFooter( + LoadMoreAdapter { + catsAdapter.retry() + } + ) + } + } + + viewModel.dataList.observe(viewLifecycleOwner) { + lifecycleScope.launchWhenCreated { + catsAdapter.submitData(it) + } + } + + lifecycleScope.launchWhenCreated { + catsAdapter.loadStateFlow.collect { + val state = it.refresh + binding.prgBarCats.isVisible = state is LoadState.Loading + } + } + } +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/di/AppModule.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/di/AppModule.kt new file mode 100644 index 0000000..c566b24 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/di/AppModule.kt @@ -0,0 +1,71 @@ +package com.example.intuitivecats.di + +import com.example.intuitivecats.network.BreedApi +import com.example.pagerv.PagerDataAdapter +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + + +@Module +@InstallIn(SingletonComponent::class) +object AppModule { + + @Provides + fun providesHeaderInterceptor(): Interceptor { + return Interceptor { chain: Interceptor.Chain -> + + val request = chain.request().newBuilder() + .header("x-api-key", "17d94b92-754f-46eb-99a0-65be65b5d18f") + .build() + chain.proceed(request) + } + } + + @Provides + fun providesLoggingInterceptor(): HttpLoggingInterceptor = + HttpLoggingInterceptor() + .apply { + setLevel(HttpLoggingInterceptor.Level.BODY) + } + + + @Provides + fun providesHttpClient( + headerInterceptor: Interceptor, + loggingInterceptor: HttpLoggingInterceptor + ): OkHttpClient { + return OkHttpClient.Builder() + .connectTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .addInterceptor(loggingInterceptor) + .addNetworkInterceptor(headerInterceptor) + .build() + } + + + @Provides + fun providesRetrofit(httpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .addConverterFactory(GsonConverterFactory.create()) + .baseUrl("https://api.thecatapi.com/") + .client(httpClient) + .build() + } + + @Provides + fun providesBreedApi(retrofit: Retrofit): BreedApi { + return retrofit.create(BreedApi::class.java) + } + + @Provides + fun providesPagerDataAdapter(): PagerDataAdapter = PagerDataAdapter() +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/BreedApi.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/BreedApi.kt new file mode 100644 index 0000000..4214d98 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/BreedApi.kt @@ -0,0 +1,11 @@ +package com.example.intuitivecats.network + +import com.example.intuitivecats.breeds.model.BreedItem +import retrofit2.http.GET +import retrofit2.http.Query + +interface BreedApi { + + @GET("v1/breeds") + suspend fun getCats(@Query("page") page: Int, @Query("limit") loadSize: Int): List +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/CatRepository.kt b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/CatRepository.kt new file mode 100644 index 0000000..64587ad --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/java/com/example/intuitivecats/network/CatRepository.kt @@ -0,0 +1,31 @@ +package com.example.intuitivecats.network + +import com.example.intuitivecats.breeds.model.BreedItem +import com.example.pagerv.paging.DataItem +import com.example.pagerv.paging.ApiRepository +import javax.inject.Inject + +class CatRepository @Inject constructor(private val breedApi: BreedApi) : ApiRepository { + + private var breeds = mutableListOf() + + override suspend fun getData(currentPage: Int, loadSize: Int): List { + return breedApi.getCats(currentPage, loadSize).also { + breeds.addAll(it) + }.map { + DataItem( + id = it.id, + title = it.name, + subTitle = it.origin, + imageUrl = it.image?.url + ) + } + } + + fun getBreedById(id: String?): BreedItem { + return breeds.filter { + it.id == id + }[0] + } + +} diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/bg_circle_white.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/bg_circle_white.xml new file mode 100644 index 0000000..9c8b75c --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/bg_circle_white.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml new file mode 100644 index 0000000..e545abc --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_close_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_close_24.xml new file mode 100644 index 0000000..70db409 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_close_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml new file mode 100644 index 0000000..996e16d --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_info_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_info_24.xml new file mode 100644 index 0000000..654cfb3 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_info_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml new file mode 100644 index 0000000..89a0ff5 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_location_on_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_location_on_24.xml new file mode 100644 index 0000000..dd12113 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_location_on_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_pets_24.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_pets_24.xml new file mode 100644 index 0000000..df83859 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_baseline_pets_24.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/loadingcat.gif b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/loadingcat.gif new file mode 100644 index 0000000..25949a4 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/drawable-v24/loadingcat.gif differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable/bg_gradient.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/bg_gradient.xml new file mode 100644 index 0000000..a6f1650 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/bg_gradient.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable/caty.jpeg b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/caty.jpeg new file mode 100644 index 0000000..640feef Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/caty.jpeg differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable/ic_launcher_background.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/drawable/splash_background.xml b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/splash_background.xml new file mode 100644 index 0000000..ccedbf7 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/drawable/splash_background.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/layout/activity_main.xml b/Android/pawan/IntuitiveCats/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..aa3d8c5 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,33 @@ + + + + + + + + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breed_details.xml b/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breed_details.xml new file mode 100644 index 0000000..a5a2b67 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breed_details.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breeds.xml b/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breeds.xml new file mode 100644 index 0000000..b0a3117 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/layout/fragment_breeds.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/Android/pawan/IntuitiveCats/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/navigation/nav_graph.xml b/Android/pawan/IntuitiveCats/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..991f1e4 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/values-night/themes.xml b/Android/pawan/IntuitiveCats/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..48e9245 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/values/colors.xml b/Android/pawan/IntuitiveCats/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..20df6f0 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/values/colors.xml @@ -0,0 +1,11 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #F7f5bc + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/values/strings.xml b/Android/pawan/IntuitiveCats/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1252add --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/values/strings.xml @@ -0,0 +1,14 @@ + + IntuitiveCats + Settings + kilograms + years + Description + More info + Done + Breed Name + Country name + Mood + start now + Learn more about us so you can become a good Karen! + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/values/styles.xml b/Android/pawan/IntuitiveCats/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..9ffbc10 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/values/styles.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/values/themes.xml b/Android/pawan/IntuitiveCats/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..ddce5ab --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/values/themes.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/xml/backup_rules.xml b/Android/pawan/IntuitiveCats/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/main/res/xml/data_extraction_rules.xml b/Android/pawan/IntuitiveCats/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/breeds/BreedsViewModelTest.kt b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/breeds/BreedsViewModelTest.kt new file mode 100644 index 0000000..07e5bdd --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/breeds/BreedsViewModelTest.kt @@ -0,0 +1,42 @@ +package com.example.intuitivecats.breeds + + +import com.example.intuitivecats.factory.BreedFactory +import com.example.intuitivecats.network.CatRepository +import com.example.intuitivecats.utils.InstantExecutorExtension +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExperimentalCoroutinesApi +@ExtendWith(InstantExecutorExtension::class) +internal class BreedsViewModelTest { + + @MockK + lateinit var catRepository: CatRepository + + private lateinit var breedsViewModel: BreedsViewModel + + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this, relaxUnitFun = true, relaxed = true) + breedsViewModel = BreedsViewModel(catRepository) + } + + @Test + fun `fetch cat breed by id successfully returns a breed`() { + every { + catRepository.getBreedById("id") + } returns BreedFactory.create(adaptability = 23, hypoallergenic = 25) + + val item = breedsViewModel.getBreedById("id") + Assertions.assertThat(item.adaptability).isEqualTo(23) + Assertions.assertThat(item.hypoallergenic).isEqualTo(25) + } +} diff --git a/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/factory/BreedFactory.kt b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/factory/BreedFactory.kt new file mode 100644 index 0000000..2690170 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/factory/BreedFactory.kt @@ -0,0 +1,92 @@ +package com.example.intuitivecats.factory + +import com.example.intuitivecats.breeds.model.BreedItem +import com.example.intuitivecats.breeds.model.Image +import com.example.intuitivecats.breeds.model.Weight + +object BreedFactory { + + fun create( + adaptability: Int = 1, + affection_level: Int = 2, + alt_names: String? = "", + bidability: Int = 3, + cat_friendly: Int = 4, + cfa_url: String = "", + child_friendly: Int = 5, + country_code: String? = "", + country_codes: String? = "", + description: String? = "", + dog_friendly: Int = 6, + energy_level: Int = 7, + experimental: Int = 8, + grooming: Int = 9, + hairless: Int = 10, + health_issues: Int = 11, + hypoallergenic: Int = 12, + id: String? = "", + image: Image? = Image(1, "", "", 1), + indoor: Int = 13, + intelligence: Int = 14, + lap: Int = 15, + life_span: String? = "", + name: String? = "", + natural: Int = 16, + origin: String? = "", + rare: Int = 17, + reference_image_id: String? = "", + rex: Int = 18, + shedding_level: Int = 19, + short_legs: Int = 20, + social_needs: Int = 21, + stranger_friendly: Int = 22, + suppressed_tail: Int = 23, + temperament: String? = "", + vcahospitals_url: String? = "", + vetstreet_url: String? = "", + vocalisation: Int = 24, + weight: Weight? = Weight("", ""), + wikipedia_url: String? = "" + ): BreedItem = BreedItem( + adaptability = adaptability, + affection_level = affection_level, + alt_names = alt_names, + bidability = bidability, + cat_friendly = cat_friendly, + cfa_url = cfa_url, + child_friendly = child_friendly, + country_code = country_code, + country_codes = country_codes, + description = description, + dog_friendly = dog_friendly, + energy_level = energy_level, + experimental = experimental, + grooming = grooming, + hairless = hairless, + health_issues = health_issues, + hypoallergenic = hypoallergenic, + id = id, + image = image, + indoor = indoor, + intelligence = intelligence, + lap = lap, + life_span = life_span, + name = name, + natural = natural, + origin = origin, + rare = rare, + reference_image_id = reference_image_id, + rex = rex, + shedding_level = shedding_level, + short_legs = short_legs, + social_needs = social_needs, + stranger_friendly = stranger_friendly, + suppressed_tail = suppressed_tail, + temperament = temperament, + vcahospitals_url = vcahospitals_url, + vetstreet_url = vetstreet_url, + vocalisation = vocalisation, + weight = weight, + wikipedia_url = wikipedia_url + ) +} diff --git a/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/utils/InstantExecutorExtension.kt b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/utils/InstantExecutorExtension.kt new file mode 100644 index 0000000..31f4869 --- /dev/null +++ b/Android/pawan/IntuitiveCats/app/src/test/java/com/example/intuitivecats/utils/InstantExecutorExtension.kt @@ -0,0 +1,26 @@ +package com.example.intuitivecats.utils + +import androidx.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +// ref: https://jeroenmols.com/blog/2019/01/17/livedatajunit5/ +class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback { + + override fun beforeEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance() + .setDelegate(object : TaskExecutor() { + override fun executeOnDiskIO(runnable: Runnable) = runnable.run() + + override fun postToMainThread(runnable: Runnable) = runnable.run() + + override fun isMainThread(): Boolean = true + }) + } + + override fun afterEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance().setDelegate(null) + } +} diff --git a/Android/pawan/IntuitiveCats/build.gradle b/Android/pawan/IntuitiveCats/build.gradle new file mode 100644 index 0000000..c2f487c --- /dev/null +++ b/Android/pawan/IntuitiveCats/build.gradle @@ -0,0 +1,30 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + // other repositories... + mavenCentral() + } + dependencies { + // other plugins... + classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44' + classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" + } +} + +plugins { + id 'com.android.application' version '7.2.2' apply false + id 'com.android.library' version '7.2.2' apply false + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false +} + +//allprojects { +// configurations.all { +// resolutionStrategy.force 'org.objenesis:objenesis:2.6' +// } +//} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/gradle.properties b/Android/pawan/IntuitiveCats/gradle.properties new file mode 100644 index 0000000..cd0519b --- /dev/null +++ b/Android/pawan/IntuitiveCats/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.jar b/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.properties b/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8d89b5e --- /dev/null +++ b/Android/pawan/IntuitiveCats/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Nov 16 16:51:44 IST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/Android/pawan/IntuitiveCats/gradlew b/Android/pawan/IntuitiveCats/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/Android/pawan/IntuitiveCats/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/Android/pawan/IntuitiveCats/gradlew.bat b/Android/pawan/IntuitiveCats/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/Android/pawan/IntuitiveCats/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Android/pawan/IntuitiveCats/pagerv/.gitignore b/Android/pawan/IntuitiveCats/pagerv/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/build.gradle b/Android/pawan/IntuitiveCats/pagerv/build.gradle new file mode 100644 index 0000000..2c7c064 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/build.gradle @@ -0,0 +1,103 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' +} + +android { + compileSdk 32 + + defaultConfig { + minSdk 21 + targetSdk 32 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + viewBinding { + enabled = true + } + + testOptions { + unitTests { + all { + useJUnitPlatform() + testLogging { + events = ["passed", "skipped", "failed"] + showExceptions = true + showCauses = true + showStackTraces = true + afterSuite { descriptor, result -> + if (!descriptor.parent) { // will match the outermost suite + println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" + } + } + } + } + includeAndroidResources = true + returnDefaultValues = true + } + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'com.google.android.material:material:1.7.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.4' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + + //paging + implementation 'androidx.paging:paging-common-ktx:3.1.1' + implementation 'androidx.paging:paging-runtime-ktx:3.1.1' + + implementation 'javax.inject:javax.inject:1' + + //glide + implementation 'com.github.bumptech.glide:glide:4.14.2' + kapt 'com.github.bumptech.glide:compiler:4.14.2' + + def junit5Version = "5.9.1" + + /*Unit testing Dependencies*/ + testImplementation "org.junit.jupiter:junit-jupiter-api:${junit5Version}" + testImplementation "org.junit.jupiter:junit-jupiter-params:${junit5Version}" + testImplementation "org.junit.jupiter:junit-jupiter-migrationsupport:${junit5Version}" + + testImplementation "org.mockito:mockito-core:3.0.0" + testImplementation "io.mockk:mockk:1.10.5" + testImplementation "com.squareup.okhttp3:mockwebserver:4.9.0" + testImplementation 'org.robolectric:robolectric:4.5.1' + testImplementation 'androidx.test:core:1.4.0' + testImplementation 'androidx.test.ext:junit:1.1.3' + testImplementation 'androidx.test:runner:1.4.0' + testImplementation "androidx.test:core-ktx:1.4.0" + testImplementation 'org.robolectric:robolectric:4.5.1' + + testImplementation "org.assertj:assertj-core:3.18.0" + + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit5Version}" + testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit5Version}" + + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' +} + +kapt { + correctErrorTypes true +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/consumer-rules.pro b/Android/pawan/IntuitiveCats/pagerv/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/Android/pawan/IntuitiveCats/pagerv/proguard-rules.pro b/Android/pawan/IntuitiveCats/pagerv/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/androidTest/java/com/example/pagerv/ExampleInstrumentedTest.kt b/Android/pawan/IntuitiveCats/pagerv/src/androidTest/java/com/example/pagerv/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..aca28f9 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/androidTest/java/com/example/pagerv/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.pagerv + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.pagerv.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/AndroidManifest.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9b33519 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/LoadMoreAdapter.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/LoadMoreAdapter.kt new file mode 100644 index 0000000..efc661d --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/LoadMoreAdapter.kt @@ -0,0 +1,39 @@ +package com.example.pagerv + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.paging.LoadState +import androidx.paging.LoadStateAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.pagerv.databinding.ItemLoadMoreBinding + +class LoadMoreAdapter(private val retry: () -> Unit) : + LoadStateAdapter() { + + private lateinit var binding: ItemLoadMoreBinding + + inner class ViewHolder(retry: () -> Unit) : RecyclerView.ViewHolder(binding.root) { + + init { + binding.btnLoadMoreRetry.setOnClickListener { retry() } + } + + fun setData(state: LoadState) { + binding.apply { + prgBarLoadMore.isVisible = state is LoadState.Loading + tvLoadMore.isVisible = state is LoadState.Error + btnLoadMoreRetry.isVisible = state is LoadState.Error + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): ViewHolder { + binding = ItemLoadMoreBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ViewHolder(retry) + } + + override fun onBindViewHolder(holder: ViewHolder, loadState: LoadState) { + holder.setData(loadState) + } +} diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/PagerDataAdapter.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/PagerDataAdapter.kt new file mode 100644 index 0000000..9336af3 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/PagerDataAdapter.kt @@ -0,0 +1,75 @@ +package com.example.pagerv + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagingDataAdapter + +import androidx.recyclerview.widget.DiffUtil + +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide + +import com.example.pagerv.paging.DataItem +import com.example.pagerv.databinding.ItemDataBinding + +class PagerDataAdapter : + PagingDataAdapter(diffCallback = differCallback) { + + inner class CatViewHolder(private val binding: ItemDataBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(cat: DataItem) { + Glide.with(binding.root.context) + .load(cat.imageUrl) + .placeholder(R.drawable.ic_baseline_pets_24) + .error(R.drawable.ic_baseline_pets_24) + .centerCrop() + .into(binding.catpic) + + binding.apply { + breedName.text = cat.title + countryName.text = cat.subTitle + + root.setOnClickListener { + onItemClickListener?.let { + it(cat) + } + } + + } + } + + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatViewHolder { + return CatViewHolder( + ItemDataBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: CatViewHolder, position: Int) { + holder.bind(getItem(position)!!) + } + + private var onItemClickListener: ((DataItem) -> Unit)? = null + + fun setOnItemClickListener(listener: (DataItem) -> Unit) { + onItemClickListener = listener + } + + companion object { + val differCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean { + return oldItem.id == oldItem.id + } + + override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/ApiRepository.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/ApiRepository.kt new file mode 100644 index 0000000..adae792 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/ApiRepository.kt @@ -0,0 +1,5 @@ +package com.example.pagerv.paging + +interface ApiRepository { + suspend fun getData(currentPage: Int, loadSize: Int): List +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataItem.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataItem.kt new file mode 100644 index 0000000..de7cb54 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataItem.kt @@ -0,0 +1,8 @@ +package com.example.pagerv.paging + +data class DataItem( + val id: String?, + val title: String?, + val subTitle: String?, + val imageUrl: String? +) \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataPagingSource.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataPagingSource.kt new file mode 100644 index 0000000..2a4f8c9 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/java/com/example/pagerv/paging/DataPagingSource.kt @@ -0,0 +1,37 @@ +package com.example.pagerv.paging + +import androidx.lifecycle.LiveData +import androidx.paging.* + +import javax.inject.Inject + + +class DataPagingSource @Inject constructor( + private val catRepository: ApiRepository, + private var loadSize: Int +) : PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return null + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 0 + val response = catRepository.getData(currentPage, loadSize) + LoadResult.Page( + data = response, + prevKey = if (currentPage == 0) null else currentPage.minus(1), + nextKey = if (response.size < loadSize) null else currentPage.plus(1) + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } +} + +class PageRv(private val repository: ApiRepository, loadSize: Int = 50) : + LiveData>() { + val data = Pager(PagingConfig(1)) { + DataPagingSource(repository, loadSize) + }.liveData +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/paging/ApiRepository.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/ApiRepository.kt new file mode 100644 index 0000000..ddd4247 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/ApiRepository.kt @@ -0,0 +1,4 @@ +package com.example.intuitivecats.paging + +abstract class ApiRepository { +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataItem.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataItem.kt new file mode 100644 index 0000000..d6d90d6 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataItem.kt @@ -0,0 +1,8 @@ +package com.example.intuitivecats.paging + +data class DataItem( + val id: String, + val title: String, + val subTitle: String, + val imageUrl: String +) \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataPagingSource.kt b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataPagingSource.kt new file mode 100644 index 0000000..8c04367 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/paging/DataPagingSource.kt @@ -0,0 +1,42 @@ +package com.example.intuitivecats.paging + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.example.intuitivecats.CatRepository +import com.example.intuitivecats.model.BreedItem +import retrofit2.HttpException +import javax.inject.Inject + + +internal class DataPagingSource @Inject constructor( + private val catRepository: CatRepository +) : PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return null + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 0 + val loadSize = 25 + val response = catRepository.getBreeds(currentPage, loadSize) + LoadResult.Page( + data = response, + prevKey = if (currentPage == 0) null else -1, + nextKey = if (response.size < loadSize) null else currentPage.plus(1) + ) + } catch (e: Exception) { + LoadResult.Error(e) + } catch (exception: HttpException) { + LoadResult.Error(exception) + } + } +} + +class PagedRecyclerView( + val repo: ApiRepository, + val loadSize: Int, + +) { + +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/bg_circle_white.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/bg_circle_white.xml new file mode 100644 index 0000000..9c8b75c --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/bg_circle_white.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml new file mode 100644 index 0000000..e545abc --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_check_circle_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_close_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_close_24.xml new file mode 100644 index 0000000..70db409 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_close_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml new file mode 100644 index 0000000..996e16d --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_favorite_border_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_info_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_info_24.xml new file mode 100644 index 0000000..654cfb3 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_info_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml new file mode 100644 index 0000000..89a0ff5 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_line_weight_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_location_on_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_location_on_24.xml new file mode 100644 index 0000000..dd12113 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_location_on_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_pets_24.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_pets_24.xml new file mode 100644 index 0000000..df83859 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_baseline_pets_24.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/loadingcat.gif b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/loadingcat.gif new file mode 100644 index 0000000..25949a4 Binary files /dev/null and b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable-v24/loadingcat.gif differ diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/bg_gradient.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/bg_gradient.xml new file mode 100644 index 0000000..a6f1650 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/bg_gradient.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/caty.jpeg b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/caty.jpeg new file mode 100644 index 0000000..640feef Binary files /dev/null and b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/caty.jpeg differ diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/ic_launcher_background.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/splash_background.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/splash_background.xml new file mode 100644 index 0000000..ccedbf7 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/drawable/splash_background.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_data.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_data.xml new file mode 100644 index 0000000..0898c3c --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_data.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_load_more.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_load_more.xml new file mode 100644 index 0000000..694fc3e --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/layout/item_load_more.xml @@ -0,0 +1,40 @@ + + + + + + + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/colors.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/colors.xml new file mode 100644 index 0000000..20df6f0 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/colors.xml @@ -0,0 +1,11 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #F7f5bc + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/strings.xml b/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/strings.xml new file mode 100644 index 0000000..4f6577f --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/main/res/values/strings.xml @@ -0,0 +1,25 @@ + + IntuitiveCats + Settings + + First Fragment + Second Fragment + Next + Previous + + Hello first fragment + Hello second fragment. Arg: %1$s + + Hello blank fragment + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nec leo ipsum. Phasellus ac ornare velit, a sodales mi. Etiam a dui ante. Duis ullamcorper dapibus sapien, quis congue metus ornare ac. Nullam vel semper nulla. + kilograms + years + Description + More info + Done + Breed Name + Country name + Mood + start now + Learn more about us so you can become a good Karen! + \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/paging/DataPagingSourceTest.kt b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/paging/DataPagingSourceTest.kt new file mode 100644 index 0000000..0adc393 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/paging/DataPagingSourceTest.kt @@ -0,0 +1,127 @@ +package com.example.pagerv.paging + +import androidx.paging.PagingSource +import com.example.pagerv.utils.InstantExecutorExtension +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.impl.annotations.MockK +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExperimentalCoroutinesApi +@ExtendWith(InstantExecutorExtension::class) +class DataPagingSourceTest { + + @MockK + lateinit var api: ApiRepository + + private lateinit var dataPagingSource: DataPagingSource + + @BeforeEach + fun setup() { + MockKAnnotations.init(this, relaxed = true, relaxUnitFun = true) + dataPagingSource = DataPagingSource(api, 2) + } + + @Test + fun `data paging source load - failure - http error`() = runTest { + val error = RuntimeException("404", Throwable()) + coEvery { + api.getData(0, 2) + } throws error + + val expectedResult = PagingSource.LoadResult.Error(error) + assertEquals( + expectedResult, dataPagingSource.load( + PagingSource.LoadParams.Refresh( + key = 0, + loadSize = 2, + placeholdersEnabled = false + ) + ) + ) + } + + @Test + fun `data paging source refresh - success`() = runTest { + val items = listOf( + DataItem("one", "", "", ""), + DataItem("two", "", "", "") + ) + coEvery { + api.getData(0, 2) + } returns items + + val expectedResult = PagingSource.LoadResult.Page( + data = items, + prevKey = null, + nextKey = 1 + ) + assertEquals( + expectedResult, dataPagingSource.load( + PagingSource.LoadParams.Refresh( + key = 0, + loadSize = 2, + placeholdersEnabled = false + ) + ) + ) + } + + @Test + fun `data paging source append - success`() = runTest { + + val nextItems = listOf( + DataItem("three", "", "", ""), + DataItem("four", "", "", "") + ) + coEvery { + api.getData(any(), any()) + } returns nextItems + + val expectedResult = PagingSource.LoadResult.Page( + data = nextItems, + prevKey = 0, + nextKey = 2 + ) + assertEquals( + expectedResult, dataPagingSource.load( + PagingSource.LoadParams.Append( + key = 1, + loadSize = 2, + placeholdersEnabled = false + ) + ) + ) + } + + @Test + fun `data paging source prepend - success`() = runTest { + val items = listOf( + DataItem("three", "", "", ""), + DataItem("four", "", "", "") + ) + coEvery { + api.getData(any(), any()) + } returns items + + val expectedResult = PagingSource.LoadResult.Page( + data = items, + prevKey = null, + nextKey = 1 + ) + assertEquals( + expectedResult, dataPagingSource.load( + PagingSource.LoadParams.Prepend( + key = 0, + loadSize = 2, + placeholdersEnabled = false + ) + ) + ) + } +} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/CoroutineTestRule.kt b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/CoroutineTestRule.kt new file mode 100644 index 0000000..fb294f4 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/CoroutineTestRule.kt @@ -0,0 +1,42 @@ +package com.example.pagerv.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.rules.TestWatcher + +import org.junit.runner.Description + +@ExperimentalCoroutinesApi +class CoroutineTestRule : TestWatcher() { + +// val testDispatcherProvider = object : DispatcherProvider { +// override val io: CoroutineDispatcher = testDispatcher +// override val ui: CoroutineDispatcher = testDispatcher +// override val default: CoroutineDispatcher = testDispatcher +// override val unconfined: CoroutineDispatcher = testDispatcher +// } + + private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher() + + override fun starting(description: Description) { + super.starting(description) + Dispatchers.setMain(testDispatcher) + } + + override fun finished(description: Description) { + super.finished(description) + Dispatchers.resetMain() + testDispatcher.cleanupTestCoroutines() + } +} + +//interface DispatcherProvider { +// val io: CoroutineDispatcher +// val ui: CoroutineDispatcher +// val default: CoroutineDispatcher +// val unconfined: CoroutineDispatcher +//} \ No newline at end of file diff --git a/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/InstantExecutorExtension.kt b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/InstantExecutorExtension.kt new file mode 100644 index 0000000..bc5dbb1 --- /dev/null +++ b/Android/pawan/IntuitiveCats/pagerv/src/test/java/com/example/pagerv/utils/InstantExecutorExtension.kt @@ -0,0 +1,26 @@ +package com.example.pagerv.utils + +import androidx.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +// ref: https://jeroenmols.com/blog/2019/01/17/livedatajunit5/ +class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback { + + override fun beforeEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance() + .setDelegate(object : TaskExecutor() { + override fun executeOnDiskIO(runnable: Runnable) = runnable.run() + + override fun postToMainThread(runnable: Runnable) = runnable.run() + + override fun isMainThread(): Boolean = true + }) + } + + override fun afterEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance().setDelegate(null) + } +} diff --git a/Android/pawan/IntuitiveCats/settings.gradle b/Android/pawan/IntuitiveCats/settings.gradle new file mode 100644 index 0000000..b5ca82a --- /dev/null +++ b/Android/pawan/IntuitiveCats/settings.gradle @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "IntuitiveCats" +include ':app' +include ':pagerv'