Skip to content
This repository has been archived by the owner on Jun 28, 2019. It is now read-only.

Commit

Permalink
Merge pull request #10 from studyplus/feature/kotlin_coroutines
Browse files Browse the repository at this point in the history
Kotlin Coroutinesの導入
  • Loading branch information
koji-1009 authored Oct 31, 2018
2 parents 9fb4a44 + d53d634 commit f688448
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 87 deletions.
11 changes: 3 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.2.61'
ext.kotlin_version = '1.3.0'
ext.versions = [
'compileSdk': 27,
'minSdk': 16,
'targetSdk': 27,
'supportLibrary': '27.1.1',
'constraintLayout': '1.1.3',
'gson': '2.8.5',
'okhttp': '3.10.0',
'retrofit' : '2.4.0',
'rx2java' : '2.2.0',
'rx2android' : '2.1.0'
'constraintLayout': '1.1.3'
]
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Sun Jul 01 16:03:41 JST 2018
#Tue Oct 30 15:28:34 JST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
28 changes: 13 additions & 15 deletions studyplus-android-sdk2/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ android {
version = versionName
group = "jp.studyplus.android.sdk"

consumerProguardFiles 'lib-proguard-rules.txt'

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

Expand All @@ -44,9 +46,6 @@ android {
initWith(buildTypes.debug)
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

buildConfigField "String", "API_ENDPOINT", quote("https://external-api.studyplus.jp")
}
}
Expand All @@ -65,23 +64,22 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0'

implementation "com.google.code.gson:gson:${versions.gson}"

implementation "com.squareup.okhttp3:okhttp:${versions.okhttp}"
implementation "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}"
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.okhttp3:okhttp:3.11.0'

implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}"
implementation "com.squareup.retrofit2:adapter-rxjava2:${versions.retrofit}"
implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
testImplementation "com.squareup.retrofit2:retrofit-mock:${versions.retrofit}"
def retrofit = "2.4.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
testImplementation "com.squareup.retrofit2:retrofit-mock:$retrofit"

implementation "io.reactivex.rxjava2:rxjava:${versions.rx2java}"
implementation "io.reactivex.rxjava2:rxandroid:${versions.rx2android}"
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

testImplementation 'junit:junit:4.12'

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}

apply from: 'build.publish.gradle'
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,14 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile

-keep public class jp.studyplus.android.sdk.* { public *; }
-keep public class jp.studyplus.android.sdk.record.** { public *; }
# ServiceLoader support
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# Most of volatile fields are updated with AFU and should not be mangled
-keepclassmembernames class kotlinx.** {
volatile <fields>;
}

# Network Rsponse
-keepnames class jp.studyplus.android.sdk.internal.api.response.** { *; }
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.util.Log
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import jp.studyplus.android.sdk.internal.api.ApiClient
import jp.studyplus.android.sdk.internal.api.CertificationStore
import jp.studyplus.android.sdk.internal.auth.AuthTransit
import jp.studyplus.android.sdk.record.StudyRecord
import kotlinx.coroutines.runBlocking

class Studyplus private constructor() {

Expand Down Expand Up @@ -72,13 +71,15 @@ class Studyplus private constructor() {
throw IllegalStateException("Please check your application's authentication before this method call.")
}

ApiClient.apiClient.postStudyRecords(context, studyRecord)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ listener?.onResult(success = true, recordId = it.recordId) },
{ listener?.onResult(success = false, throwable = it) }
)
runBlocking {
try {
val deferred = ApiClient.postStudyRecords(context, studyRecord)
val result = deferred.await()
listener?.onResult(success = true, recordId = result.recordId)
} catch (t: Throwable) {
listener?.onResult(success = false, throwable = t)
}
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
package jp.studyplus.android.sdk.internal.api

import android.content.Context
import io.reactivex.Observable
import jp.studyplus.android.sdk.internal.api.response.PostStudyRecordsResponse
import jp.studyplus.android.sdk.record.StudyRecord
import retrofit2.Retrofit
import kotlinx.coroutines.Deferred

internal class ApiClient
constructor(retrofit: Retrofit) {
internal object ApiClient {
private val apiService by lazy { ApiManager.retrofit.create(ApiService::class.java) }

companion object {
val apiClient by lazy { ApiClient(ApiManager.retrofit) }
lateinit var apiService: ApiService

private fun getOAuthAccessToken(context: Context): Observable<String> {
return Observable.just(CertificationStore.create(context))
.map { it.apiCertification() }
.map { "OAuth $it" }
}
}

init {
apiService = retrofit.create(ApiService::class.java)
}

fun postStudyRecords(context: Context, studyRecord: StudyRecord): Observable<PostStudyRecordsResponse> {
return getOAuthAccessToken(context).flatMap { apiService.postStudyRecords(it, studyRecord) }
fun postStudyRecords(context: Context, studyRecord: StudyRecord): Deferred<PostStudyRecordsResponse> {
return apiService.postStudyRecords(CertificationStore.create(context).getOAuthAccessToken(), studyRecord)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package jp.studyplus.android.sdk.internal.api

import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import jp.studyplus.android.sdk.BuildConfig
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

internal object ApiManager {

private val client by lazy {
private val client: OkHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build()
}

val retrofit by lazy {
val retrofit: Retrofit by lazy {
Retrofit.Builder()
.client(client)
.baseUrl(BuildConfig.API_ENDPOINT)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.build()!!
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package jp.studyplus.android.sdk.internal.api

import io.reactivex.Observable
import jp.studyplus.android.sdk.internal.api.response.PostStudyRecordsResponse
import jp.studyplus.android.sdk.record.StudyRecord
import retrofit2.http.*
import kotlinx.coroutines.Deferred
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.Headers
import retrofit2.http.POST

internal interface ApiService {
@Headers(value = [
Expand All @@ -13,6 +16,6 @@ internal interface ApiService {
@POST("/v1/study_records")
fun postStudyRecords(
@Header("Authorization") oauth: String,
@Body studyRecord: StudyRecord)
: Observable<PostStudyRecordsResponse>
@Body studyRecord: StudyRecord
): Deferred<PostStudyRecordsResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ private constructor(private val preferences: SharedPreferences) {
return !token.isNullOrEmpty()
}

fun apiCertification(): String = preferences.getString(KEY_ACCESS_TOKEN, "")
fun getOAuthAccessToken(): String {
val certification = preferences.getString(KEY_ACCESS_TOKEN, "")
return "OAuth $certification"
}

fun update(data: Intent) {
val code = data.getStringExtra(EXTRA_SP_AUTH_RESULT_CODE).orEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@ package jp.studyplus.android.sdk

import jp.studyplus.android.sdk.internal.api.MockApiClient
import jp.studyplus.android.sdk.record.StudyRecordBuilder
import kotlinx.coroutines.runBlocking
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull

class ApiUnitTest {

@Test
fun mockApi() {
val record = StudyRecordBuilder().build()
MockApiClient.apiClient.postStudyRecords(null, record)
.test()
.assertNoErrors()
.assertValue {
it.recordId?.let {
it == 9999L
} ?: run {
false
}
}
runBlocking {
try {
val deferred = MockApiClient.apiClient.postStudyRecords(null, record)
val result = deferred.await()

assertEquals(result.recordId, 9999L)
} catch (t: Throwable) {
assertNull(t)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package jp.studyplus.android.sdk.internal.api

import android.content.Context
import io.reactivex.Observable
import jp.studyplus.android.sdk.internal.api.response.PostStudyRecordsResponse
import jp.studyplus.android.sdk.record.StudyRecord
import retrofit2.mock.MockRetrofit

internal class MockApiClient
constructor(retrofit: MockRetrofit) {
internal class MockApiClient constructor(retrofit: MockRetrofit) {

companion object {
val apiClient by lazy { MockApiClient(MockApiManager.retrofit) }
Expand All @@ -19,7 +16,6 @@ constructor(retrofit: MockRetrofit) {
apiService = MockApiService(delegate)
}

fun postStudyRecords(context: Context?, studyRecord: StudyRecord): Observable<PostStudyRecordsResponse> {
return apiService.postStudyRecords("", studyRecord)
}
fun postStudyRecords(context: Context?, studyRecord: StudyRecord) =
apiService.postStudyRecords("", studyRecord)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package jp.studyplus.android.sdk.internal.api

import io.reactivex.Observable
import jp.studyplus.android.sdk.internal.api.response.PostStudyRecordsResponse
import jp.studyplus.android.sdk.record.StudyRecord
import kotlinx.coroutines.Deferred
import retrofit2.mock.BehaviorDelegate

internal class MockApiService(private val delegate: BehaviorDelegate<ApiService>) : ApiService {
override fun postStudyRecords(oauth: String, studyRecord: StudyRecord): Observable<PostStudyRecordsResponse> {
override fun postStudyRecords(oauth: String, studyRecord: StudyRecord): Deferred<PostStudyRecordsResponse> {
return delegate.returningResponse(PostStudyRecordsResponse(9999)).postStudyRecords(oauth, studyRecord)
}
}

0 comments on commit f688448

Please sign in to comment.