diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 4aeca14..e8f2d42 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -28,8 +28,8 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | ${{ runner.os }}-gradle- - name: Build debug APK diff --git a/app/build.gradle b/app/build.gradle index c830294..8f8030e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ plugins { id 'kotlin-kapt' id 'kotlin-parcelize' id 'androidx.navigation.safeargs.kotlin' + id 'com.google.dagger.hilt.android' } android { @@ -53,12 +54,12 @@ dependencies { def lifecycle_version = "2.5.1" def recyclerview_version = "1.2.1" def navigation_version = "2.5.3" + def hilt_version = "2.44" def swipe_to_refresh_version = "1.1.0" def test_junit_version = "4.13.2" def androidTest_junit_version = "1.1.5" def androidTest_espresso_version = "3.5.1" - def dagger_version = "2.43" implementation "androidx.core:core-ktx:$core_version" @@ -103,9 +104,9 @@ dependencies { // swipe to refresh implementation "androidx.swiperefreshlayout:swiperefreshlayout:$swipe_to_refresh_version" - // dagger2 - implementation "com.google.dagger:dagger:$dagger_version" - kapt "com.google.dagger:dagger-compiler:$dagger_version" + // hilt + implementation "com.google.dagger:hilt-android:$hilt_version" + kapt "com.google.dagger:hilt-android-compiler:$hilt_version" testImplementation "junit:junit:$test_junit_version" androidTestImplementation "androidx.test.ext:junit:$androidTest_junit_version" diff --git a/app/src/main/java/com/example/android/dogsapp/BindingAdapters.kt b/app/src/main/java/com/example/android/dogsapp/BindingAdapters.kt index ed038ff..39f1566 100644 --- a/app/src/main/java/com/example/android/dogsapp/BindingAdapters.kt +++ b/app/src/main/java/com/example/android/dogsapp/BindingAdapters.kt @@ -1,5 +1,6 @@ package com.example.android.dogsapp +import android.annotation.SuppressLint import android.view.View import android.widget.ImageView import android.widget.TextView @@ -49,6 +50,7 @@ fun bindStatus(statusImageView: ImageView, status: DogsApiStatus?) { } } +@SuppressLint("SetTextI18n") @BindingAdapter("breedName") fun bindBreedName(textView: TextView, imageUrl: String?) { imageUrl?.let { diff --git a/app/src/main/java/com/example/android/dogsapp/DogsApplication.kt b/app/src/main/java/com/example/android/dogsapp/DogsApplication.kt index a1a2191..ad78e1b 100644 --- a/app/src/main/java/com/example/android/dogsapp/DogsApplication.kt +++ b/app/src/main/java/com/example/android/dogsapp/DogsApplication.kt @@ -1,21 +1,12 @@ package com.example.android.dogsapp import android.app.Application -import com.example.android.dogsapp.common.di.application.ApplicationComponent -import com.example.android.dogsapp.common.di.application.ApplicationModule -import com.example.android.dogsapp.common.di.application.DaggerApplicationComponent - +import dagger.hilt.android.HiltAndroidApp +@HiltAndroidApp class DogsApplication: Application() { - val appComponent: ApplicationComponent by lazy { - DaggerApplicationComponent.builder() - .applicationModule(ApplicationModule(application = this)) - .build() - } - override fun onCreate() { super.onCreate() - appComponent.inject(this) } } diff --git a/app/src/main/java/com/example/android/dogsapp/common/di/application/ApplicationModule.kt b/app/src/main/java/com/example/android/dogsapp/common/di/application/ApplicationModule.kt index 3bb8a2a..e9a1215 100644 --- a/app/src/main/java/com/example/android/dogsapp/common/di/application/ApplicationModule.kt +++ b/app/src/main/java/com/example/android/dogsapp/common/di/application/ApplicationModule.kt @@ -1,21 +1,37 @@ package com.example.android.dogsapp.common.di.application -import android.app.Application +import android.content.Context +import com.example.android.dogsapp.DogsApplication import com.example.android.dogsapp.data.network.DogsApi import com.example.android.dogsapp.data.repository.DogsRepository import com.example.android.dogsapp.data.repository.DogsRepositoryImpl +import com.example.android.dogsapp.ui.utils.RefreshManager +import com.example.android.dogsapp.ui.utils.SwipeToRefreshManagerImpl import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory +import javax.inject.Singleton + + +private const val BASE_URL = "https://dog.ceo/api/" @Module -class ApplicationModule(private val application: Application) { +@InstallIn(SingletonComponent::class) +object ApplicationModule { + + @Singleton + @Provides + fun provideApplication(@ApplicationContext app: Context): DogsApplication = + app as DogsApplication + @Singleton @Provides - @ApplicationScope fun retrofit(): Retrofit { val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) @@ -27,19 +43,19 @@ class ApplicationModule(private val application: Application) { .build() } + @Singleton @Provides - @ApplicationScope - fun provideDogsRepository(dogsApi: DogsApi): DogsRepository = DogsRepositoryImpl(dogsApi) + fun provideDogsApi(retrofit: Retrofit): DogsApi { + return retrofit.create(DogsApi::class.java) + } @Provides - @ApplicationScope - fun application() = application + fun provideDogsRepository(dogsApi: DogsApi): DogsRepository { + return DogsRepositoryImpl(dogsApi) + } @Provides - @ApplicationScope - fun dogsApi(retrofit: Retrofit): DogsApi = retrofit.create(DogsApi::class.java) - - companion object{ - private const val BASE_URL = "https://dog.ceo/api/" + fun provideRefreshManager(swipeToRefreshManagerImpl: SwipeToRefreshManagerImpl): RefreshManager { + return swipeToRefreshManagerImpl } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepository.kt b/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepository.kt index 85a5c5d..460ec2c 100644 --- a/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepository.kt +++ b/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepository.kt @@ -4,4 +4,4 @@ import com.example.android.dogsapp.data.domain.DogsResponse interface DogsRepository { suspend fun getDogsPhotos(): DogsResponse -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepositoryImpl.kt b/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepositoryImpl.kt index 1776fc5..6351daf 100644 --- a/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepositoryImpl.kt +++ b/app/src/main/java/com/example/android/dogsapp/data/repository/DogsRepositoryImpl.kt @@ -1,11 +1,9 @@ package com.example.android.dogsapp.data.repository -import com.example.android.dogsapp.common.di.application.ApplicationScope import com.example.android.dogsapp.data.domain.DogsResponse import com.example.android.dogsapp.data.network.DogsApi import javax.inject.Inject -@ApplicationScope class DogsRepositoryImpl @Inject constructor(private val dogsApi: DogsApi): DogsRepository { override suspend fun getDogsPhotos(): DogsResponse = dogsApi.getRandomDogs() diff --git a/app/src/main/java/com/example/android/dogsapp/ui/MainActivity.kt b/app/src/main/java/com/example/android/dogsapp/ui/MainActivity.kt index 96af5f2..c669148 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/MainActivity.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/MainActivity.kt @@ -6,38 +6,25 @@ import androidx.databinding.DataBindingUtil import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI -import com.example.android.dogsapp.DogsApplication import com.example.android.dogsapp.R -import com.example.android.dogsapp.common.di.activity.ActivityComponent -import com.example.android.dogsapp.common.di.activity.DaggerActivityComponent import com.example.android.dogsapp.databinding.ActivityMainBinding +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class MainActivity : AppCompatActivity() { - private lateinit var activityComponent: ActivityComponent - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activityComponent = DaggerActivityComponent.builder() - .applicationComponent((application as DogsApplication).appComponent) - .build() - - activityComponent.inject(this) - DataBindingUtil.setContentView(this, R.layout.activity_main) val navHostFragment = supportFragmentManager - .findFragmentById(R.id.nav_host_fragment) as NavHostFragment + .findFragmentById(R.id.nav_host_fragment_container) as NavHostFragment val navController = navHostFragment.navController NavigationUI.setupActionBarWithNavController(this, navController) } - fun getActivityComponent(): ActivityComponent { - return activityComponent - } - override fun onSupportNavigateUp(): Boolean { - val navController = this.findNavController(R.id.nav_host_fragment) + val navController = this.findNavController(R.id.nav_host_fragment_container) return NavigationUI.navigateUp(navController, null) } } diff --git a/app/src/main/java/com/example/android/dogsapp/ui/details/DetailsFragment.kt b/app/src/main/java/com/example/android/dogsapp/ui/details/DetailsFragment.kt index 531f91a..b1c6798 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/details/DetailsFragment.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/details/DetailsFragment.kt @@ -7,7 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import com.example.android.dogsapp.databinding.FragmentDetailsBinding -class DetailsFragment : Fragment(){ +class DetailsFragment : Fragment() { private lateinit var binding: FragmentDetailsBinding @@ -15,8 +15,6 @@ class DetailsFragment : Fragment(){ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - // Inflate the layout for this fragment -// return inflater.inflate(R.layout.fragment_details, container, false) binding = FragmentDetailsBinding.inflate(inflater, container, false) binding.lifecycleOwner = viewLifecycleOwner diff --git a/app/src/main/java/com/example/android/dogsapp/ui/main/DogsAdapter.kt b/app/src/main/java/com/example/android/dogsapp/ui/main/DogsAdapter.kt index 745fb26..d5b2a5a 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/main/DogsAdapter.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/main/DogsAdapter.kt @@ -12,10 +12,7 @@ class DogsAdapter(private val listener: DogClickListener) : ListAdapter Unit) { fun onClick(dog: Dog) = listener(dog) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/android/dogsapp/ui/main/MainFragment.kt b/app/src/main/java/com/example/android/dogsapp/ui/main/MainFragment.kt index 10dd09d..ce44d8f 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/main/MainFragment.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/main/MainFragment.kt @@ -1,6 +1,5 @@ package com.example.android.dogsapp.ui.main -import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -8,29 +7,14 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import com.example.android.dogsapp.DogsApplication -import com.example.android.dogsapp.data.repository.DogsRepository import com.example.android.dogsapp.databinding.FragmentMainBinding -import com.example.android.dogsapp.ui.MainActivity -import com.example.android.dogsapp.ui.utils.RefreshManager -import javax.inject.Inject +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class MainFragment : Fragment() { private lateinit var binding: FragmentMainBinding - - @Inject - lateinit var dogsRepository: DogsRepository - - @Inject - lateinit var refreshManager: RefreshManager - - private val viewModel: MainViewModel by viewModels { MainViewModelFactory(dogsRepository, refreshManager) } - - override fun onAttach(context: Context) { - super.onAttach(context) - (requireActivity() as MainActivity).getActivityComponent().inject(this) - } + private val viewModel: MainViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/com/example/android/dogsapp/ui/main/MainViewModel.kt b/app/src/main/java/com/example/android/dogsapp/ui/main/MainViewModel.kt index 59de574..8a8a278 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/main/MainViewModel.kt @@ -7,11 +7,14 @@ import androidx.lifecycle.viewModelScope import com.example.android.dogsapp.data.domain.Dog import com.example.android.dogsapp.data.repository.DogsRepository import com.example.android.dogsapp.ui.utils.RefreshManager +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch +import javax.inject.Inject enum class DogsApiStatus { LOADING, ERROR, DONE } -class MainViewModel( +@HiltViewModel +class MainViewModel @Inject constructor( private val dogsRepository: DogsRepository, private val refreshManager: RefreshManager ) : ViewModel() { @@ -21,7 +24,6 @@ class MainViewModel( get() = _status private val _dogs = MutableLiveData>() - val dogs: LiveData> get() = _dogs diff --git a/app/src/main/java/com/example/android/dogsapp/ui/utils/SwipeToRefreshManagerImpl.kt b/app/src/main/java/com/example/android/dogsapp/ui/utils/SwipeToRefreshManagerImpl.kt index 3c345c7..592511f 100644 --- a/app/src/main/java/com/example/android/dogsapp/ui/utils/SwipeToRefreshManagerImpl.kt +++ b/app/src/main/java/com/example/android/dogsapp/ui/utils/SwipeToRefreshManagerImpl.kt @@ -23,4 +23,4 @@ class SwipeToRefreshManagerImpl @Inject constructor() : RefreshManager { refreshListener?.invoke() } } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ade2cf1..f37c379 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -12,12 +12,12 @@ android:layout_height="match_parent"> + app:navGraph="@navigation/navgraph" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 941b68f..3f439cd 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -43,6 +43,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" + android:gravity="center" android:textAppearance="?attr/textAppearanceHeadline5" app:breedName="@{dog.imageUrl}" tools:text="Breed Name" /> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/navgraph.xml similarity index 96% rename from app/src/main/res/navigation/nav_graph.xml rename to app/src/main/res/navigation/navgraph.xml index 91cf773..bfef9d1 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/navgraph.xml @@ -2,7 +2,7 @@