Skip to content

Commit

Permalink
Create first domain providers and data classes
Browse files Browse the repository at this point in the history
  • Loading branch information
imablanco committed Feb 18, 2024
1 parent 71741c5 commit c46400c
Show file tree
Hide file tree
Showing 20 changed files with 308 additions and 26 deletions.
3 changes: 3 additions & 0 deletions applivery-android-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ android {

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")

val PUBLISH_VERSION: String by rootProject.extra
buildConfigField("String", "LibraryVersion", "\"$PUBLISH_VERSION\"")
}

buildTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner
import com.applivery.android.sdk.di.AppliveryDiContext
import com.applivery.android.sdk.di.Properties
import com.applivery.android.sdk.di.domainModules
import com.applivery.android.sdk.di.networkModules
import com.applivery.android.sdk.updates.IsUpToDateCallback
import com.applivery.android.sdk.updates.UpdatesLifecycleObserver
import org.koin.android.ext.koin.androidContext

Expand All @@ -23,7 +25,10 @@ internal class AppliverySdk : Applivery {
AppliveryDiContext.koinApp.apply {
androidContext(app)
// TODO: modules
modules(networkModules)
modules(
networkModules,
domainModules
)
// TODO: map properties from build config correctly
properties(
mapOf(
Expand All @@ -46,4 +51,8 @@ internal class AppliverySdk : Applivery {
override fun checkForUpdates() {
TODO("Not yet implemented")
}

override fun isUpToDate(callback: IsUpToDateCallback) {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.applivery.android.sdk.data.auth

import android.content.SharedPreferences
import androidx.core.content.edit
import arrow.core.Option
import com.applivery.android.sdk.domain.SharedPreferencesProvider

interface SessionManager {

val isLoggedIn: Boolean

fun saveToken(token: String)

fun getToken(): Option<String>

fun logOut()
}

class SessionManagerImpl(
private val preferencesProvider: SharedPreferencesProvider
) : SessionManager {

private val preferences: SharedPreferences get() = preferencesProvider.sharedPreferences

override val isLoggedIn: Boolean = getToken().isSome()

override fun saveToken(token: String) {
preferences.edit { putString(KeyToken, token) }
}

override fun getToken(): Option<String> {
return Option.fromNullable(preferences.getString(KeyToken, null))
}

override fun logOut() {
preferences.edit { remove(KeyToken) }
}

companion object {
private const val KeyToken = "token_key"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.applivery.android.sdk.network.base
package com.applivery.android.sdk.data.base

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.applivery.android.sdk.network.models
package com.applivery.android.sdk.data.models

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.applivery.android.sdk.network.models
package com.applivery.android.sdk.data.models

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.applivery.android.sdk.network.models
package com.applivery.android.sdk.data.models

import com.google.gson.annotations.SerializedName

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.applivery.android.sdk.network.service
package com.applivery.android.sdk.data.service

import com.applivery.android.sdk.network.models.AppConfigApi
import com.applivery.android.sdk.network.models.ServerResponseSchema
import arrow.core.Either
import com.applivery.android.sdk.data.models.ApiError
import com.applivery.android.sdk.data.models.AppConfigApi
import com.applivery.android.sdk.data.models.ServerResponseSchema
import retrofit2.http.GET

interface AppliveryApiService {

@GET("v1/app")
suspend fun getConfig(): ServerResponseSchema<AppConfigApi>
suspend fun getConfig(): Either<ApiError, ServerResponseSchema<AppConfigApi>>
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.applivery.android.sdk.network.service
package com.applivery.android.sdk.data.service

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.applivery.android.sdk.network.base.JsonMapper
import com.applivery.android.sdk.network.models.ApiError
import com.applivery.android.sdk.network.models.ApiErrorSchema
import com.applivery.android.sdk.network.models.IOError
import com.applivery.android.sdk.network.models.InternalError
import com.applivery.android.sdk.data.base.JsonMapper
import com.applivery.android.sdk.data.models.ApiError
import com.applivery.android.sdk.data.models.ApiErrorSchema
import com.applivery.android.sdk.data.models.IOError
import com.applivery.android.sdk.data.models.InternalError
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
Expand Down Expand Up @@ -83,8 +83,9 @@ class EitherCallSuspendAdapter<T : Any>(
private val jsonMapper: JsonMapper
) : CallAdapter<T, Call<Either<ApiError, T>>> {

override fun adapt(call: Call<T>): Call<Either<ApiError, T>> =
EitherCallMapper(call, jsonMapper)
override fun adapt(call: Call<T>): Call<Either<ApiError, T>> {
return EitherCallMapper(call, jsonMapper)
}

override fun responseType(): Type = returnType
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.applivery.android.sdk.data.service

import android.os.Build
import com.applivery.android.sdk.BuildConfig
import com.applivery.android.sdk.domain.HostAppPackageInfoProvider
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.util.Locale

class HeadersInterceptor(
private val packageInfoProvider: HostAppPackageInfoProvider
) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(composeRequest(chain))
}

private fun composeRequest(chain: Interceptor.Chain): Request {
val original = chain.request()
val packageInfo = packageInfoProvider.packageInfo
return original.newBuilder()
.addHeader("Accept-Language", Locale.getDefault().language)
.addHeader("x-sdk-version", "ANDROID_" + BuildConfig.LibraryVersion)
.addHeader("x-app-version", packageInfo.versionName)
.addHeader("x-os-version", Build.VERSION.RELEASE)
.addHeader("x-os-name", "android")
.addHeader("x-device-vendor", Build.MANUFACTURER)
.addHeader("x-device-model", Build.MODEL)
.addHeader("x-package-name", packageInfo.packageName)
.addHeader("x-package-version", packageInfo.versionCode.toString())
.addHeader("x-os-minsdkversion", packageInfo.minSdkVersion.toString())
.addHeader("x-os-targetsdkversion", packageInfo.targetSdkVersion.toString())
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.applivery.android.sdk.data.service

import com.applivery.android.sdk.data.auth.SessionManager
import com.applivery.android.sdk.domain.InstallationIdProvider
import okhttp3.Interceptor
import okhttp3.Interceptor.Chain
import okhttp3.Response

class SessionInterceptor(
private val sessionManager: SessionManager,
private val idProvider: InstallationIdProvider,
private val appToken: String
) : Interceptor {

override fun intercept(chain: Chain): Response {
val requestBuilder = chain.request()
.newBuilder()
.addHeader("Authorization", "Bearer $appToken")
.addHeader("x-installation-token", idProvider.installationId)

sessionManager.getToken().onSome {
requestBuilder.addHeader("x-sdk-auth-token", it)
}
val response = chain.proceed(requestBuilder.build())
if (response.code == CodeUnauthorized) {
sessionManager.logOut()
}
return response
}

companion object {
private const val CodeUnauthorized = 401
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package com.applivery.android.sdk.di

import com.applivery.android.sdk.network.base.JsonMapper
import com.applivery.android.sdk.network.service.AppliveryApiService
import com.applivery.android.sdk.network.service.EitherCallAdapterFactory
import com.applivery.android.sdk.data.auth.SessionManager
import com.applivery.android.sdk.data.auth.SessionManagerImpl
import com.applivery.android.sdk.data.base.JsonMapper
import com.applivery.android.sdk.data.service.AppliveryApiService
import com.applivery.android.sdk.data.service.EitherCallAdapterFactory
import com.applivery.android.sdk.data.service.HeadersInterceptor
import com.applivery.android.sdk.data.service.SessionInterceptor
import com.applivery.android.sdk.domain.AndroidHostAppPackageInfoProvider
import com.applivery.android.sdk.domain.AndroidSharedPreferencesProvider
import com.applivery.android.sdk.domain.HostAppPackageInfoProvider
import com.applivery.android.sdk.domain.InstallationIdProvider
import com.applivery.android.sdk.domain.InstallationIdProviderImpl
import com.applivery.android.sdk.domain.SharedPreferencesProvider
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import org.koin.core.module.dsl.factoryOf
import org.koin.dsl.bind
import org.koin.dsl.module
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create


internal val networkModules = module {

// TODO: add interceptors
factory { OkHttpClient.Builder() }
factory { GsonBuilder().create() }
factory { JsonMapper(gson = get()) }
factory {
Retrofit.Builder()
.baseUrl(getProperty<String>(Properties.ApiUrl))
Expand All @@ -26,5 +33,26 @@ internal val networkModules = module {
.build()
}

factory {
OkHttpClient.Builder().apply {
addInterceptor(HeadersInterceptor(packageInfoProvider = get()))
addInterceptor(
SessionInterceptor(
sessionManager = get(),
idProvider = get(),
appToken = getProperty(Properties.AppToken)
)
)
}
}
factory { get<Retrofit>().create<AppliveryApiService>() }
}
factory { GsonBuilder().create() }
factoryOf(::JsonMapper)
factoryOf(::SessionManagerImpl).bind<SessionManager>()
}

internal val domainModules = module {
factoryOf(::AndroidHostAppPackageInfoProvider).bind<HostAppPackageInfoProvider>()
factoryOf(::AndroidSharedPreferencesProvider).bind<SharedPreferencesProvider>()
factoryOf(::InstallationIdProviderImpl).bind<InstallationIdProvider>()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.applivery.android.sdk.domain

import android.content.Context
import android.os.Build
import com.applivery.android.sdk.domain.model.PackageInfo
import android.content.pm.PackageInfo as AndroidPackageInfo

interface HostAppPackageInfoProvider {

val packageInfo: PackageInfo
}

class AndroidHostAppPackageInfoProvider(
private val context: Context
) : HostAppPackageInfoProvider {

override val packageInfo: PackageInfo
get() {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val appInfo = context.packageManager.getApplicationInfo(context.packageName, 0)
return PackageInfo(
packageName = pInfo.packageName,
versionCode = pInfo.versionCodeCompat,
versionName = pInfo.versionName,
minSdkVersion = appInfo.minSdkVersion,
targetSdkVersion = appInfo.targetSdkVersion
)
}

private val AndroidPackageInfo.versionCodeCompat: Long
get() {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
longVersionCode
} else {
versionCode.toLong()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.applivery.android.sdk.domain

import androidx.core.content.edit
import java.util.UUID

interface InstallationIdProvider {

val installationId: String
}

class InstallationIdProviderImpl(
private val sharedPreferencesProvider: SharedPreferencesProvider
) : InstallationIdProvider {

private val preferences get() = sharedPreferencesProvider.sharedPreferences

override val installationId: String
get() {
return preferences.getString(KeyInstallationId, null).orEmpty().ifEmpty {
UUID.randomUUID().toString().also {
preferences.edit { putString(KeyInstallationId, it) }
}
}
}

companion object {
private const val KeyInstallationId = "applivery_id_token_key"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.applivery.android.sdk.domain

import android.content.Context
import android.content.SharedPreferences

interface SharedPreferencesProvider {

val sharedPreferences: SharedPreferences
}

class AndroidSharedPreferencesProvider(private val context: Context) : SharedPreferencesProvider {

override val sharedPreferences: SharedPreferences
get() = context.getSharedPreferences(PreferencesName, Context.MODE_PRIVATE)

companion object {
private const val PreferencesName = "applivery-data"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.applivery.android.sdk.domain.model

abstract class Error : Throwable()
Loading

0 comments on commit c46400c

Please sign in to comment.