├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── layout
│ │ │ ├── layout_frame.xml
│ │ │ ├── layout_question_list_item.xml
│ │ │ ├── layout_view_model.xml
│ │ │ ├── layout_questions_list.xml
│ │ │ ├── layout_my_toolbar.xml
│ │ │ └── layout_question_details.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── drawable-v24
│ │ │ ├── ic_navigate_up.xml
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ └── com
│ │ │ └── techyourchance
│ │ │ └── dagger2course
│ │ │ ├── common
│ │ │ ├── dependnecyinjection
│ │ │ │ ├── app
│ │ │ │ │ ├── AppScope.kt
│ │ │ │ │ ├── AppComponent.kt
│ │ │ │ │ └── AppModule.kt
│ │ │ │ ├── Retrofit1.kt
│ │ │ │ ├── Retrofit2.kt
│ │ │ │ ├── activity
│ │ │ │ │ ├── ActivityScope.kt
│ │ │ │ │ ├── ActivityComponent.kt
│ │ │ │ │ └── ActivityModule.kt
│ │ │ │ ├── presentation
│ │ │ │ │ ├── PresentationScope.kt
│ │ │ │ │ ├── ViewModelKey.kt
│ │ │ │ │ ├── PresentationModule.kt
│ │ │ │ │ ├── ViewModelModule.kt
│ │ │ │ │ └── PresentationComponent.kt
│ │ │ │ └── service
│ │ │ │ │ ├── ServiceComponent.kt
│ │ │ │ │ └── ServiceModule.kt
│ │ │ └── service
│ │ │ │ └── BaseService.kt
│ │ │ ├── Constants.kt
│ │ │ ├── screens
│ │ │ ├── common
│ │ │ │ ├── ScreensNavigator.kt
│ │ │ │ ├── viewmodels
│ │ │ │ │ ├── SavedStateViewModel.kt
│ │ │ │ │ └── ViewModelFactory.kt
│ │ │ │ ├── dialogs
│ │ │ │ │ ├── DialogsNavigator.kt
│ │ │ │ │ ├── BaseDialog.kt
│ │ │ │ │ └── ServerErrorDialogFragment.kt
│ │ │ │ ├── fragments
│ │ │ │ │ └── BaseFragment.kt
│ │ │ │ ├── imageloader
│ │ │ │ │ └── ImageLoader.kt
│ │ │ │ ├── ScreensNavigatorImpl.kt
│ │ │ │ ├── activities
│ │ │ │ │ └── BaseActivity.kt
│ │ │ │ ├── viewsmvc
│ │ │ │ │ ├── ViewMvcFactory.kt
│ │ │ │ │ └── BaseViewMvc.kt
│ │ │ │ └── toolbar
│ │ │ │ │ └── MyToolbar.kt
│ │ │ ├── questionslist
│ │ │ │ ├── QuestionsListActivity.kt
│ │ │ │ ├── QuestionsListFragment.kt
│ │ │ │ └── QuestionsListViewMvc.kt
│ │ │ ├── viewmodel
│ │ │ │ ├── MyViewModel2.kt
│ │ │ │ ├── MyViewModel.kt
│ │ │ │ └── ViewModelActivity.kt
│ │ │ └── questiondetails
│ │ │ │ ├── QuestionDetailsViewMvc.kt
│ │ │ │ └── QuestionDetailsActivity.kt
│ │ │ ├── users
│ │ │ └── User.kt
│ │ │ ├── networking
│ │ │ ├── QuestionsListResponseSchema.kt
│ │ │ ├── UrlProvider.kt
│ │ │ ├── SingleQuestionResponseSchema.kt
│ │ │ └── StackoverflowApi.kt
│ │ │ ├── questions
│ │ │ ├── Question.kt
│ │ │ ├── QuestionWithBody.kt
│ │ │ ├── FetchQuestionsUseCase.kt
│ │ │ └── FetchQuestionDetailsUseCase.kt
│ │ │ └── MyApplication.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/techyourchance/course-android-dependency-injection-with-dagger-2/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 54dp
3 | 54dp
4 | 24dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/app/AppScope.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.app
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope
6 | annotation class AppScope {
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/Retrofit1.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier
6 | annotation class Retrofit1 {
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/Retrofit2.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier
6 | annotation class Retrofit2 {
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course
2 |
3 | object Constants {
4 | const val BASE_URL = "https://api.stackexchange.com/2.2/"
5 | const val STACKOVERFLOW_API_KEY = "ZiXCZbWaOwnDgpVT9Hx8IA(("
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/activity/ActivityScope.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.activity
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope
6 | annotation class ActivityScope {
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/presentation/PresentationScope.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.presentation
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope
6 | annotation class PresentationScope {
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/ScreensNavigator.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common
2 |
3 | interface ScreensNavigator {
4 |
5 | fun navigateBack()
6 |
7 | fun toQuestionDetails(questionId: String)
8 | fun toViewModel()
9 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 15 12:21:12 IDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_frame.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/service/ServiceComponent.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.service
2 |
3 | import dagger.Subcomponent
4 |
5 | @Subcomponent(modules = [ServiceModule::class])
6 | interface ServiceComponent {
7 |
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/users/User.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.users
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class User(
6 | @SerializedName("display_name") val name: String,
7 | @SerializedName("profile_image") val imageUrl: String
8 | )
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Dagger 2 Course
3 | Server Error
4 | Communication with the server failed
5 | OK
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/networking/QuestionsListResponseSchema.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.networking
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.techyourchance.dagger2course.questions.Question
5 |
6 | data class QuestionsListResponseSchema(@SerializedName("items") val questions: List)
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/presentation/ViewModelKey.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.presentation
2 |
3 | import androidx.lifecycle.ViewModel
4 | import dagger.MapKey
5 | import kotlin.reflect.KClass
6 |
7 | @MapKey
8 | annotation class ViewModelKey(val value: KClass)
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/questions/Question.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.questions
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import java.io.Serializable
5 |
6 | data class Question(
7 | @SerializedName("title") val title: String,
8 | @SerializedName("question_id") val id: String
9 | ): Serializable
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/viewmodels/SavedStateViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.viewmodels
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 |
6 | abstract class SavedStateViewModel: ViewModel() {
7 | abstract fun init(savedStateHandle: SavedStateHandle)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/networking/UrlProvider.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.networking
2 |
3 | import com.techyourchance.dagger2course.Constants
4 |
5 | class UrlProvider {
6 |
7 | fun getBaseUrl1(): String {
8 | return Constants.BASE_URL
9 | }
10 |
11 | fun getBaseUrl2(): String {
12 | return "base_url"
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_navigate_up.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #000000
8 | #FFFFFF
9 | #00000000
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/networking/SingleQuestionResponseSchema.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.networking
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.techyourchance.dagger2course.questions.QuestionWithBody
5 |
6 | data class SingleQuestionResponseSchema(@SerializedName("items") val questions: List) {
7 | val question: QuestionWithBody get() = questions[0]
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/service/ServiceModule.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.service
2 |
3 | import android.app.Service
4 | import android.content.Context
5 | import dagger.Module
6 | import dagger.Provides
7 |
8 | @Module
9 | class ServiceModule(
10 | val service: Service
11 | ) {
12 |
13 | @Provides
14 | fun context(): Context = service
15 |
16 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #built application files
3 | *.apk
4 | *.ap_
5 |
6 | # files for the dex VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # generated files
13 | bin/
14 | gen/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | # Windows thumbnail db
20 | Thumbs.db
21 |
22 | # OSX files
23 | .DS_Store
24 |
25 | # Android Studio
26 | .idea/
27 | .gradle
28 | build/
29 | *.iml
30 | captures/
31 | .externalNativeBuild
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/presentation/PresentationModule.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.presentation
2 |
3 | import androidx.savedstate.SavedStateRegistryOwner
4 | import dagger.Module
5 | import dagger.Provides
6 |
7 | @Module
8 | class PresentationModule(private val savedStateRegistryOwner: SavedStateRegistryOwner) {
9 |
10 | @Provides
11 | fun savedStateRegistryOwner() = savedStateRegistryOwner
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/questions/QuestionWithBody.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.questions
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.techyourchance.dagger2course.users.User
5 |
6 | data class QuestionWithBody(
7 | @SerializedName("title") val title: String,
8 | @SerializedName("question_id") val id: String,
9 | @SerializedName("body") val body: String,
10 | @SerializedName("owner") val owner: User
11 | )
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/dialogs/DialogsNavigator.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.dialogs
2 |
3 | import androidx.fragment.app.FragmentManager
4 | import javax.inject.Inject
5 |
6 | class DialogsNavigator @Inject constructor(private val fragmentManager: FragmentManager) {
7 |
8 | fun showServerErrorDialog() {
9 | fragmentManager.beginTransaction()
10 | .add(ServerErrorDialogFragment.newInstance(), null)
11 | .commitAllowingStateLoss()
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/service/BaseService.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.service
2 |
3 | import android.app.Service
4 | import com.techyourchance.dagger2course.MyApplication
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.service.ServiceModule
6 |
7 | abstract class BaseService: Service() {
8 |
9 | private val appComponent get() = (application as MyApplication).appComponent
10 |
11 | val serviceComponent by lazy {
12 | appComponent.newServiceComponent(ServiceModule(this))
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/fragments/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.fragments
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.presentation.PresentationModule
5 | import com.techyourchance.dagger2course.screens.common.activities.BaseActivity
6 |
7 | open class BaseFragment: Fragment() {
8 |
9 | private val presentationComponent by lazy {
10 | (requireActivity() as BaseActivity).activityComponent.newPresentationComponent(PresentationModule(this))
11 | }
12 |
13 | protected val injector get() = presentationComponent
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/dialogs/BaseDialog.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.dialogs
2 |
3 | import androidx.fragment.app.DialogFragment
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.presentation.PresentationModule
5 | import com.techyourchance.dagger2course.screens.common.activities.BaseActivity
6 |
7 | open class BaseDialog: DialogFragment() {
8 |
9 | private val presentationComponent by lazy {
10 | (requireActivity() as BaseActivity).activityComponent.newPresentationComponent(PresentationModule(this))
11 | }
12 |
13 | protected val injector get() = presentationComponent
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course
2 |
3 | import android.app.Application
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.app.AppComponent
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.app.AppModule
6 | import com.techyourchance.dagger2course.common.dependnecyinjection.app.DaggerAppComponent
7 |
8 | class MyApplication: Application() {
9 |
10 | public val appComponent: AppComponent by lazy {
11 | DaggerAppComponent.builder()
12 | .appModule(AppModule(this))
13 | .build()
14 | }
15 |
16 | override fun onCreate() {
17 | super.onCreate()
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_question_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/app/AppComponent.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.app
2 |
3 | import com.techyourchance.dagger2course.common.dependnecyinjection.activity.ActivityComponent
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.service.ServiceComponent
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.service.ServiceModule
6 | import dagger.Component
7 |
8 | @AppScope
9 | @Component(modules = [AppModule::class])
10 | interface AppComponent {
11 |
12 | fun newActivityComponentBuilder(): ActivityComponent.Builder
13 |
14 | fun newServiceComponent(serviceModule: ServiceModule): ServiceComponent
15 |
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/questionslist/QuestionsListActivity.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.questionslist
2 |
3 | import android.os.Bundle
4 | import com.techyourchance.dagger2course.R
5 | import com.techyourchance.dagger2course.screens.common.activities.BaseActivity
6 |
7 | class QuestionsListActivity : BaseActivity() {
8 |
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.layout_frame)
12 |
13 | if (savedInstanceState == null) {
14 | supportFragmentManager.beginTransaction()
15 | .add(R.id.frame_content, QuestionsListFragment())
16 | .commit()
17 | }
18 |
19 | }
20 |
21 |
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/imageloader/ImageLoader.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.imageloader
2 |
3 | import android.widget.ImageView
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.bumptech.glide.Glide
6 | import com.bumptech.glide.request.RequestOptions
7 | import com.techyourchance.dagger2course.common.dependnecyinjection.activity.ActivityScope
8 | import javax.inject.Inject
9 |
10 | @ActivityScope
11 | class ImageLoader @Inject constructor(private val activity: AppCompatActivity) {
12 |
13 | private val requestOptions = RequestOptions().centerCrop()
14 |
15 | fun loadImage(imageUrl: String, target: ImageView) {
16 | Glide.with(activity).load(imageUrl).apply(requestOptions).into(target)
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/networking/StackoverflowApi.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.networking
2 |
3 | import com.techyourchance.dagger2course.Constants
4 | import retrofit2.Response
5 | import retrofit2.http.GET
6 | import retrofit2.http.Path
7 | import retrofit2.http.Query
8 |
9 | interface StackoverflowApi {
10 | @GET("/questions?key=" + Constants.STACKOVERFLOW_API_KEY + "&order=desc&sort=activity&site=stackoverflow")
11 | suspend fun lastActiveQuestions(@Query("pagesize") pageSize: Int?): Response
12 |
13 | @GET("/questions/{questionId}?key=" + Constants.STACKOVERFLOW_API_KEY + "&site=stackoverflow&filter=withbody")
14 | suspend fun questionDetails(@Path("questionId") questionId: String?): Response
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/ScreensNavigatorImpl.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import com.techyourchance.dagger2course.screens.questiondetails.QuestionDetailsActivity
5 | import com.techyourchance.dagger2course.screens.viewmodel.ViewModelActivity
6 | import javax.inject.Inject
7 |
8 | class ScreensNavigatorImpl @Inject constructor(private val activity: AppCompatActivity): ScreensNavigator {
9 |
10 | override fun navigateBack() {
11 | activity.onBackPressed()
12 | }
13 |
14 | override fun toQuestionDetails(questionId: String) {
15 | QuestionDetailsActivity.start(activity, questionId)
16 | }
17 |
18 | override fun toViewModel() {
19 | ViewModelActivity.start(activity)
20 | }
21 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/presentation/ViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.presentation
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.techyourchance.dagger2course.screens.viewmodel.MyViewModel
5 | import com.techyourchance.dagger2course.screens.viewmodel.MyViewModel2
6 | import dagger.Binds
7 | import dagger.Module
8 | import dagger.Provides
9 | import dagger.multibindings.IntoMap
10 |
11 | @Module
12 | abstract class ViewModelModule {
13 |
14 | @Binds
15 | @IntoMap
16 | @ViewModelKey(MyViewModel::class)
17 | abstract fun myViewModel(myViewModel: MyViewModel): ViewModel
18 |
19 | @Binds
20 | @IntoMap
21 | @ViewModelKey(MyViewModel2::class)
22 | abstract fun myViewModel2(myViewModel2: MyViewModel2): ViewModel
23 |
24 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | android.enableJetifier=true
13 | android.useAndroidX=true
14 | org.gradle.jvmargs=-Xmx1536m
15 |
16 | # When configured, Gradle will run in incubating parallel mode.
17 | # This option should only be used with decoupled projects. More details, visit
18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
19 | # org.gradle.parallel=true
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/viewmodel/MyViewModel2.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.viewmodel
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.techyourchance.dagger2course.questions.FetchQuestionDetailsUseCase
7 | import com.techyourchance.dagger2course.questions.FetchQuestionsUseCase
8 | import com.techyourchance.dagger2course.questions.Question
9 | import javax.inject.Inject
10 |
11 | class MyViewModel2 @Inject constructor(
12 | private val fetchQuestionsUseCase: FetchQuestionsUseCase,
13 | private val fetchQuestionDetailsUseCase: FetchQuestionDetailsUseCase
14 | ): ViewModel() {
15 |
16 | private val _questions = MutableLiveData>()
17 | val questions: LiveData> = _questions
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/activities/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.activities
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import com.techyourchance.dagger2course.MyApplication
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.presentation.PresentationModule
6 |
7 | open class BaseActivity: AppCompatActivity() {
8 |
9 | private val appComponent get() = (application as MyApplication).appComponent
10 |
11 | val activityComponent by lazy {
12 | appComponent.newActivityComponentBuilder()
13 | .activity(this)
14 | .build()
15 | }
16 |
17 | private val presentationComponent by lazy {
18 | activityComponent.newPresentationComponent(PresentationModule(this))
19 | }
20 |
21 | protected val injector get() = presentationComponent
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/activity/ActivityComponent.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.activity
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.presentation.PresentationComponent
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.presentation.PresentationModule
6 | import dagger.BindsInstance
7 | import dagger.Subcomponent
8 |
9 | @ActivityScope
10 | @Subcomponent(modules = [ActivityModule::class])
11 | interface ActivityComponent {
12 |
13 | fun newPresentationComponent(presentationModule: PresentationModule): PresentationComponent
14 |
15 | @Subcomponent.Builder
16 | interface Builder {
17 | @BindsInstance fun activity(activity: AppCompatActivity): Builder
18 | fun build(): ActivityComponent
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/presentation/PresentationComponent.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.presentation
2 |
3 | import com.techyourchance.dagger2course.screens.questiondetails.QuestionDetailsActivity
4 | import com.techyourchance.dagger2course.screens.questionslist.QuestionsListActivity
5 | import com.techyourchance.dagger2course.screens.questionslist.QuestionsListFragment
6 | import com.techyourchance.dagger2course.screens.viewmodel.ViewModelActivity
7 | import dagger.Subcomponent
8 |
9 | @PresentationScope
10 | @Subcomponent(modules = [PresentationModule::class, ViewModelModule::class])
11 | interface PresentationComponent {
12 | fun inject(fragment: QuestionsListFragment)
13 | fun inject(activity: QuestionDetailsActivity)
14 | fun inject(questionsListActivity: QuestionsListActivity)
15 | fun inject(viewModelActivity: ViewModelActivity)
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/dialogs/ServerErrorDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.dialogs
2 |
3 | import android.app.AlertDialog
4 | import android.app.Dialog
5 | import android.os.Bundle
6 | import com.techyourchance.dagger2course.R
7 |
8 | class ServerErrorDialogFragment : BaseDialog() {
9 |
10 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
11 | return AlertDialog.Builder(activity).let {
12 | it.setTitle(R.string.server_error_dialog_title)
13 | it.setMessage(R.string.server_error_dialog_message)
14 | it.setPositiveButton(R.string.server_error_dialog_button_caption) { _, _ -> dismiss() }
15 | it.create()
16 | }
17 | }
18 |
19 | companion object {
20 | fun newInstance(): ServerErrorDialogFragment {
21 | return ServerErrorDialogFragment()
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/activity/ActivityModule.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.activity
2 |
3 | import android.view.LayoutInflater
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.techyourchance.dagger2course.screens.common.ScreensNavigator
6 | import com.techyourchance.dagger2course.screens.common.ScreensNavigatorImpl
7 | import dagger.Binds
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | @Module
12 | abstract class ActivityModule {
13 |
14 | @ActivityScope
15 | @Binds
16 | abstract fun screensNavigator(screensNavigatorImpl: ScreensNavigatorImpl): ScreensNavigator
17 |
18 | companion object {
19 | @Provides
20 | fun layoutInflater(activity: AppCompatActivity) = LayoutInflater.from(activity)
21 |
22 | @Provides
23 | fun fragmentManager(activity: AppCompatActivity) = activity.supportFragmentManager
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_view_model.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_questions_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/viewsmvc/ViewMvcFactory.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.viewsmvc
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import com.techyourchance.dagger2course.screens.common.imageloader.ImageLoader
6 | import com.techyourchance.dagger2course.screens.questiondetails.QuestionDetailsViewMvc
7 | import com.techyourchance.dagger2course.screens.questionslist.QuestionsListViewMvc
8 | import javax.inject.Inject
9 | import javax.inject.Provider
10 |
11 | class ViewMvcFactory @Inject constructor(
12 | private val layoutInflaterProvider: Provider,
13 | private val imageLoaderProvider: Provider
14 | ) {
15 |
16 | fun newQuestionsListViewMvc(parent: ViewGroup?): QuestionsListViewMvc {
17 | return QuestionsListViewMvc(layoutInflaterProvider.get(), parent)
18 | }
19 |
20 | fun newQuestionDetailsViewMvc(parent: ViewGroup?): QuestionDetailsViewMvc {
21 | return QuestionDetailsViewMvc(layoutInflaterProvider.get(), imageLoaderProvider.get(), parent)
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/viewsmvc/BaseViewMvc.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.viewsmvc
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.annotation.IdRes
8 | import androidx.annotation.LayoutRes
9 |
10 | open class BaseViewMvc(
11 | private val layoutInflater: LayoutInflater,
12 | private val parent: ViewGroup?,
13 | @LayoutRes private val layoutId: Int
14 | ) {
15 |
16 | val rootView: View = layoutInflater.inflate(layoutId, parent, false)
17 |
18 | protected val context: Context get() = rootView.context
19 |
20 | protected val listeners = HashSet()
21 |
22 |
23 | fun registerListener(listener: LISTENER_TYPE) {
24 | listeners.add(listener)
25 | }
26 |
27 | fun unregisterListener(listener: LISTENER_TYPE) {
28 | listeners.remove(listener)
29 | }
30 |
31 | protected fun findViewById(@IdRes id: Int): T {
32 | return rootView.findViewById(id)
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/viewmodels/ViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.viewmodels
2 |
3 | import androidx.lifecycle.AbstractSavedStateViewModelFactory
4 | import androidx.lifecycle.SavedStateHandle
5 | import androidx.lifecycle.ViewModel
6 | import androidx.savedstate.SavedStateRegistryOwner
7 | import javax.inject.Inject
8 | import javax.inject.Provider
9 |
10 | class ViewModelFactory @Inject constructor(
11 | private val providersMap: Map, @JvmSuppressWildcards Provider>,
12 | savedStateRegistryOwner: SavedStateRegistryOwner
13 | ): AbstractSavedStateViewModelFactory(savedStateRegistryOwner, null) {
14 |
15 | override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T {
16 | val provider = providersMap[modelClass]
17 | val viewModel = provider?.get() ?: throw RuntimeException("unsupported viewmodel type: $modelClass")
18 | if (viewModel is SavedStateViewModel) {
19 | viewModel.init(handle)
20 | }
21 | return viewModel as T
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/questions/FetchQuestionsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.questions
2 |
3 | import com.techyourchance.dagger2course.networking.StackoverflowApi
4 | import kotlinx.coroutines.CancellationException
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import javax.inject.Inject
8 |
9 | class FetchQuestionsUseCase @Inject constructor(private val stackoverflowApi: StackoverflowApi) {
10 |
11 | sealed class Result {
12 | data class Success(val questions: List) : Result()
13 | object Failure: Result()
14 | }
15 |
16 | suspend fun fetchLatestQuestions(): Result {
17 | return withContext(Dispatchers.IO) {
18 | try {
19 | val response = stackoverflowApi.lastActiveQuestions(20)
20 | if (response.isSuccessful && response.body() != null) {
21 | return@withContext Result.Success(response.body()!!.questions)
22 | } else {
23 | return@withContext Result.Failure
24 | }
25 | } catch (t: Throwable) {
26 | if (t !is CancellationException) {
27 | return@withContext Result.Failure
28 | } else {
29 | throw t
30 | }
31 | }
32 | }
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/questions/FetchQuestionDetailsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.questions
2 |
3 | import com.techyourchance.dagger2course.networking.StackoverflowApi
4 | import kotlinx.coroutines.CancellationException
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import javax.inject.Inject
8 |
9 | class FetchQuestionDetailsUseCase @Inject constructor(private val stackoverflowApi: StackoverflowApi) {
10 |
11 | sealed class Result {
12 | data class Success(val question: QuestionWithBody) : Result()
13 | object Failure: Result()
14 | }
15 |
16 | suspend fun fetchQuestion(questionId: String): Result {
17 | return withContext(Dispatchers.IO) {
18 | try {
19 | val response = stackoverflowApi.questionDetails(questionId)
20 | if (response.isSuccessful && response.body() != null) {
21 | return@withContext Result.Success(response.body()!!.question)
22 | } else {
23 | return@withContext Result.Failure
24 | }
25 | } catch (t: Throwable) {
26 | if (t !is CancellationException) {
27 | return@withContext Result.Failure
28 | } else {
29 | throw t
30 | }
31 | }
32 | }
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/viewmodel/MyViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.viewmodel
2 |
3 | import androidx.lifecycle.*
4 | import com.techyourchance.dagger2course.questions.FetchQuestionDetailsUseCase
5 | import com.techyourchance.dagger2course.questions.FetchQuestionsUseCase
6 | import com.techyourchance.dagger2course.questions.Question
7 | import com.techyourchance.dagger2course.screens.common.viewmodels.SavedStateViewModel
8 | import kotlinx.coroutines.delay
9 | import kotlinx.coroutines.launch
10 | import javax.inject.Inject
11 |
12 | class MyViewModel @Inject constructor(
13 | private val fetchQuestionsUseCase: FetchQuestionsUseCase,
14 | private val fetchQuestionDetailsUseCase: FetchQuestionDetailsUseCase
15 | ): SavedStateViewModel() {
16 |
17 | private lateinit var _questions: MutableLiveData>
18 | val questions: LiveData> get() = _questions
19 |
20 | override fun init(savedStateHandle: SavedStateHandle) {
21 | _questions = savedStateHandle.getLiveData("questions")
22 |
23 | viewModelScope.launch {
24 | delay(5000)
25 | val result = fetchQuestionsUseCase.fetchLatestQuestions()
26 | if (result is FetchQuestionsUseCase.Result.Success) {
27 | _questions.value = result.questions
28 | } else {
29 | throw RuntimeException("fetch failed")
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/common/dependnecyinjection/app/AppModule.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.common.dependnecyinjection.app
2 |
3 | import android.app.Application
4 | import com.techyourchance.dagger2course.common.dependnecyinjection.Retrofit1
5 | import com.techyourchance.dagger2course.common.dependnecyinjection.Retrofit2
6 | import com.techyourchance.dagger2course.networking.StackoverflowApi
7 | import com.techyourchance.dagger2course.networking.UrlProvider
8 | import dagger.Module
9 | import dagger.Provides
10 | import retrofit2.Retrofit
11 | import retrofit2.converter.gson.GsonConverterFactory
12 |
13 | @Module
14 | class AppModule(val application: Application) {
15 |
16 | @Provides
17 | @AppScope
18 | @Retrofit1
19 | fun retrofit1(urlProvider: UrlProvider): Retrofit {
20 | return Retrofit.Builder()
21 | .baseUrl(urlProvider.getBaseUrl1())
22 | .addConverterFactory(GsonConverterFactory.create())
23 | .build()
24 | }
25 |
26 | @Provides
27 | @AppScope
28 | @Retrofit2
29 | fun retrofit2(urlProvider: UrlProvider): Retrofit {
30 | return Retrofit.Builder()
31 | .baseUrl(urlProvider.getBaseUrl2())
32 | .addConverterFactory(GsonConverterFactory.create())
33 | .build()
34 | }
35 |
36 | @AppScope
37 | @Provides
38 | fun urlProvider() = UrlProvider()
39 |
40 | @Provides
41 | fun application() = application
42 |
43 | @Provides
44 | @AppScope
45 | fun stackoverflowApi(@Retrofit1 retrofit: Retrofit) = retrofit.create(StackoverflowApi::class.java)
46 |
47 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | android {
6 | namespace "com.techyourchance.dagger2course"
7 | compileSdk 34
8 | defaultConfig {
9 | minSdkVersion 19
10 | targetSdkVersion 34
11 | versionCode 1
12 | versionName "1.0"
13 | vectorDrawables.useSupportLibrary = true
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | compileOptions {
22 | sourceCompatibility = 17
23 | targetCompatibility = 17
24 | }
25 |
26 | kotlinOptions {
27 | jvmTarget = 17
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
35 |
36 | // DI
37 | api "com.google.dagger:dagger:$dagger_version"
38 | kapt "com.google.dagger:dagger-compiler:$dagger_version"
39 |
40 | // Image loading
41 | implementation 'com.github.bumptech.glide:glide:4.11.0'
42 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
43 |
44 | implementation 'androidx.appcompat:appcompat:1.6.1'
45 | implementation 'androidx.recyclerview:recyclerview:1.2.1'
46 | implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
47 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
48 | implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2"
49 |
50 | implementation 'com.squareup.retrofit2:retrofit:2.8.2'
51 | implementation 'com.squareup.retrofit2:converter-gson:2.8.2'
52 |
53 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
54 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
55 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_my_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
21 |
22 |
23 |
24 |
39 |
40 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/viewmodel/ViewModelActivity.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.viewmodel
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.widget.Toast
7 | import androidx.lifecycle.Observer
8 | import androidx.lifecycle.ViewModelProvider
9 | import com.techyourchance.dagger2course.R
10 | import com.techyourchance.dagger2course.screens.common.ScreensNavigator
11 | import com.techyourchance.dagger2course.screens.common.activities.BaseActivity
12 | import com.techyourchance.dagger2course.screens.common.toolbar.MyToolbar
13 | import com.techyourchance.dagger2course.screens.common.viewmodels.ViewModelFactory
14 | import javax.inject.Inject
15 |
16 | class ViewModelActivity : BaseActivity() {
17 |
18 | @Inject lateinit var screensNavigator: ScreensNavigator
19 | @Inject lateinit var myViewModelFactory: ViewModelFactory
20 |
21 | private lateinit var myViewModel: MyViewModel
22 | private lateinit var myViewModel2: MyViewModel2
23 |
24 | private lateinit var toolbar: MyToolbar
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | injector.inject(this)
28 | super.onCreate(savedInstanceState)
29 |
30 | setContentView(R.layout.layout_view_model)
31 |
32 | toolbar = findViewById(R.id.toolbar)
33 | toolbar.setNavigateUpListener {
34 | screensNavigator.navigateBack()
35 | }
36 |
37 | myViewModel = ViewModelProvider(this, myViewModelFactory).get(MyViewModel::class.java)
38 | myViewModel2 = ViewModelProvider(this, myViewModelFactory).get(MyViewModel2::class.java)
39 |
40 | myViewModel.questions.observe(this, Observer {
41 | questions -> Toast.makeText(this, "fetched ${questions.size} questions", Toast.LENGTH_SHORT).show()
42 | })
43 | }
44 |
45 | companion object {
46 | fun start(context: Context) {
47 | val intent = Intent(context, ViewModelActivity::class.java)
48 | context.startActivity(intent)
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/common/toolbar/MyToolbar.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.common.toolbar
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.widget.FrameLayout
8 | import android.widget.TextView
9 | import androidx.appcompat.widget.Toolbar
10 | import com.techyourchance.dagger2course.R
11 |
12 | class MyToolbar : Toolbar {
13 |
14 | interface NavigateUpListener {
15 | fun onNavigationUpClicked()
16 | }
17 |
18 | interface ViewModelListener {
19 | fun onViewModelClicked()
20 | }
21 |
22 | private var navigateUpListener: () -> Unit = {}
23 | private var viewModelListener: () -> Unit = {}
24 |
25 | private lateinit var navigateUp: FrameLayout
26 | private lateinit var viewmodel: TextView
27 |
28 | constructor(context: Context) : super(context) {
29 | init(context)
30 | }
31 |
32 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
33 | init(context)
34 | }
35 |
36 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
37 | init(context)
38 | }
39 |
40 | private fun init(context: Context) {
41 | val view = LayoutInflater.from(context).inflate(R.layout.layout_my_toolbar, this, true)
42 | setContentInsetsRelative(0, 0)
43 | navigateUp = view.findViewById(R.id.navigate_up)
44 | navigateUp.setOnClickListener { navigateUpListener.invoke() }
45 | viewmodel = view.findViewById(R.id.viewmodel)
46 | viewmodel.setOnClickListener { viewModelListener.invoke() }
47 | }
48 |
49 | fun setNavigateUpListener(navigateUpListener: () -> Unit) {
50 | this.navigateUpListener = navigateUpListener
51 | navigateUp.visibility = View.VISIBLE
52 | }
53 |
54 | fun setViewModelListener(viewModelListener: () -> Unit) {
55 | this.viewModelListener = viewModelListener
56 | viewmodel.visibility = View.VISIBLE
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_question_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
14 |
15 |
19 |
20 |
23 |
24 |
28 |
29 |
35 |
36 |
47 |
48 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/questiondetails/QuestionDetailsViewMvc.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.questiondetails
2 |
3 | import android.os.Build
4 | import android.text.Html
5 | import android.view.LayoutInflater
6 | import android.view.ViewGroup
7 | import android.widget.ImageView
8 | import android.widget.TextView
9 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
10 | import com.techyourchance.dagger2course.R
11 | import com.techyourchance.dagger2course.questions.QuestionWithBody
12 | import com.techyourchance.dagger2course.screens.common.imageloader.ImageLoader
13 | import com.techyourchance.dagger2course.screens.common.toolbar.MyToolbar
14 | import com.techyourchance.dagger2course.screens.common.viewsmvc.BaseViewMvc
15 |
16 | class QuestionDetailsViewMvc(
17 | layoutInflater: LayoutInflater,
18 | private val imageLoader: ImageLoader,
19 | parent: ViewGroup?
20 | ): BaseViewMvc(
21 | layoutInflater,
22 | parent,
23 | R.layout.layout_question_details
24 | ) {
25 |
26 | interface Listener {
27 | fun onBackClicked()
28 | }
29 |
30 | private val toolbar: MyToolbar
31 | private val swipeRefresh: SwipeRefreshLayout
32 | private val txtQuestionBody: TextView
33 | private val imgUser: ImageView
34 | private val txtUserName: TextView
35 |
36 | init {
37 | txtQuestionBody = findViewById(R.id.txt_question_body)
38 |
39 | // init toolbar
40 | toolbar = findViewById(R.id.toolbar)
41 | toolbar.setNavigateUpListener {
42 | for (listener in listeners) {
43 | listener.onBackClicked()
44 | }
45 | }
46 |
47 | // init pull-down-to-refresh (used as a progress indicator)
48 | swipeRefresh = findViewById(R.id.swipeRefresh)
49 | swipeRefresh.isEnabled = false
50 |
51 | imgUser = findViewById(R.id.img_user)
52 | txtUserName = findViewById(R.id.txt_user_name)
53 | }
54 |
55 | fun bindQuestionWithBody(question: QuestionWithBody) {
56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
57 | txtQuestionBody.text = Html.fromHtml(question.body, Html.FROM_HTML_MODE_LEGACY)
58 | } else {
59 | @Suppress("DEPRECATION")
60 | txtQuestionBody.text = Html.fromHtml(question.body)
61 | }
62 | imageLoader.loadImage(question.owner.imageUrl, imgUser)
63 | txtUserName.text = question.owner.name
64 | }
65 |
66 | fun showProgressIndication() {
67 | swipeRefresh.isRefreshing = true
68 | }
69 |
70 | fun hideProgressIndication() {
71 | if (swipeRefresh.isRefreshing) {
72 | swipeRefresh.isRefreshing = false
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/questiondetails/QuestionDetailsActivity.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.questiondetails
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import com.techyourchance.dagger2course.questions.FetchQuestionDetailsUseCase
7 | import com.techyourchance.dagger2course.screens.common.ScreensNavigator
8 | import com.techyourchance.dagger2course.screens.common.activities.BaseActivity
9 | import com.techyourchance.dagger2course.screens.common.dialogs.DialogsNavigator
10 | import com.techyourchance.dagger2course.screens.common.viewsmvc.ViewMvcFactory
11 | import kotlinx.coroutines.*
12 | import javax.inject.Inject
13 |
14 | class QuestionDetailsActivity : BaseActivity(), QuestionDetailsViewMvc.Listener {
15 |
16 | private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
17 |
18 | @Inject lateinit var fetchQuestionDetailsUseCase: FetchQuestionDetailsUseCase
19 | @Inject lateinit var dialogsNavigator: DialogsNavigator
20 | @Inject lateinit var screensNavigator: ScreensNavigator
21 | @Inject lateinit var viewMvcFactory: ViewMvcFactory
22 |
23 | private lateinit var viewMvc: QuestionDetailsViewMvc
24 |
25 | private lateinit var questionId: String
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | injector.inject(this)
29 | super.onCreate(savedInstanceState)
30 | viewMvc = viewMvcFactory.newQuestionDetailsViewMvc(null)
31 | setContentView(viewMvc.rootView)
32 |
33 | questionId = intent.extras!!.getString(EXTRA_QUESTION_ID)!!
34 | }
35 |
36 | override fun onStart() {
37 | super.onStart()
38 | viewMvc.registerListener(this)
39 | fetchQuestionDetails()
40 | }
41 |
42 | override fun onStop() {
43 | super.onStop()
44 | coroutineScope.coroutineContext.cancelChildren()
45 | viewMvc.unregisterListener(this)
46 | }
47 |
48 | private fun fetchQuestionDetails() {
49 | coroutineScope.launch {
50 | viewMvc.showProgressIndication()
51 | try {
52 | val result = fetchQuestionDetailsUseCase.fetchQuestion(questionId)
53 | when(result) {
54 | is FetchQuestionDetailsUseCase.Result.Success -> {
55 | viewMvc.bindQuestionWithBody(result.question)
56 | }
57 | is FetchQuestionDetailsUseCase.Result.Failure -> onFetchFailed()
58 | }
59 | } finally {
60 | viewMvc.hideProgressIndication()
61 | }
62 |
63 | }
64 | }
65 |
66 | private fun onFetchFailed() {
67 | dialogsNavigator.showServerErrorDialog()
68 | }
69 |
70 | override fun onBackClicked() {
71 | screensNavigator.navigateBack()
72 | }
73 |
74 | companion object {
75 | const val EXTRA_QUESTION_ID = "EXTRA_QUESTION_ID"
76 | fun start(context: Context, questionId: String) {
77 | val intent = Intent(context, QuestionDetailsActivity::class.java)
78 | intent.putExtra(EXTRA_QUESTION_ID, questionId)
79 | context.startActivity(intent)
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/questionslist/QuestionsListFragment.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.questionslist
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.techyourchance.dagger2course.questions.FetchQuestionsUseCase
8 | import com.techyourchance.dagger2course.questions.Question
9 | import com.techyourchance.dagger2course.screens.common.ScreensNavigator
10 | import com.techyourchance.dagger2course.screens.common.dialogs.DialogsNavigator
11 | import com.techyourchance.dagger2course.screens.common.fragments.BaseFragment
12 | import com.techyourchance.dagger2course.screens.common.viewsmvc.ViewMvcFactory
13 | import kotlinx.coroutines.*
14 | import javax.inject.Inject
15 |
16 | class QuestionsListFragment : BaseFragment(), QuestionsListViewMvc.Listener {
17 |
18 | private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
19 |
20 | @Inject lateinit var fetchQuestionsUseCase: FetchQuestionsUseCase
21 | @Inject lateinit var dialogsNavigator: DialogsNavigator
22 | @Inject lateinit var screensNavigator: ScreensNavigator
23 | @Inject lateinit var viewMvcFactory: ViewMvcFactory
24 |
25 | private lateinit var viewMvc: QuestionsListViewMvc
26 |
27 | private var isDataLoaded = false
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | injector.inject(this)
31 | super.onCreate(savedInstanceState)
32 | }
33 |
34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
35 | viewMvc = viewMvcFactory.newQuestionsListViewMvc(container)
36 | return viewMvc.rootView
37 | }
38 |
39 | override fun onStart() {
40 | super.onStart()
41 | viewMvc.registerListener(this)
42 | if (!isDataLoaded) {
43 | fetchQuestions()
44 | }
45 | }
46 |
47 | override fun onStop() {
48 | super.onStop()
49 | coroutineScope.coroutineContext.cancelChildren()
50 | viewMvc.unregisterListener(this)
51 | }
52 |
53 | override fun onRefreshClicked() {
54 | fetchQuestions()
55 | }
56 |
57 | private fun fetchQuestions() {
58 | coroutineScope.launch {
59 | viewMvc.showProgressIndication()
60 | try {
61 | val result = fetchQuestionsUseCase.fetchLatestQuestions()
62 | when (result) {
63 | is FetchQuestionsUseCase.Result.Success -> {
64 | viewMvc.bindQuestions(result.questions)
65 | isDataLoaded = true
66 | }
67 | is FetchQuestionsUseCase.Result.Failure -> onFetchFailed()
68 | }
69 | } finally {
70 | viewMvc.hideProgressIndication()
71 | }
72 | }
73 | }
74 |
75 | private fun onFetchFailed() {
76 | dialogsNavigator.showServerErrorDialog()
77 | }
78 |
79 | override fun onQuestionClicked(clickedQuestion: Question) {
80 | screensNavigator.toQuestionDetails(clickedQuestion.id)
81 | }
82 |
83 | override fun onViewModelClicked() {
84 | screensNavigator.toViewModel()
85 | }
86 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/techyourchance/dagger2course/screens/questionslist/QuestionsListViewMvc.kt:
--------------------------------------------------------------------------------
1 | package com.techyourchance.dagger2course.screens.questionslist
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.TextView
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import androidx.recyclerview.widget.RecyclerView
9 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
10 | import com.techyourchance.dagger2course.R
11 | import com.techyourchance.dagger2course.questions.Question
12 | import com.techyourchance.dagger2course.screens.common.toolbar.MyToolbar
13 | import com.techyourchance.dagger2course.screens.common.viewsmvc.BaseViewMvc
14 |
15 | class QuestionsListViewMvc(
16 | private val layoutInflater: LayoutInflater,
17 | private val parent: ViewGroup?
18 | ): BaseViewMvc(
19 | layoutInflater,
20 | parent,
21 | R.layout.layout_questions_list
22 | ) {
23 |
24 | interface Listener {
25 | fun onRefreshClicked()
26 | fun onQuestionClicked(clickedQuestion: Question)
27 | fun onViewModelClicked()
28 | }
29 |
30 | private val toolbar: MyToolbar
31 | private val swipeRefresh: SwipeRefreshLayout
32 | private val recyclerView: RecyclerView
33 | private val questionsAdapter: QuestionsAdapter
34 |
35 | init {
36 |
37 | toolbar = findViewById(R.id.toolbar)
38 | toolbar.setViewModelListener {
39 | for (listener in listeners) {
40 | listener.onViewModelClicked()
41 | }
42 | }
43 | // init pull-down-to-refresh
44 | swipeRefresh = findViewById(R.id.swipeRefresh)
45 | swipeRefresh.setOnRefreshListener {
46 | for (listener in listeners) {
47 | listener.onRefreshClicked()
48 | }
49 | }
50 |
51 | // init recycler view
52 | recyclerView = findViewById(R.id.recycler)
53 | recyclerView.layoutManager = LinearLayoutManager(context)
54 | questionsAdapter = QuestionsAdapter{ clickedQuestion ->
55 | for (listener in listeners) {
56 | listener.onQuestionClicked(clickedQuestion)
57 | }
58 | }
59 | recyclerView.adapter = questionsAdapter
60 | }
61 |
62 | fun bindQuestions(questions: List) {
63 | questionsAdapter.bindData(questions)
64 | }
65 |
66 | fun showProgressIndication() {
67 | swipeRefresh.isRefreshing = true
68 | }
69 |
70 | fun hideProgressIndication() {
71 | if (swipeRefresh.isRefreshing) {
72 | swipeRefresh.isRefreshing = false
73 | }
74 | }
75 |
76 | class QuestionsAdapter(
77 | private val onQuestionClickListener: (Question) -> Unit
78 | ) : RecyclerView.Adapter() {
79 |
80 | private var questionsList: List = ArrayList(0)
81 |
82 | inner class QuestionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
83 | val title: TextView = view.findViewById(R.id.txt_title)
84 | }
85 |
86 | fun bindData(questions: List) {
87 | questionsList = ArrayList(questions)
88 | notifyDataSetChanged()
89 | }
90 |
91 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuestionViewHolder {
92 | val itemView = LayoutInflater.from(parent.context)
93 | .inflate(R.layout.layout_question_list_item, parent, false)
94 | return QuestionViewHolder(itemView)
95 | }
96 |
97 | override fun onBindViewHolder(holder: QuestionViewHolder, position: Int) {
98 | holder.title.text = questionsList[position].title
99 | holder.itemView.setOnClickListener {
100 | onQuestionClickListener.invoke(questionsList[position])
101 | }
102 | }
103 |
104 | override fun getItemCount(): Int {
105 | return questionsList.size
106 | }
107 |
108 | }
109 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------