Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thanaa.countriesmvvm">
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/thanaa/countriesmvvm/di/ApiComponent.kt
Original file line number Diff line number Diff line change
@@ -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)
}
22 changes: 22 additions & 0 deletions app/src/main/java/com/thanaa/countriesmvvm/di/ApiModule.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/thanaa/countriesmvvm/model/CountriesAPI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.thanaa.countriesmvvm.model

import io.reactivex.Single
import retrofit2.http.GET

interface CountriesAPI {

@GET("DevTides/countries/master/countriesV2.json")
fun getCountries():Single<List<Country>>//single is an observable that emits one variable then closes it

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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 {

@Inject
lateinit var api:CountriesAPI
init {
DaggerApiComponent.create().inject(this)
}

fun getCountries(): Single<List<Country>> {
return api.getCountries()
}
}
6 changes: 5 additions & 1 deletion app/src/main/java/com/thanaa/countriesmvvm/model/Data.kt
Original file line number Diff line number Diff line change
@@ -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?){

}
16 changes: 16 additions & 0 deletions app/src/main/java/com/thanaa/countriesmvvm/util/Util.kt
Original file line number Diff line number Diff line change
@@ -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()
}


}
Original file line number Diff line number Diff line change
@@ -1,28 +1,54 @@
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<Country>):RecyclerView.Adapter<CountryListAdapter.CountryViewHolder>() {

class CountryListAdapter(var countries:ArrayList<Country>):RecyclerView.Adapter<CountryListAdapter.CountryViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CountryViewHolder {
TODO("Not yet implemented")
fun updateCounties (newCountry:List<Country>){
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 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) }

}

}
}

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)
}
55 changes: 54 additions & 1 deletion app/src/main/java/com/thanaa/countriesmvvm/view/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,64 @@ 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 = LinearLayoutManager(context)
adapter = countriesAdapter
}
swipeRefreshLayout.setOnRefreshListener {
swipeRefreshLayout.isRefreshing = false
viewModel.refresh()
}

observeViewModel()
}

private fun observeViewModel() {

viewModel.countries.observe(this,Observer { countries ->
countries?.let {
countriesList.visibility = View.VISIBLE
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
}
} })


}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,54 @@ 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<List<Country>>()
val countryLoadError = MutableLiveData<Boolean>()
val loading = MutableLiveData<Boolean>()

fun fetch(){
fun refresh(){
fetchCountries()
}

private fun fetchCountries(){
var mockData:List<Country> = listOf(Country("Saudi"),Country("US"))
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<List<Country>>(){
override fun onSuccess(value: List<Country>) {
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()
}

}
22 changes: 22 additions & 0 deletions app/src/main/res/layout/item_country.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,30 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>

<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:text="Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/capital"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Capital"/>

</LinearLayout>

</LinearLayout>