Created
January 9, 2018 17:16
-
-
Save nglauber/616563985a6a05830eb4ae4d086aa4bc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
apply plugin: 'com.android.application' | |
apply plugin: 'kotlin-android' | |
apply plugin: 'kotlin-android-extensions' | |
android { | |
compileSdkVersion 27 | |
defaultConfig { | |
applicationId "br.com.nglauber.marvel" | |
minSdkVersion 19 | |
targetSdkVersion 27 | |
versionCode 1 | |
versionName "1.0" | |
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | |
} | |
buildTypes { | |
release { | |
minifyEnabled false | |
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |
} | |
} | |
} | |
dependencies { | |
implementation fileTree(dir: 'libs', include: ['*.jar']) | |
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" | |
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.19.3' | |
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3' | |
implementation 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.8.2' | |
implementation "com.android.support:appcompat-v7:$appcompat_version" | |
implementation "com.android.support:recyclerview-v7:$appcompat_version" | |
implementation "com.android.support.constraint:constraint-layout:$constraintlayout_version" | |
implementation "android.arch.lifecycle:extensions:1.0.0" | |
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" | |
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" | |
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version" | |
implementation "com.github.bumptech.glide:glide:$glide_version" | |
kapt "com.github.bumptech.glide:compiler:$glide_version" | |
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { | |
exclude group: 'com.android.support', module: 'support-annotations' | |
}) | |
testImplementation 'junit:junit:4.12' | |
} | |
repositories { | |
mavenCentral() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
buildscript { | |
ext.kotlin_version = '1.2.10' | |
ext.appcompat_version = '27.0.1' | |
ext.constraintlayout_version = '1.0.2' | |
ext.rxjava2_version = '2.1.3' | |
ext.rxandroid_version = '2.0.1' | |
ext.retrofit_version = '2.3.0' | |
ext.okhttp_version = '3.6.0' | |
ext.glide_version = '4.3.1' | |
repositories { | |
jcenter() | |
google() | |
} | |
dependencies { | |
classpath 'com.android.tools.build:gradle:3.0.1' | |
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |
} | |
} | |
allprojects { | |
repositories { | |
jcenter() | |
google() | |
} | |
} | |
task clean(type: Delete) { | |
delete rootProject.buildDir | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.arch.lifecycle.Observer | |
import android.arch.lifecycle.ViewModelProviders | |
import android.os.Bundle | |
import android.os.Parcelable | |
import android.support.v7.app.AppCompatActivity | |
import android.support.v7.widget.LinearLayoutManager | |
import android.support.v7.widget.RecyclerView | |
import android.util.Log | |
import br.com.nglauber.marvel.R | |
import kotlinx.android.synthetic.main.activity_characters.* | |
class CharactersActivity : AppCompatActivity() { | |
private val viewModel: CharactersViewModel by lazy { | |
ViewModelProviders.of(this).get(CharactersViewModel::class.java) | |
} | |
private val adapter: CharactersAdapter by lazy { | |
CharactersAdapter() | |
} | |
private var recyclerState: Parcelable? = null | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_characters) | |
val observer = MyLifecycleObserver() | |
lifecycle.addObserver(observer) | |
viewModel.getCharacters().observe(this, Observer { characters -> | |
characters?.let { | |
adapter.setItems(characters) | |
} | |
if (recyclerState != null) { | |
recyclerCharacters.layoutManager.onRestoreInstanceState(recyclerState) | |
recyclerState = null | |
} | |
}) | |
val llm = LinearLayoutManager(this) | |
recyclerCharacters.layoutManager = llm | |
recyclerCharacters.adapter = adapter | |
recyclerCharacters.addOnScrollListener(object : RecyclerView.OnScrollListener() { | |
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { | |
super.onScrollStateChanged(recyclerView, newState) | |
val lastVisibleItemPosition = llm.findLastVisibleItemPosition() | |
if (lastVisibleItemPosition == adapter.itemCount - 1 && !viewModel.isLoading) { | |
Log.d("NGVL", "Loading more...") | |
loadCharacters(viewModel.currentPage + 1) | |
} | |
} | |
}) | |
loadCharacters(0) | |
} | |
override fun onSaveInstanceState(outState: Bundle?) { | |
super.onSaveInstanceState(outState) | |
outState?.putParcelable("lmState", recyclerCharacters.layoutManager.onSaveInstanceState()) | |
} | |
override fun onRestoreInstanceState(savedInstanceState: Bundle?) { | |
super.onRestoreInstanceState(savedInstanceState) | |
recyclerState = savedInstanceState?.getParcelable("lmState") | |
} | |
private fun loadCharacters(page: Int) { | |
viewModel.load(page) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.support.v7.widget.RecyclerView | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import br.com.nglauber.marvel.R | |
import br.com.nglauber.marvel.extensions.load | |
import br.com.nglauber.marvel.model.api.entity.Character | |
import kotlinx.android.synthetic.main.item_character.view.* | |
class CharactersAdapter() : RecyclerView.Adapter<CharactersAdapter.VH>() { | |
private val items = mutableListOf<Character>() | |
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): VH { | |
val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_character, parent, false) | |
return VH(view) | |
} | |
override fun onBindViewHolder(holder: VH, position: Int) { | |
val character = items[position] | |
holder.txtName.text = character.name | |
holder.imgThumbnail.load("${character.thumbnail.path}/standard_medium.${character.thumbnail.extension}") | |
} | |
override fun getItemCount(): Int = items.size | |
class VH(itemView: View) : RecyclerView.ViewHolder(itemView) { | |
val imgThumbnail = itemView.imgThumbnail | |
val txtName = itemView.txtName | |
} | |
fun setItems(characters: List<Character>) { | |
items.clear() | |
items.addAll(characters) | |
notifyDataSetChanged() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.arch.lifecycle.LiveData | |
import android.arch.lifecycle.MutableLiveData | |
import android.arch.lifecycle.ViewModel | |
import br.com.nglauber.marvel.model.api.MarvelApi | |
import br.com.nglauber.marvel.model.api.entity.Character | |
import kotlinx.coroutines.experimental.android.UI | |
import kotlinx.coroutines.experimental.launch | |
import ru.gildor.coroutines.retrofit.await | |
class CharactersViewModel : ViewModel() { | |
var isLoading: Boolean = false | |
private set | |
var currentPage = -1 | |
private set | |
private val characters = MutableLiveData<List<Character>>() | |
fun getCharacters(): LiveData<List<Character>> { | |
return characters | |
} | |
init { | |
characters.value = emptyList() | |
} | |
fun load(page: Int) { | |
launch(UI) { | |
isLoading = true | |
if (page > currentPage) { | |
val result = MarvelApi.loadCharacters(page).await() | |
val list = characters.value?.toMutableList() | |
list?.addAll(result.data.results) | |
characters.value = list | |
} | |
isLoading = false | |
currentPage++ | |
} | |
} | |
fun reset() { | |
isLoading = false | |
currentPage = -1 | |
characters.value = emptyList() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data class Response( | |
val code: Int, | |
val etag: String, | |
val data: Data | |
) | |
data class Data( | |
val offset: Int, | |
val limit: Int, | |
val total: Int, | |
val count: Int, | |
val results: List<Character> | |
) | |
data class Character( | |
val id: Int, | |
val name: String, | |
val description: String, | |
val thumbnail: Thumbnail | |
) | |
data class Thumbnail( | |
val path: String, | |
val extension: String | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import br.com.nglauber.marvel.extensions.md5 | |
import br.com.nglauber.marvel.model.api.entity.API_KEY | |
import br.com.nglauber.marvel.model.api.entity.PRIVATE_KEY | |
import com.google.gson.GsonBuilder | |
import okhttp3.OkHttpClient | |
import okhttp3.logging.HttpLoggingInterceptor | |
import retrofit2.Retrofit | |
import retrofit2.converter.gson.GsonConverterFactory | |
import java.util.* | |
object MarvelApi { | |
private val api: MarvelApiDef by lazy { | |
val logging = HttpLoggingInterceptor() | |
logging.level = HttpLoggingInterceptor.Level.BODY | |
val httpClient = OkHttpClient.Builder() | |
httpClient.addInterceptor(logging) | |
httpClient.addInterceptor { chain -> | |
val original = chain.request() | |
val originalHttpUrl = original.url() | |
val ts = (Calendar.getInstance(TimeZone.getTimeZone("UTC")).timeInMillis / 1000L).toString() | |
val url = originalHttpUrl.newBuilder() | |
.addQueryParameter("apikey", API_KEY) | |
.addQueryParameter("ts", ts) | |
.addQueryParameter("hash", "$ts$PRIVATE_KEY$API_KEY".md5()) | |
.build() | |
val requestBuilder = original.newBuilder().url(url) | |
val request = requestBuilder.build() | |
chain.proceed(request) | |
} | |
val gson = GsonBuilder().setLenient().create() | |
val retrofit = Retrofit.Builder() | |
.baseUrl("http://gateway.marvel.com/v1/public/") | |
.addConverterFactory(GsonConverterFactory.create(gson)) | |
.client(httpClient.build()) | |
.build() | |
retrofit.create<MarvelApiDef>(MarvelApiDef::class.java) | |
} | |
fun loadCharacters(page: Int) = api.allCharacters(page * 20) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import br.com.nglauber.marvel.model.api.entity.Response | |
import retrofit2.Call | |
import retrofit2.http.GET | |
import retrofit2.http.Query | |
interface MarvelApiDef { | |
@GET("characters") | |
fun allCharacters(@Query("offset") offset: Int? = 0): Call<Response> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment