├── app
├── .gitignore
├── src
│ └── main
│ │ ├── assets
│ │ ├── screen1.png
│ │ ├── screen2.png
│ │ ├── screen3.png
│ │ └── screen4.jpg
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ └── layout
│ │ │ ├── view_news_item.xml
│ │ │ └── activity_main.xml
│ │ ├── java
│ │ └── com
│ │ │ └── ktoi
│ │ │ └── toi
│ │ │ ├── view
│ │ │ ├── interfaces
│ │ │ │ └── NewsView.kt
│ │ │ ├── adapters
│ │ │ │ └── NewsAdapter.kt
│ │ │ └── activities
│ │ │ │ └── MainActivity.kt
│ │ │ ├── shared
│ │ │ ├── NewsApiInterface.kt
│ │ │ ├── Constants.kt
│ │ │ ├── AppDelegate.kt
│ │ │ ├── NewsModule.kt
│ │ │ └── DateUtil.kt
│ │ │ ├── model
│ │ │ ├── Image.kt
│ │ │ ├── Pagination.kt
│ │ │ ├── NewsResponse.java
│ │ │ └── NewsItem.kt
│ │ │ └── presenter
│ │ │ └── NewsPresenter.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── made_in_steelkiwi.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/made_in_steelkiwi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/made_in_steelkiwi.png
--------------------------------------------------------------------------------
/app/src/main/assets/screen1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/assets/screen1.png
--------------------------------------------------------------------------------
/app/src/main/assets/screen2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/assets/screen2.png
--------------------------------------------------------------------------------
/app/src/main/assets/screen3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/assets/screen3.png
--------------------------------------------------------------------------------
/app/src/main/assets/screen4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/assets/screen4.jpg
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/steelkiwi/Getting-started-with-Kotlin/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea/
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TOI
3 | Agency: %s
4 | List is empty
5 | Connection error
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/view/interfaces/NewsView.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.view.interfaces
2 |
3 | import com.ktoi.toi.model.NewsItem
4 |
5 | interface NewsView {
6 |
7 | fun onNewsItemLoaded(newsItems: List)
8 |
9 | fun onError(throwable: Throwable?)
10 |
11 | fun hideLoading()
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/shared/NewsApiInterface.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.shared
2 |
3 | import com.ktoi.toi.model.NewsResponse
4 |
5 | import retrofit2.http.GET
6 | import rx.Observable
7 |
8 | interface NewsApiInterface {
9 |
10 | @GET("/feeds/newsdefaultfeeds.cms?feedtype=sjson")
11 | fun getNews(): Observable
12 |
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/shared/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.shared
2 |
3 | class Constants {
4 |
5 | companion object {
6 |
7 | val NEWS_ENDPOINT = "http://timesofindia.indiatimes.com/"
8 | val DATE_PATTERN = "MMM dd, yyyy, hh.mma"
9 | val IST_TIME_ZONE = "IST"
10 |
11 |
12 | val LEADING_ZERO_TEMPLATE = "%02d"
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/model/Image.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.model
2 |
3 | import com.google.gson.annotations.Expose
4 | import com.google.gson.annotations.SerializedName
5 |
6 | import io.realm.RealmObject
7 |
8 | open class Image : RealmObject() {
9 |
10 |
11 | @SerializedName("Photo")
12 | @Expose
13 | var photo: String? = null
14 |
15 | @SerializedName("Thumb")
16 | @Expose
17 | var thumb: String? = null
18 |
19 |
20 | @SerializedName("PhotoCaption")
21 | @Expose
22 | var photoCaption: String? = null
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 8dp
6 | 16dp
7 | 40dp
8 | 16sp
9 | 56dp
10 | 13sp
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/model/Pagination.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.model
2 |
3 | import com.google.gson.annotations.Expose
4 | import com.google.gson.annotations.SerializedName
5 |
6 | import javax.annotation.Generated
7 |
8 | @Generated("org.jsonschema2pojo")
9 | class Pagination {
10 |
11 | @SerializedName("TotalPages")
12 | @Expose
13 | var totalPages: String? = null
14 |
15 | @SerializedName("PageNo")
16 | @Expose
17 | var pageNo: String? = null
18 |
19 | @SerializedName("PerPage")
20 | @Expose
21 | var perPage: String? = null
22 |
23 | @SerializedName("WebURL")
24 | @Expose
25 | var webURL: String? = null
26 |
27 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/polyakov/Android/Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/shared/AppDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.shared
2 |
3 | import android.app.Application
4 | import com.ktoi.toi.view.activities.MainActivity
5 | import dagger.Component
6 | import io.realm.Realm
7 | import io.realm.RealmConfiguration
8 | import javax.inject.Singleton
9 |
10 | class AppDelegate : Application() {
11 |
12 | var injector: AppInjector? = null
13 |
14 | @Singleton
15 | @Component(modules = arrayOf(NewsModule::class))
16 | interface AppInjector {
17 |
18 | fun inject(activity: MainActivity)
19 |
20 | }
21 |
22 | override fun onCreate() {
23 | super.onCreate()
24 | injector = DaggerAppDelegate_AppInjector.builder().build()
25 |
26 | Realm.init(this)
27 | val config = RealmConfiguration.Builder().build()
28 | Realm.setDefaultConfiguration(config)
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/model/NewsResponse.java:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.model;
2 |
3 | import com.google.gson.annotations.Expose;
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | /**
10 | * example of work of Kotlin project with Java class.
11 | */
12 | public class NewsResponse {
13 |
14 | @SerializedName("Pagination")
15 | @Expose
16 | private Pagination pagination;
17 | @SerializedName("NewsItem")
18 | @Expose
19 | private List newsItem = new ArrayList();
20 |
21 | public Pagination getPagination() {
22 | return pagination;
23 | }
24 |
25 | public void setPagination(Pagination pagination) {
26 | this.pagination = pagination;
27 | }
28 |
29 | public List getNewsItem() {
30 | return newsItem;
31 | }
32 |
33 | public void setNewsItem(List newsItem) {
34 | this.newsItem = newsItem;
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/shared/NewsModule.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.shared
2 |
3 | import com.ktoi.toi.presenter.NewsPresenter
4 | import javax.inject.Singleton
5 |
6 | import dagger.Module
7 | import dagger.Provides
8 | import retrofit2.Retrofit
9 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
10 | import retrofit2.converter.gson.GsonConverterFactory
11 | import rx.schedulers.Schedulers
12 |
13 |
14 | /**
15 | * Created by polyakov on 29.10.16.
16 | */
17 | @Module
18 | class NewsModule {
19 |
20 | @Provides
21 | @Singleton
22 | fun provideNewsPresenter(): NewsPresenter {
23 | return NewsPresenter()
24 | }
25 |
26 | @Provides
27 | @Singleton
28 | internal fun provideNewApiInterface(): NewsApiInterface {
29 | val retrofit = Retrofit.Builder()
30 | .baseUrl(Constants.NEWS_ENDPOINT)
31 | .addConverterFactory(GsonConverterFactory.create())
32 | .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
33 | .build()
34 | return retrofit.create(NewsApiInterface::class.java)
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/model/NewsItem.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.model
2 |
3 | import com.google.gson.annotations.Expose
4 | import com.google.gson.annotations.SerializedName
5 |
6 | import io.realm.RealmObject
7 |
8 | open class NewsItem : RealmObject() {
9 |
10 | @SerializedName("NewsItemId")
11 | @Expose
12 | var newsItemId: String? = null
13 |
14 | @SerializedName("HeadLine")
15 | @Expose
16 | var headLine: String? = null
17 |
18 | @SerializedName("Agency")
19 | @Expose
20 | var agency: String? = null
21 |
22 | @SerializedName("DateLine")
23 | @Expose
24 | var dateLine: String? = null
25 |
26 | @SerializedName("WebURL")
27 | @Expose
28 | var webURL: String? = null
29 |
30 | @SerializedName("Caption")
31 | @Expose
32 | var caption: String? = null
33 |
34 | @SerializedName("Image")
35 | @Expose
36 | var image: Image? = null
37 |
38 | @SerializedName("Keywords")
39 | @Expose
40 | var keywords: String? = null
41 |
42 | @SerializedName("Story")
43 | @Expose
44 | var story: String? = null
45 |
46 | @SerializedName("CommentCountUrl")
47 | @Expose
48 | var commentCountUrl: String? = null
49 |
50 | @SerializedName("CommentFeedUrl")
51 | @Expose
52 | var commentFeedUrl: String? = null
53 |
54 | @SerializedName("Related")
55 | @Expose
56 | var related: String? = null
57 |
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/shared/DateUtil.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.shared
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.Calendar
5 | import java.util.Locale
6 | import java.util.TimeZone
7 |
8 | class DateUtil {
9 |
10 | companion object {
11 |
12 | fun formatDate(originalDate: String): String {
13 |
14 | try {
15 | val sdf = SimpleDateFormat(Constants.DATE_PATTERN, Locale.ENGLISH)
16 | val timeZone = TimeZone.getTimeZone(Constants.IST_TIME_ZONE)
17 | sdf.timeZone = timeZone
18 | val date = sdf.parse(originalDate)
19 | val calendar = Calendar.getInstance()
20 | calendar.time = date
21 | val month = calendar.getDisplayName(
22 | Calendar.MONTH,
23 | Calendar.SHORT,
24 | Locale.getDefault())
25 | val dayOFMonth = calendar.get(Calendar.DAY_OF_MONTH)
26 | val hours = calendar.get(Calendar.HOUR_OF_DAY)
27 | val minutes = calendar.get(Calendar.MINUTE)
28 |
29 | return StringBuilder()
30 | .append(dayOFMonth.toString())
31 | .append(" ")
32 | .append(month)
33 | .append(" ")
34 | .append(String.format(Locale.getDefault(), Constants.LEADING_ZERO_TEMPLATE, hours))
35 | .append(":")
36 | .append(String.format(Locale.getDefault(), Constants.LEADING_ZERO_TEMPLATE, minutes))
37 | .toString()
38 | } catch (e: Exception) {
39 | e.printStackTrace()
40 | }
41 |
42 | return originalDate
43 | }
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/presenter/NewsPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.presenter
2 |
3 | import com.ktoi.toi.model.NewsItem
4 | import com.ktoi.toi.shared.NewsApiInterface
5 | import com.ktoi.toi.view.interfaces.NewsView
6 | import io.realm.Realm
7 | import rx.Observable
8 | import rx.Subscription
9 | import rx.android.schedulers.AndroidSchedulers
10 |
11 |
12 | /**
13 | * @author polyakov
14 | * 09.11.16.
15 | */
16 | class NewsPresenter {
17 |
18 | private var mNewsApiInterface: NewsApiInterface? = null
19 | private var mNewsView: NewsView? = null
20 |
21 | private var subscription: Subscription? = null
22 |
23 | fun onViewCreated(view: NewsView) {
24 | mNewsView = view
25 | }
26 |
27 | fun setNewsApiInterface(newsApiInterface: NewsApiInterface) {
28 | this.mNewsApiInterface = newsApiInterface
29 | }
30 |
31 | fun loadNews() {
32 | subscription = mNewsApiInterface!!
33 | .getNews()
34 | .map { it.newsItem }
35 | .flatMap({ items ->
36 | Realm.getDefaultInstance().executeTransaction({ realm ->
37 | realm.delete(NewsItem::class.java)
38 | realm.insert(items)
39 | })
40 | Observable.just(items)
41 | })
42 | .onErrorResumeNext { throwable ->
43 | val realm = Realm.getDefaultInstance()
44 | val items = realm.where(NewsItem::class.java).findAll()
45 | Observable.just(realm.copyFromRealm(items))
46 | }
47 | .observeOn(AndroidSchedulers.mainThread())
48 | .doOnTerminate { mNewsView?.hideLoading() }
49 | .subscribe({ mNewsView?.onNewsItemLoaded(it) }, { mNewsView?.onError(it) })
50 | }
51 |
52 | fun onDestroy() {
53 | subscription?.unsubscribe()
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_news_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
22 |
23 |
31 |
32 |
37 |
38 |
42 |
43 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'realm-android'
4 |
5 | android {
6 | compileSdkVersion 25
7 | buildToolsVersion "25.0.0"
8 | defaultConfig {
9 | applicationId "com.ktoi.toi"
10 | minSdkVersion 21
11 | targetSdkVersion 25
12 | versionCode 1
13 | versionName "1.0"
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | sourceSets {
23 | main.java.srcDirs += 'src/main/kotlin'
24 | }
25 | }
26 | kapt {
27 | generateStubs = true
28 | }
29 | buildscript {
30 | ext.supportVersion = '25.0.0'
31 | ext.daggerVersion = '2.7'
32 | ext.retrofitVersion = '2.1.0'
33 | ext.rxVersion = '1.2.1'
34 | repositories {
35 | mavenCentral()
36 | jcenter()
37 | }
38 | dependencies {
39 | classpath "io.realm:realm-gradle-plugin:2.1.1"
40 | }
41 | }
42 |
43 | dependencies {
44 | compile fileTree(dir: 'libs', include: ['*.jar'])
45 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
46 | exclude group: 'com.android.support', module: 'support-annotations'
47 | })
48 | compile 'com.android.support:appcompat-v7:25.0.0'
49 | testCompile 'junit:junit:4.12'
50 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
51 | compile "com.android.support:cardview-v7:${supportVersion}"
52 | compile "com.android.support:design:${supportVersion}"
53 |
54 | // Dagger 2
55 | compile "com.google.dagger:dagger:${daggerVersion}"
56 | kapt "com.google.dagger:dagger-compiler:${daggerVersion}"
57 | provided "org.glassfish:javax.annotation:3.1.1"
58 |
59 | //Retrofit 2
60 | compile "com.squareup.retrofit2:retrofit:${retrofitVersion}"
61 | compile "com.squareup.retrofit2:adapter-rxjava:${retrofitVersion}"
62 | compile "com.squareup.retrofit2:converter-gson:${retrofitVersion}"
63 |
64 | compile 'com.google.code.gson:gson:2.8.0'
65 |
66 | compile "io.reactivex:rxjava:${rxVersion}"
67 | compile "io.reactivex:rxandroid:${rxVersion}"
68 |
69 | compile 'com.github.bumptech.glide:glide:3.7.0'
70 |
71 | }
72 | repositories {
73 | mavenCentral()
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
25 |
26 |
27 |
28 |
32 |
33 |
38 |
39 |
43 |
44 |
45 |
46 |
51 |
52 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/view/adapters/NewsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.view.adapters
2 |
3 | import android.graphics.Bitmap
4 | import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
5 | import android.support.v7.widget.RecyclerView.Adapter
6 | import android.support.v7.widget.RecyclerView.ViewHolder
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.widget.ImageView
11 | import android.widget.TextView
12 |
13 | import com.bumptech.glide.Glide
14 | import com.bumptech.glide.request.target.BitmapImageViewTarget
15 | import com.ktoi.toi.R
16 | import com.ktoi.toi.model.NewsItem
17 | import com.ktoi.toi.shared.DateUtil
18 |
19 |
20 | /**
21 | * Created by polyakov on 29.10.16.
22 | */
23 |
24 | class NewsAdapter : Adapter() {
25 |
26 | private var mDataSource: List? = null
27 |
28 | fun setDataSource(dataSource: List) {
29 | this.mDataSource = dataSource
30 | notifyDataSetChanged()
31 | }
32 |
33 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
34 | val view = LayoutInflater.from(parent.context).inflate(R.layout.view_news_item, parent, false)
35 | return NewsViewHolder(view)
36 | }
37 |
38 | override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
39 | val newsItem = mDataSource!![position]
40 | holder.bind(newsItem)
41 | }
42 |
43 |
44 | override fun getItemCount(): Int {
45 | return mDataSource?.size ?: 0
46 | }
47 |
48 | class NewsViewHolder internal constructor(itemView: View) : ViewHolder(itemView) {
49 |
50 | var newsItem: NewsItem? = null
51 |
52 | private val mImageView: ImageView? by lazy {
53 | itemView.findViewById(R.id.image_view) as ImageView?
54 | }
55 | private val mHeadLineTextView: TextView? by lazy {
56 | itemView.findViewById(R.id.headLineTextView) as TextView?
57 | }
58 | private val mAgencyTextView: TextView?by lazy {
59 | itemView.findViewById(R.id.agencyTextView) as TextView?
60 | }
61 | private val mDateTextView: TextView? by lazy {
62 | itemView.findViewById(R.id.dateTextView) as TextView?
63 | }
64 | private val mCaptionTextView: TextView? by lazy {
65 | itemView.findViewById(R.id.captionTextView) as TextView?
66 | }
67 |
68 | fun bind(newsItem: NewsItem) {
69 | this.newsItem = newsItem
70 | mHeadLineTextView!!.text = newsItem.headLine
71 |
72 | if (newsItem.agency == null)
73 | mAgencyTextView!!.visibility = View.GONE
74 | else
75 | mAgencyTextView!!.text = itemView.resources.getString(
76 | R.string.view_news_item_agency,
77 | newsItem.agency)
78 |
79 | mDateTextView!!.text = DateUtil.formatDate(newsItem.dateLine!!)
80 |
81 | mCaptionTextView!!.text = newsItem.caption
82 |
83 | Glide.with(itemView.context)
84 | .load(newsItem.image?.thumb)
85 | .asBitmap().centerCrop()
86 | .into(object : BitmapImageViewTarget(mImageView!!) {
87 | override fun setResource(resource: Bitmap) {
88 | val bitmapDrawable = RoundedBitmapDrawableFactory.create(
89 | itemView.resources,
90 | resource)
91 | bitmapDrawable.isCircular = true
92 | mImageView!!.setImageDrawable(bitmapDrawable)
93 | }
94 | })
95 | }
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ktoi/toi/view/activities/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ktoi.toi.view.activities
2 |
3 | import android.graphics.Color
4 | import android.support.v7.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.support.v4.widget.SwipeRefreshLayout
7 | import android.support.v7.widget.LinearLayoutManager
8 | import android.support.v7.widget.RecyclerView
9 | import android.support.v7.widget.Toolbar
10 | import android.view.View
11 | import android.widget.ProgressBar
12 | import android.widget.TextView
13 | import com.ktoi.toi.R
14 | import com.ktoi.toi.model.NewsItem
15 | import com.ktoi.toi.presenter.NewsPresenter
16 | import com.ktoi.toi.shared.AppDelegate
17 | import com.ktoi.toi.shared.NewsApiInterface
18 | import com.ktoi.toi.view.adapters.NewsAdapter
19 | import com.ktoi.toi.view.interfaces.NewsView
20 | import java.io.IOException
21 | import javax.inject.Inject
22 |
23 | class MainActivity : AppCompatActivity(), NewsView, SwipeRefreshLayout.OnRefreshListener {
24 |
25 | @Inject
26 | lateinit var mNewsPresenter: NewsPresenter
27 | @Inject
28 | lateinit var mNewsAPI: NewsApiInterface
29 |
30 | private val mProgressBar: ProgressBar by lazy {
31 | findViewById(R.id.progress_bar) as ProgressBar
32 | }
33 | private val mStatusTextView: TextView by lazy {
34 | findViewById(R.id.status_text_view) as TextView
35 | }
36 | private val mRecyclerView: RecyclerView by lazy {
37 | findViewById(R.id.recycler_view) as RecyclerView
38 | }
39 | private val mSwipeRefreshLayout: SwipeRefreshLayout by lazy {
40 | findViewById(R.id.swipe_layout) as SwipeRefreshLayout
41 | }
42 | private val toolbar: Toolbar by lazy {
43 | findViewById(R.id.toolbar) as Toolbar
44 | }
45 |
46 | private val mNewsAdapter = NewsAdapter()
47 |
48 | override fun onCreate(savedInstanceState: Bundle?) {
49 | super.onCreate(savedInstanceState)
50 | setContentView(R.layout.activity_main)
51 | setSupportActionBar(toolbar)
52 | inject()
53 | prepareSwipeRefreshLayout()
54 | prepareRecyclerView()
55 | mRecyclerView.adapter = mNewsAdapter
56 | mNewsPresenter.setNewsApiInterface(mNewsAPI)
57 | mNewsPresenter.onViewCreated(this)
58 | mNewsPresenter.loadNews()
59 | }
60 |
61 | private fun inject() {
62 | (application as AppDelegate).injector?.inject(this)
63 | }
64 |
65 | private fun prepareSwipeRefreshLayout() {
66 | mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW)
67 | mSwipeRefreshLayout.setOnRefreshListener(this)
68 | }
69 |
70 | private fun prepareRecyclerView() {
71 | val layoutManager = LinearLayoutManager(this)
72 | mRecyclerView.layoutManager = layoutManager
73 | mRecyclerView.setHasFixedSize(true)
74 | }
75 |
76 | override fun onNewsItemLoaded(newsItems: List) {
77 | mSwipeRefreshLayout.isRefreshing = false
78 | mProgressBar.visibility = View.GONE
79 | if(newsItems.isEmpty()) {
80 | mStatusTextView.setText(R.string.list_is_empty)
81 | return
82 | }
83 | mStatusTextView.text = null
84 | mNewsAdapter.setDataSource(newsItems)
85 | }
86 |
87 | override fun onError(throwable: Throwable?) {
88 | mSwipeRefreshLayout.isRefreshing = false
89 | mProgressBar.visibility = View.GONE
90 | if (throwable is IOException) {
91 | mStatusTextView.setText(R.string.connection_error)
92 | } else {
93 | mStatusTextView.setText(R.string.list_is_empty)
94 | }
95 | }
96 |
97 | override fun hideLoading() {
98 | mSwipeRefreshLayout.isRefreshing = false
99 | mProgressBar.visibility = View.GONE
100 | }
101 |
102 | override fun onRefresh() {
103 | mNewsPresenter.loadNews()
104 | }
105 |
106 | override fun onStop() {
107 | super.onStop()
108 | mNewsPresenter.onDestroy()
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Getting started with Kotlin And third-party libraries Glide, Dagger 2, Retrofit 2, Realm, RxJava and MVP architecture on Android
2 | [](http://steelkiwi.com/blog/getting-started-kotlin-libraries-Glide-Dagger/)
3 | [](https://android-arsenal.com/details/3/5316)
4 | [](http://steelkiwi.com/blog/)
5 | ## Read our new article [here](https://github.com/steelkiwi/collection_in_kotlin)
6 |
7 | It’s not a mystery to Android developers all over the world that the whole IT community has been trying to find a decent replacement for Java. Until 2011, before the Kotlin creation had been announced, Scala was the most suitable candidate. However, when JetBrains presented Kotlin programming language and its source code, it gradually became the strongest Java alternative in the field. This resulted in Kotlin becoming the official Android development language in 2017.
8 |
9 | Kotlin was claimed to be a response to the massive amounts of code developers often had to write in Java and to Scala’s low-speed compilation. Today, many famous IT companies use Kotlin in their projects. The attention to this language is continuing to grow, as it wins over developers with its syntax and the fact that Kotlin is supported by several IDEs as a plugin. Even Jake Wharton, a recognized Android gospeller, uses Kotlin in his own projects, thus encouraging the whole Android community to use this language.
10 |
11 | Let’s stay in tune with this trend and try Kotlin in practice to see its extensively debated pros and cons. Also, we will learn how to start developing Android applications in Kotlin.
12 |
13 | ## What does Kotlin hide?
14 | ### Mostly Kotlin is commended for its briefness and code safety and also for compatibility with Java and flexible usage. It’s hard to list all its advantages, so let’s consider the most interesting ones.
15 | ## Strong sides of Kotlin:
16 | * It is Fully [compatible with Java](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/model/NewsResponse.java#L12). It can use all available Java frameworks, libraries and also with separate modules in current projects.
17 | * It has [open source code](https://kotlinlang.org/), so it’s easy to track and determine an issue and if you come up with it, you can always submit it to Kotlin developers.
18 | * Kotlins repository consumes less space than Scala, and adding Kotkin to a project is equal to Google library.
19 | * Starting from Java 6, it can use the major part of Java 7 and some portable elements of Java 8. That’s why it is easily available even if you are facing troubles updating to new JVM version.
20 | * There’s immutability by default.
21 | * It has Higher-Order Functions, i.e. the functions that take functions as parameters.
22 | * Null safety. Variables in Kotlin can’t possess the null value by default unless you denote them.
23 | * Kotlin became an officially supported language at the last Google IO conference (May 17th, 2017).
24 |
25 | As for today, Kotlin isn’t very popular. The language is young and the community is yet to be formed. However, the ambitions for Kotlin are high. It has been under development for more than five years and the developers tried their best to avoid Java’s past mistakes. Java’s principle “Write Once Run Anywhere” doesn’t apply here as the language constantly changes and improves. Kotlin developers promise many interesting features, including the coverage of current Scala functionality.
26 |
27 | With so many advantages, Kotlin almost seems to be the perfect programming language. However, it does also have some cons, even though they might not be as obvious.
28 |
29 | ## WEAKNESSES OF KOTLIN:
30 | * Kotlin is Java 6 bytecode-oriented and doesn’t use a number of improvements available in Java 8 like invoke-dynamic.
31 | * There can appear issues with annotation processing.
32 | * There are no analogues to plugin-macros or compilers which thus limits the usage of convenient Scala macros or plugins like Checker Framework from Java.
33 | * If you practice joint usage of Java and Kotlin you need to follow certain rules of compatibility.
34 | * You will have to rewrite some proven to be effective solutions for Android, including the architectural ones as Kotlin enables the usage of alternative approach.
35 | * The language is pretty young and hasn’t found any specific niche, however, it is suitable not only for Android but also for backend development.
36 | * The language is pretty green and there are no worked out best practices for solving particular tasks.
37 |
38 | ## Let’s dwell on compatibility and look closely at the example of a simple application development using such popular libraries like:
39 | * [Glide](https://github.com/bumptech/glide) - one of the most popular image loading libraries from Bump Technologies. Glide Library can upload and display images from many sources and manage cache, while also consuming small amount of internal memory for image processing.
40 | * [Dagger 2](https://github.com/google/dagger) - the library developed by Google Inc. as a fast dependency injector for Android. It helps developers implement a Dependency Injection pattern which is “Inversion of control specific form”. Check out Kotlin + Dagger2 example below.
41 | * [Retrofit 2](https://github.com/square/retrofit) - a type-safe and the most popular HTTP client for Android Development. Kotlin and Retrofit libraries can be used in Android development without any drawbacks. Later we will show you a Kotlin Retrofit example.
42 | * [Realm database.](https://realm.io/) - a Mobile Database, which is an excellent substitute for SQLite and ORMs, as it operates faster, simpler and safer. The major part of Realm Kotlin library is written in Java. Our examples are below and you can check Realm Kotlin examples in the official Realm-java repository on GitHub.
43 | * [RxJava + RxAndroid](https://github.com/ReactiveX/RxAndroid) - Reactive programming has been deeply rooted in Mobile Development. Rx is a powerful tool that allows to solve issues in a neat declarative way of functional programming.There are thousands of articles telling about the benefits of RxJava, RxKotlin, RxAndroid and RxBinding for Android development. We won’t step aside and will use these libraries to work with asynchronous operations in a more convenient way. We will demonstrate you a small example of using Realm with RxJava in Kotlin.
44 |
45 | ## The app serves as a good instance of libraries data usage and implementation of MVP architecture:
46 | 
47 | ## Let’s proceed to the development
48 | ## It’s pretty easy to start the development process with Kotlin. First of all, you need to install the plugin:
49 | 
50 | ## After that configure your project. The easiest way to do this is to press Ctrl+Shift+A and find Configure Kotlin in Project item that will appear in autocomplete:
51 | 
52 | ## To convert existing Java classes to Kotlin you need to find the command named Convert Java to Kotlin:
53 | 
54 | ## Now let's start the integration of the needed libraries
55 | ## Now let's start the integration of the needed libraries A few words before we start. Some libraries like Dagger 2 require Annotation Processing. Java Annotation Processing is not suitable for Kotlin. That’s why it includes its own Annotation Processing Tool for Kotlin (kapt), here is a great opinion on it.
56 |
57 | ## To activate it you need to add this in the [build.gradle](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/build.gradle#L26) file:
58 | ```groovy
59 | kapt {
60 | generateStubs = true
61 | }
62 | buildscript {
63 | ext.supportVersion = '25.0.0'
64 | ext.daggerVersion = '2.7'
65 | ext.retrofitVersion = '2.1.0'
66 | ext.rxVersion = '1.2.1'
67 | repositories {
68 | mavenCentral()
69 | jcenter()
70 | }
71 | dependencies {
72 | classpath "io.realm:realm-gradle-plugin:2.1.1"
73 | }
74 | }
75 |
76 |
77 | dependencies {
78 | compile fileTree(dir: 'libs', include: ['*.jar'])
79 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
80 | exclude group: 'com.android.support', module: 'support-annotations'
81 | })
82 | compile 'com.android.support:appcompat-v7:25.0.0'
83 | testCompile 'junit:junit:4.12'
84 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
85 | compile "com.android.support:cardview-v7:${supportVersion}"
86 | compile "com.android.support:design:${supportVersion}"
87 |
88 |
89 | // Dagger 2
90 | compile "com.google.dagger:dagger:${daggerVersion}"
91 | kapt "com.google.dagger:dagger-compiler:${daggerVersion}"
92 | provided "org.glassfish:javax.annotation:3.1.1"
93 |
94 |
95 | //Retrofit 2
96 | compile "com.squareup.retrofit2:retrofit:${retrofitVersion}"
97 | compile "com.squareup.retrofit2:adapter-rxjava:${retrofitVersion}"
98 | compile "com.squareup.retrofit2:converter-gson:${retrofitVersion}"
99 |
100 |
101 | compile 'com.google.code.gson:gson:2.8.0'
102 |
103 |
104 | compile "io.reactivex:rxjava:${rxVersion}"
105 | compile "io.reactivex:rxandroid:${rxVersion}"
106 |
107 |
108 | compile 'com.github.bumptech.glide:glide:3.7.0'
109 |
110 |
111 | }
112 | ```
113 |
114 | ## Then we create [inheriting class](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/shared/AppDelegate.kt#L10) from Application and configure Realm and dependencies graph for Dagger 2 developed by Google:
115 | ```kotlin
116 | class AppDelegate : Application() {
117 |
118 |
119 | var injector: AppInjector? = null
120 |
121 |
122 | @Singleton
123 | @Component(modules = arrayOf(NewsModule::class))
124 | interface AppInjector {
125 |
126 | fun inject(activity: MainActivity)
127 |
128 | }
129 |
130 |
131 | override fun onCreate() {
132 | super.onCreate()
133 | injector = DaggerAppDelegate_AppInjector.builder().build()
134 |
135 |
136 | Realm.init(this)
137 | val config = RealmConfiguration.Builder().build()
138 | Realm.setDefaultConfiguration(config)
139 | }
140 | }
141 | ```
142 | ## To work with HTTP we use Retrofit. We need to create an [interface](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/shared/NewsApiInterface.kt#L8) with one method that will receive news feed:
143 |
144 | ```kotlin
145 | interface NewsApiInterface {
146 | @GET("/feeds/newsdefaultfeeds.cms?feedtype=sjson")
147 | fun getNews(): Observable
148 | }
149 | ```
150 |
151 | ## We create [NewsModule class](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/shared/NewsModule.kt#L18) wherein the injected objectives will be created:
152 |
153 | ```kotlin
154 | @Module
155 | class NewsModule {
156 |
157 |
158 | @Provides
159 | @Singleton
160 | fun provideNewsPresenter(): NewsPresenter {
161 | return NewsPresenter()
162 | }
163 |
164 |
165 | @Provides
166 | @Singleton
167 | internal fun provideNewApiInterface(): NewsApiInterface {
168 | val retrofit = Retrofit.Builder()
169 | .baseUrl(Constants.NEWS_ENDPOINT)
170 | .addConverterFactory(GsonConverterFactory.create())
171 | .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
172 | .build()
173 | return retrofit.create(NewsApiInterface::class.java)
174 | }
175 | }
176 | ```
177 |
178 | ## In [NewsPresenter](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/presenter/NewsPresenter.kt#L16) class we will use RxAndroid and Realm for [news feed processing and caching](https://github.com/steelkiwi/Getting-started-with-Kotlin/blob/master/app/src/main/java/com/ktoi/toi/presenter/NewsPresenter.kt#L32) :
179 |
180 | ```kotlin
181 | subscription = mNewsApiInterface!!
182 | .getNews()
183 | .map { it.newsItem }
184 | .flatMap({ items ->
185 | Realm.getDefaultInstance().executeTransaction({ realm ->
186 | realm.delete(NewsItem::class.java)
187 | realm.insert(items)
188 | })
189 | Observable.just(items)
190 | })
191 | .onErrorResumeNext { throwable ->
192 | val realm = Realm.getDefaultInstance()
193 | val items = realm.where(NewsItem::class.java).findAll()
194 | Observable.just(realm.copyFromRealm(items))
195 | }
196 | .observeOn(AndroidSchedulers.mainThread())
197 | .doOnTerminate { mNewsView?.hideLoading() }
198 | .subscribe({ mNewsView?.onNewsItemLoaded(it) }, { mNewsView?.onError(it) })
199 | ```
200 |
201 | Projects source code is available on this Repository =)
202 |
203 | ## Let’s sum up
204 | All in all, Kotlin is a modern and secure programming language that simplifies Android apps development. It looks like a distinct alternative to Java as it has good documentation and it is simple enough for understanding. We hope that this article helped you to figure out how to create a project on Kotlin and integrate the needed libraries.
205 |
206 | ## License
207 |
208 | The MIT License (MIT)
209 |
210 | Copyright © 2017 SteelKiwi, http://steelkiwi.com
211 |
212 | Permission is hereby granted, free of charge, to any person obtaining a copy
213 | of this software and associated documentation files (the "Software"), to deal
214 | in the Software without restriction, including without limitation the rights
215 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
216 | copies of the Software, and to permit persons to whom the Software is
217 | furnished to do so, subject to the following conditions:
218 |
219 | The above copyright notice and this permission notice shall be included in
220 | all copies or substantial portions of the Software.
221 |
222 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
223 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
224 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
225 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
226 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
227 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
228 | THE SOFTWARE.
229 |
--------------------------------------------------------------------------------