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
2 changes: 1 addition & 1 deletion .idea/compiler.xml

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

2 changes: 1 addition & 1 deletion .idea/gradle.xml

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

2 changes: 1 addition & 1 deletion .idea/misc.xml

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

10 changes: 10 additions & 0 deletions .idea/runConfigurations.xml

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

2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
def fragment_version = "1.2.5"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.example.architecturebase

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.example.architecturebase">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.architecturebase

import com.example.architecturebase.network.model.Post

interface CallbackFromRetrofit {
fun onSuccess(value: List<Post>)
fun onFailure(value: Throwable)
}
57 changes: 57 additions & 0 deletions app/src/main/java/com/example/architecturebase/Fragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.example.architecturebase

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.architecturebase.adapter.MainAdapter
import com.example.architecturebase.databinding.FragmentBinding


class Fragment : Fragment(R.layout.fragment) {

private val mvvmModelView: MvvmContract = ViewModelMvvm()


private var _binding: FragmentBinding? = null
private val binding get() = _binding!!

private val mainAdapter = MainAdapter()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentBinding.bind(view)
binding.mainRV.apply {
layoutManager = LinearLayoutManager(activity)
adapter = mainAdapter
}

mvvmModelView.getPosts()

binding.listSRL.isRefreshing = true

mvvmModelView.listPosts.observe(viewLifecycleOwner) {
mainAdapter.items = it
binding.listSRL.isRefreshing = false
}

mvvmModelView.errorMessage.observe(viewLifecycleOwner) { t -> showFailureLoadDataDialog(t) }

binding.listSRL.setOnRefreshListener {
mainAdapter.items = emptyList()
mvvmModelView.getPosts()
}
}

private fun showFailureLoadDataDialog(t: Throwable) {
t.printStackTrace()
binding.listSRL.isRefreshing = false
Toast.makeText(activity, t.message, Toast.LENGTH_SHORT).show()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/com/example/architecturebase/IRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.architecturebase

import com.example.architecturebase.network.IPostApi

interface IRepository {

fun getData(): IPostApi
}
107 changes: 7 additions & 100 deletions app/src/main/java/com/example/architecturebase/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,113 +1,20 @@
package com.example.architecturebase

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.architecturebase.adapter.MainAdapter
import com.example.architecturebase.databinding.ActivityMainBinding
import com.example.architecturebase.network.IPostApi
import com.example.architecturebase.network.model.Post
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

companion object {
private const val REQUEST_TIMEOUT_SECONDS = 5L
}

private val binding by lazy {
val bind = ActivityMainBinding.inflate(layoutInflater)
setContentView(bind.root)
bind
}

private val mainAdapter = MainAdapter()

private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.callTimeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.build()

private val retrofit = Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()

private val postApi = retrofit.create(IPostApi::class.java)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding.mainRV.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = mainAdapter
}
binding.listSRL.isRefreshing = true
postApi.getPosts().enqueue(object : Callback<List<Post>> {
override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
if (response.isSuccessful) {
response.body()?.let { posts ->
// logic starts
val processedPosts = posts.filter {
!it.title.startsWith("H")
}.map {
it.copy(title = it.title + "appendix")
}.sortedBy {
it.title
}.subList(0, posts.size - 3)
// logic ends
mainAdapter.items = processedPosts
binding.listSRL.isRefreshing = false
}
}
}

override fun onFailure(call: Call<List<Post>>, t: Throwable) {
Toast.makeText(this@MainActivity, t.message, Toast.LENGTH_SHORT).show()
t.printStackTrace()
binding.listSRL.isRefreshing = false
setContentView(R.layout.activity_main)
val fragment = Fragment()
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction().apply {
setReorderingAllowed(true)
add(R.id.fragmentContainerView, fragment)
commit()
}
})

binding.listSRL.setOnRefreshListener {
mainAdapter.items = emptyList()

postApi.getPosts().enqueue(object : Callback<List<Post>> {
override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
if (response.isSuccessful) {
response.body()?.let { posts ->
// logic starts
val processedPosts = posts.filter {
!it.title.startsWith("H")
}.map {
it.copy(title = it.title + "appendix")
}.sortedBy {
it.title
}.subList(0, posts.size - 3)
// logic ends
mainAdapter.items = processedPosts
binding.listSRL.isRefreshing = false
}
}
}

override fun onFailure(call: Call<List<Post>>, t: Throwable) {
Toast.makeText(this@MainActivity, t.message, Toast.LENGTH_SHORT).show()
t.printStackTrace()
binding.listSRL.isRefreshing = false
}
})
}
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/example/architecturebase/MvvmContract.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.architecturebase

import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LiveData
import com.example.architecturebase.network.model.Post

interface MvvmContract {

val listPosts: LiveData<List<Post>>
val errorMessage: LiveData<Throwable>
fun getPosts()
}
34 changes: 34 additions & 0 deletions app/src/main/java/com/example/architecturebase/Repository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.architecturebase

import com.example.architecturebase.network.IPostApi
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

class Repository : IRepository {

private val okHttpClient = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.callTimeout(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.build()

private val retrofit = Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()

private val postApi = retrofit.create(IPostApi::class.java)

override fun getData(): IPostApi {
return postApi
}

companion object {
private const val REQUEST_TIMEOUT_SECONDS = 5L
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.architecturebase.UseCases

import com.example.architecturebase.CallbackFromRetrofit
import com.example.architecturebase.IRepository
import com.example.architecturebase.Repository
import com.example.architecturebase.network.model.Post
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

object UseCaseGetPosts {

private val repository: IRepository = Repository()

fun loadPosts(customCallback: CallbackFromRetrofit) {
repository.getData().getPosts().enqueue(object : Callback<List<Post>> {
override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
if (response.isSuccessful) {
response.body()?.let { posts ->
customCallback.onSuccess(sortedPosts(posts))
}
}
}
override fun onFailure(call: Call<List<Post>>, t: Throwable) {
customCallback.onFailure(t)
}
})
}

private fun sortedPosts(posts: List<Post>): List<Post> {
return posts.filter {
!it.title.startsWith("H")
}.map {
it.copy(title = it.title + "appendix")
}.sortedBy {
it.title
}.subList(0, posts.size - 3)
}
}
25 changes: 25 additions & 0 deletions app/src/main/java/com/example/architecturebase/ViewModelMvvm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.architecturebase

import androidx.lifecycle.*
import com.example.architecturebase.UseCases.UseCaseGetPosts
import com.example.architecturebase.network.model.Post

class ViewModelMvvm : MvvmContract, ViewModel() {


override val listPosts: MutableLiveData<List<Post>> = MutableLiveData()

override val errorMessage: MutableLiveData<Throwable> = MutableLiveData()

override fun getPosts() {
UseCaseGetPosts.loadPosts(object : CallbackFromRetrofit {
override fun onSuccess(value: List<Post>) {
listPosts.value = value
}

override fun onFailure(value: Throwable) {
errorMessage.value = value
}
})
}
}
Loading