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: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
A small Reddit Android client developed using Kotlin

## Tutorial
https://android.jlelse.eu/learn-kotlin-while-developing-an-android-app-introduction-567e21ff9664
Original from this tutorial: https://android.jlelse.eu/learn-kotlin-while-developing-an-android-app-introduction-567e21ff9664

## Libraries used:
- Retrofit 2.0
- Coroutines (previously with RxJava)
- Picasso
- Dagger 2
- Kotlin Android Extensions

## Update
- Lifecycle aware
- TODO: Use ViewModel, Paging
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
theme: jekyll-theme-time-machine
50 changes: 34 additions & 16 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
compileSdkVersion 28

defaultConfig {
applicationId "com.droidcba.kedditbysteps"
minSdkVersion 16
targetSdkVersion 27
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
Expand All @@ -27,37 +26,56 @@ android {
packagingOptions {
exclude 'META-INF/main.kotlin_module'
}

androidExtensions {
experimental = true
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.squareup.picasso:picasso:2.71828'

implementation 'com.squareup.picasso:picasso:2.5.2'
// livedata & viewmodel
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

// lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"

// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.0.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

// Dagger 2
implementation 'com.google.dagger:dagger:2.11'
kapt 'com.google.dagger:dagger-compiler:2.11'
implementation "com.google.dagger:dagger:$dagger2_version"
kapt "com.google.dagger:dagger-compiler:$dagger2_version"
compileOnly 'org.glassfish:javax.annotation:10.0-b28'

// okhttp
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'

// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.23.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2"
// Coroutines - Retrofit extention
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

// Tests
testImplementation 'junit:junit:4.12'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0-alpha04'
testImplementation 'org.jetbrains.spek:spek:1.0.25'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
testImplementation "org.jetbrains.spek:spek-api:$spek_version"
testImplementation "org.jetbrains.spek:spek-junit-platform-engine:$spek_version"
testImplementation "org.junit.platform:junit-platform-runner:$junitPlatformVersion"

testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-test:1.2.51"
}
repositories {
mavenCentral()
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 @@ -5,7 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".KedditApp"
android:name=".app.KedditApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand Down
14 changes: 7 additions & 7 deletions app/src/main/java/com/droidcba/kedditbysteps/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.droidcba.kedditbysteps

import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.droidcba.kedditbysteps.features.news.NewsFragment

class MainActivity : AppCompatActivity() {
Expand All @@ -20,7 +20,7 @@ class MainActivity : AppCompatActivity() {
}
}

fun changeFragment(f: Fragment, cleanStack: Boolean = false) {
private fun changeFragment(f: Fragment, cleanStack: Boolean = false) {
val ft = supportFragmentManager.beginTransaction()
if (cleanStack) {
clearBackStack()
Expand All @@ -32,11 +32,11 @@ class MainActivity : AppCompatActivity() {
ft.commit()
}

fun clearBackStack() {
private fun clearBackStack() {
val manager = supportFragmentManager
if (manager.backStackEntryCount > 0) {
val first = manager.getBackStackEntryAt(0)
manager.popBackStack(first.id, FragmentManager.POP_BACK_STACK_INCLUSIVE)
manager.popBackStack(first.id, androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
}

Expand Down
5 changes: 0 additions & 5 deletions app/src/main/java/com/droidcba/kedditbysteps/api/NewsAPI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ package com.droidcba.kedditbysteps.api

import retrofit2.Call

/**
* News API
*
* @author juancho.
*/
interface NewsAPI {
fun getNewsOldApi(after: String, limit: String): Call<RedditNewsResponse>
suspend fun getNews(after: String, limit: String): RedditNewsResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import retrofit2.Call
import javax.inject.Inject

class NewsRestAPI @Inject constructor(private val redditApi: RedditApi) : NewsAPI {

override suspend fun getNews(after: String, limit: String): RedditNewsResponse {
return redditApi.getDeferredTop(after, limit).await()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.droidcba.kedditbysteps.api

import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.Deferred
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package com.droidcba.kedditbysteps
package com.droidcba.kedditbysteps.app

import android.app.Application
import com.droidcba.kedditbysteps.di.AppModule
import com.droidcba.kedditbysteps.di.news.DaggerNewsComponent
import com.droidcba.kedditbysteps.di.news.NewsComponent

/**
*
* @author juancho.
*/
class KedditApp : Application() {

companion object {
Expand All @@ -18,8 +12,6 @@ class KedditApp : Application() {
override fun onCreate() {
super.onCreate()
newsComponent = DaggerNewsComponent.builder()
.appModule(AppModule(this))
//.newsModule(NewsModule()) Module with empty constructor is implicitly created by dagger.
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.droidcba.kedditbysteps.commons

import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class InfiniteScrollListener(
val func: () -> Unit,
val layoutManager: LinearLayoutManager) : RecyclerView.OnScrollListener() {
val func: () -> Unit,
private val layoutManager: LinearLayoutManager) : RecyclerView.OnScrollListener() {

private var previousTotal = 0
private var loading = true
Expand All @@ -30,13 +30,12 @@ class InfiniteScrollListener(
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
Log.i("InfiniteScrollListener", "End reached")
func()
loading = true
}
}
}

}
}
63 changes: 13 additions & 50 deletions app/src/main/java/com/droidcba/kedditbysteps/commons/Models.kt
Original file line number Diff line number Diff line change
@@ -1,61 +1,24 @@
package com.droidcba.kedditbysteps.commons

import android.os.Parcel
import android.os.Parcelable
import com.droidcba.kedditbysteps.commons.adapter.AdapterConstants
import com.droidcba.kedditbysteps.commons.adapter.ViewType
import kotlinx.android.parcel.Parcelize

@Parcelize
data class RedditNews(
val after: String,
val before: String,
val news: List<RedditNewsItem>) : Parcelable {
companion object {
@Suppress("unused")
@JvmField val CREATOR: Parcelable.Creator<RedditNews> = object : Parcelable.Creator<RedditNews> {
override fun createFromParcel(source: Parcel): RedditNews = RedditNews(source)
override fun newArray(size: Int): Array<RedditNews?> = arrayOfNulls(size)
}
}

constructor(source: Parcel) : this(source.readString(), source.readString(), source.createTypedArrayList(RedditNewsItem.CREATOR))

override fun describeContents() = 0

override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(after)
dest?.writeString(before)
dest?.writeTypedList(news)
}
}
val after: String,
val before: String,
val news: List<RedditNewsItem>) : Parcelable

@Parcelize
data class RedditNewsItem(
val author: String,
val title: String,
val numComments: Int,
val created: Long,
val thumbnail: String,
val url: String?
val author: String,
val title: String,
val numComments: Int,
val created: Long,
val thumbnail: String,
val url: String?
) : ViewType, Parcelable {

override fun getViewType() = AdapterConstants.NEWS

companion object {
@JvmField val CREATOR: Parcelable.Creator<RedditNewsItem> = object : Parcelable.Creator<RedditNewsItem> {
override fun createFromParcel(source: Parcel): RedditNewsItem = RedditNewsItem(source)
override fun newArray(size: Int): Array<RedditNewsItem?> = arrayOfNulls(size)
}
}

constructor(source: Parcel) : this(source.readString(), source.readString(), source.readInt(), source.readLong(), source.readString(), source.readString())

override fun describeContents() = 0

override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(author)
dest?.writeString(title)
dest?.writeInt(numComments)
dest?.writeLong(created)
dest?.writeString(thumbnail)
dest?.writeString(url)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package com.droidcba.kedditbysteps.commons.adapter
object AdapterConstants {
val NEWS = 1
val LOADING = 2
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.droidcba.kedditbysteps.commons.adapter

interface ViewType {
fun getViewType(): Int
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.droidcba.kedditbysteps.commons.adapter

import android.support.v7.widget.RecyclerView
import android.view.ViewGroup

interface ViewTypeDelegateAdapter {

fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder

fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType)
}
fun onCreateViewHolder(parent: ViewGroup): androidx.recyclerview.widget.RecyclerView.ViewHolder
fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, item: ViewType)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ import android.widget.ImageView
import com.droidcba.kedditbysteps.R
import com.squareup.picasso.Picasso


fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)
}

fun ImageView.loadImg(imageUrl: String) {
if (TextUtils.isEmpty(imageUrl)) {
Picasso.with(context).load(R.mipmap.ic_launcher).into(this)
Picasso.get().load(R.mipmap.ic_launcher).into(this)
} else {
Picasso.with(context).load(imageUrl).into(this)
Picasso.get().load(imageUrl).into(this)
}
}

fun <T> androidLazy(initializer: () -> T) : Lazy<T> = lazy(LazyThreadSafetyMode.NONE, initializer)
fun <T> androidLazy(initializer: () -> T) : Lazy<T> = lazy(LazyThreadSafetyMode.NONE, initializer)
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ fun Long.getFriendlyTime(): String {
sb.append(" ago")

return sb.toString()
}
}
Loading