From 124a5c8c0bf83a62e4b4fff44eed20165d8e662e Mon Sep 17 00:00:00 2001 From: ThanaCS Date: Sat, 21 Nov 2020 16:19:54 +0300 Subject: [PATCH 1/4] Adding Dummy Data for test --- .idea/vcs.xml | 6 +++ .../countriesmvvm/view/CountryListAdapter.kt | 20 +++++--- .../thanaa/countriesmvvm/view/MainActivity.kt | 49 ++++++++++++++++++- .../countriesmvvm/viewmodel/ListViewModel.kt | 4 +- 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt b/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt index 90f4f58..09d7227 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt @@ -1,26 +1,34 @@ package com.thanaa.countriesmvvm.view +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.thanaa.countriesmvvm.R import com.thanaa.countriesmvvm.model.Country +import kotlinx.android.synthetic.main.activity_main.view.* +import kotlinx.android.synthetic.main.item_country.view.* class CountryListAdapter(var countries:ArrayList):RecyclerView.Adapter() { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CountryViewHolder { - TODO("Not yet implemented") + fun updateCounties (newCountry:List){ + countries.clear() + countries.addAll(newCountry) + notifyDataSetChanged() } - + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + CountryViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_country,parent,false)) override fun getItemCount()= countries.size override fun onBindViewHolder(holder: CountryViewHolder, position: Int) { holder.bind(countries[position]) } - class CountryViewHolder(view: View):RecyclerView.ViewHolder(view){ - fun bind (country:Country){ + class CountryViewHolder(view: View):RecyclerView.ViewHolder(view){ + private val countryName = view.name + fun bind (country:Country){ + countryName.text = country.countryName } } diff --git a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt index d68185d..bfe24e4 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt @@ -2,11 +2,58 @@ package com.thanaa.countriesmvvm.view import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import android.view.View +import android.widget.ListAdapter +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.thanaa.countriesmvvm.R +import com.thanaa.countriesmvvm.viewmodel.ListViewModel +import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { + lateinit var viewModel:ListViewModel + var countriesAdapter = CountryListAdapter(arrayListOf()) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java) + viewModel.refresh() + + countriesList.apply { + layoutManager = GridLayoutManager(context,2) + adapter = countriesAdapter + } + + observeViewModel() + } + + private fun observeViewModel() { + + viewModel.countries.observe(this,Observer { countries -> + countries?.let { + countriesAdapter.updateCounties(it) + } + }) + + viewModel.countryLoadError.observe(this, Observer { isError -> isError?.let { + list_error.visibility = if(it) View.VISIBLE else View.GONE + } }) + + viewModel.loading.observe(this, Observer { isLoading -> isLoading?.let{ + progressBar.visibility = if(it) View.VISIBLE else View.GONE + + if(it){ + list_error.visibility =View.GONE + countriesList.visibility = View.GONE + } + } }) + + + } } -} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt b/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt index 7067174..78420d8 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt @@ -11,12 +11,12 @@ class ListViewModel:ViewModel() { val countryLoadError = MutableLiveData() val loading = MutableLiveData() - fun fetch(){ + fun refresh(){ fetchCountries() } private fun fetchCountries(){ - var mockData:List = listOf(Country("Saudi"),Country("US")) + var mockData:List = listOf(Country("Saudi"),Country("US"),Country("Germany")) countryLoadError.value = false loading.value =false countries.value = mockData From ab810fa162638d4c14b376b78db691f511d94375 Mon Sep 17 00:00:00 2001 From: ThanaCS Date: Sun, 22 Nov 2020 10:16:01 +0300 Subject: [PATCH 2/4] Add Retrofit --- app/src/main/AndroidManifest.xml | 1 + .../countriesmvvm/model/CountriesAPI.kt | 11 +++++ .../countriesmvvm/model/CountriesService.kt | 24 +++++++++++ .../com/thanaa/countriesmvvm/model/Data.kt | 6 ++- .../thanaa/countriesmvvm/view/MainActivity.kt | 4 +- .../countriesmvvm/viewmodel/ListViewModel.kt | 41 ++++++++++++++++--- 6 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/thanaa/countriesmvvm/model/CountriesAPI.kt create mode 100644 app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d5df8cf..95498ce 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + >//single is an observable that emits one variable then closes it + +} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt b/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt new file mode 100644 index 0000000..a4181ec --- /dev/null +++ b/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt @@ -0,0 +1,24 @@ +package com.thanaa.countriesmvvm.model + +import com.google.gson.Gson +import io.reactivex.Single +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory + +class CountriesService { + private val BASE_URL = "https://raw.githubusercontent.com" + private val api:CountriesAPI + init { + api= Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build() + .create(CountriesAPI::class.java) + } + + fun getCountries(): Single> { + return api.getCountries() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/model/Data.kt b/app/src/main/java/com/thanaa/countriesmvvm/model/Data.kt index f7d6bef..aa2a0a4 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/model/Data.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/model/Data.kt @@ -1,6 +1,10 @@ package com.thanaa.countriesmvvm.model +import com.google.gson.annotations.SerializedName -data class Country(var countryName: String?){ + +data class Country(@SerializedName("name")val countryName: String?, + @SerializedName("capital") val capital:String?, + @SerializedName("flagPNG")val flag:String?){ } \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt index bfe24e4..b384e90 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt @@ -26,7 +26,7 @@ class MainActivity : AppCompatActivity() { viewModel.refresh() countriesList.apply { - layoutManager = GridLayoutManager(context,2) + layoutManager = LinearLayoutManager(context) adapter = countriesAdapter } @@ -37,7 +37,9 @@ class MainActivity : AppCompatActivity() { viewModel.countries.observe(this,Observer { countries -> countries?.let { + countriesList.visibility = View.VISIBLE countriesAdapter.updateCounties(it) + } }) diff --git a/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt b/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt index 78420d8..94dbe37 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/viewmodel/ListViewModel.kt @@ -2,11 +2,20 @@ package com.thanaa.countriesmvvm.viewmodel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.thanaa.countriesmvvm.model.CountriesService import com.thanaa.countriesmvvm.model.Country +import io.reactivex.Scheduler +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import io.reactivex.observers.DisposableSingleObserver +import io.reactivex.schedulers.Schedulers class ListViewModel:ViewModel() { - + private val countriesService = CountriesService() + //disposable for cleaning the connection + private val disposable = CompositeDisposable() val countries = MutableLiveData>() val countryLoadError = MutableLiveData() val loading = MutableLiveData() @@ -16,11 +25,31 @@ class ListViewModel:ViewModel() { } private fun fetchCountries(){ - var mockData:List = listOf(Country("Saudi"),Country("US"),Country("Germany")) - countryLoadError.value = false - loading.value =false - countries.value = mockData - } + loading.value = true + disposable.add( + //called on a background thread + countriesService.getCountries() + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeWith(object: DisposableSingleObserver>(){ + override fun onSuccess(value: List) { + countries.value = value + countryLoadError.value = false + loading.value = false + } + + override fun onError(e: Throwable) { + countryLoadError.value = true + loading.value = false + } + + }) + + ) + } + override fun onCleared(){ + disposable.clear() + } } \ No newline at end of file From 0aec12e6640374adeaba3b68d3abbc2edea6a477 Mon Sep 17 00:00:00 2001 From: ThanaCS Date: Sun, 22 Nov 2020 11:46:26 +0300 Subject: [PATCH 3/4] finishing Retrofit --- .../com/thanaa/countriesmvvm/util/Util.kt | 16 ++++++++++++++ .../countriesmvvm/view/CountryListAdapter.kt | 22 +++++++++++++++++-- .../thanaa/countriesmvvm/view/MainActivity.kt | 4 ++++ app/src/main/res/layout/item_country.xml | 22 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/thanaa/countriesmvvm/util/Util.kt diff --git a/app/src/main/java/com/thanaa/countriesmvvm/util/Util.kt b/app/src/main/java/com/thanaa/countriesmvvm/util/Util.kt new file mode 100644 index 0000000..bc89b59 --- /dev/null +++ b/app/src/main/java/com/thanaa/countriesmvvm/util/Util.kt @@ -0,0 +1,16 @@ +package com.thanaa.countriesmvvm.util + +import android.content.Context +import android.widget.ImageView +import androidx.swiperefreshlayout.widget.CircularProgressDrawable + +fun getProgressDrawable(context: Context):CircularProgressDrawable{ + //progress than will be displayed in an image + return CircularProgressDrawable(context).apply { + strokeWidth = 10f + centerRadius = 50f + start() + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt b/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt index 09d7227..14a13b8 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/view/CountryListAdapter.kt @@ -3,12 +3,18 @@ package com.thanaa.countriesmvvm.view import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView +import androidx.swiperefreshlayout.widget.CircularProgressDrawable +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions import com.thanaa.countriesmvvm.R import com.thanaa.countriesmvvm.model.Country +import com.thanaa.countriesmvvm.util.getProgressDrawable import kotlinx.android.synthetic.main.activity_main.view.* import kotlinx.android.synthetic.main.item_country.view.* + class CountryListAdapter(var countries:ArrayList):RecyclerView.Adapter() { fun updateCounties (newCountry:List){ @@ -26,11 +32,23 @@ class CountryListAdapter(var countries:ArrayList):RecyclerView.Adapter< class CountryViewHolder(view: View):RecyclerView.ViewHolder(view){ + private val imageView = view.imageView + private val capital = view.capital private val countryName = view.name + private val progressDrawable = getProgressDrawable(view.context) fun bind (country:Country){ countryName.text = country.countryName - } + capital.text = country.capital + imageView.loadImage(country.flag, progressDrawable) } } -} \ No newline at end of file +} + +fun ImageView.loadImage(uri: String?, progressDrawable: CircularProgressDrawable) { +val options = RequestOptions().placeholder(progressDrawable).error(R.mipmap.ic_launcher) + Glide.with(this.context) + .setDefaultRequestOptions(options) + .load(uri) + .into(this) +} diff --git a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt index b384e90..aad7f90 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt @@ -29,6 +29,10 @@ class MainActivity : AppCompatActivity() { layoutManager = LinearLayoutManager(context) adapter = countriesAdapter } + swipeRefreshLayout.setOnRefreshListener { + swipeRefreshLayout.isRefreshing = false + viewModel.refresh() + } observeViewModel() } diff --git a/app/src/main/res/layout/item_country.xml b/app/src/main/res/layout/item_country.xml index 093ab24..065057b 100644 --- a/app/src/main/res/layout/item_country.xml +++ b/app/src/main/res/layout/item_country.xml @@ -3,8 +3,30 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="100dp"> + + + + + + + + \ No newline at end of file From afb6bf0821bf42e8f78623e19aedc4a02494d594 Mon Sep 17 00:00:00 2001 From: ThanaCS Date: Sun, 22 Nov 2020 13:06:01 +0300 Subject: [PATCH 4/4] finishing Dagger 2 --- .../thanaa/countriesmvvm/di/ApiComponent.kt | 9 ++++++++ .../com/thanaa/countriesmvvm/di/ApiModule.kt | 22 +++++++++++++++++++ .../countriesmvvm/model/CountriesService.kt | 14 +++++------- 3 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/thanaa/countriesmvvm/di/ApiComponent.kt create mode 100644 app/src/main/java/com/thanaa/countriesmvvm/di/ApiModule.kt diff --git a/app/src/main/java/com/thanaa/countriesmvvm/di/ApiComponent.kt b/app/src/main/java/com/thanaa/countriesmvvm/di/ApiComponent.kt new file mode 100644 index 0000000..5c6ddab --- /dev/null +++ b/app/src/main/java/com/thanaa/countriesmvvm/di/ApiComponent.kt @@ -0,0 +1,9 @@ +package com.thanaa.countriesmvvm.di + +import com.thanaa.countriesmvvm.model.CountriesService +import dagger.Component + +@Component(modules = [ApiModule::class]) +interface ApiComponent { + fun inject(service: CountriesService) +} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/di/ApiModule.kt b/app/src/main/java/com/thanaa/countriesmvvm/di/ApiModule.kt new file mode 100644 index 0000000..8f57200 --- /dev/null +++ b/app/src/main/java/com/thanaa/countriesmvvm/di/ApiModule.kt @@ -0,0 +1,22 @@ +package com.thanaa.countriesmvvm.di + +import com.thanaa.countriesmvvm.model.CountriesAPI +import dagger.Module +import dagger.Provides +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory + +@Module +class ApiModule { +private val BASE_URL = "https://raw.githubusercontent.com" + @Provides + fun provideCountiesApi():CountriesAPI{ + return Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build() + .create(CountriesAPI::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt b/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt index a4181ec..0de542e 100644 --- a/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt +++ b/app/src/main/java/com/thanaa/countriesmvvm/model/CountriesService.kt @@ -1,21 +1,19 @@ package com.thanaa.countriesmvvm.model import com.google.gson.Gson +import com.thanaa.countriesmvvm.di.DaggerApiComponent import io.reactivex.Single import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory +import javax.inject.Inject class CountriesService { - private val BASE_URL = "https://raw.githubusercontent.com" - private val api:CountriesAPI + + @Inject + lateinit var api:CountriesAPI init { - api= Retrofit.Builder() - .baseUrl(BASE_URL) - .addConverterFactory(GsonConverterFactory.create()) - .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) - .build() - .create(CountriesAPI::class.java) + DaggerApiComponent.create().inject(this) } fun getCountries(): Single> {