Add CaldavClientProvider with support for tokens

pull/1208/head
Alex Baker 5 years ago
parent 50c62a4114
commit 75d130556c

@ -211,6 +211,7 @@ dependencies {
googleplayImplementation("com.google.android.gms:play-services-maps:17.0.0") googleplayImplementation("com.google.android.gms:play-services-maps:17.0.0")
googleplayImplementation("com.google.android.libraries.places:places:2.4.0") googleplayImplementation("com.google.android.libraries.places:places:2.4.0")
googleplayImplementation("com.android.billingclient:billing:1.2.2") googleplayImplementation("com.android.billingclient:billing:1.2.2")
googleplayImplementation("com.google.android.gms:play-services-auth:18.1.0")
androidTestImplementation("com.google.dagger:hilt-android-testing:${Versions.hilt}") androidTestImplementation("com.google.dagger:hilt-android-testing:${Versions.hilt}")
kaptAndroidTest("com.google.dagger:hilt-compiler:${Versions.hilt}") kaptAndroidTest("com.google.dagger:hilt-compiler:${Versions.hilt}")

@ -12,8 +12,8 @@ import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
class DebugNetworkInterceptor @Inject constructor(@param:ApplicationContext private val context: Context) { class DebugNetworkInterceptor @Inject constructor(@param:ApplicationContext private val context: Context) {
fun add(builder: OkHttpClient.Builder) { fun apply(builder: OkHttpClient.Builder?) {
builder.addNetworkInterceptor(FlipperOkhttpInterceptor(getNetworkPlugin(context))) builder?.addNetworkInterceptor(FlipperOkhttpInterceptor(getNetworkPlugin(context)))
} }
@Throws(IOException::class) @Throws(IOException::class)

@ -2,8 +2,7 @@ package org.tasks.billing
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
class Purchase(json: String?) { class Purchase(json: String?) {
val sku: String? val sku: String = ""
get() = null
fun toJson(): String? { fun toJson(): String? {
return null return null
@ -20,4 +19,6 @@ class Purchase(json: String?) {
val isProSubscription: Boolean val isProSubscription: Boolean
get() = false get() = false
val purchaseToken = ""
} }

@ -1,13 +1,17 @@
package org.tasks.gtasks package org.tasks.gtasks
import android.app.Activity import android.app.Activity
import android.content.Intent
import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.activity.MainActivity
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.disposables.Disposables import io.reactivex.disposables.Disposables
import org.tasks.auth.OauthSignIn
import javax.inject.Inject import javax.inject.Inject
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
class PlayServices @Inject constructor() { class PlayServices @Inject constructor() {
val signInIntent: Intent? = null
val isPlayServicesAvailable: Boolean val isPlayServicesAvailable: Boolean
get() = false get() = false
@ -23,4 +27,8 @@ class PlayServices @Inject constructor() {
fun check(mainActivity: MainActivity?): Disposable { fun check(mainActivity: MainActivity?): Disposable {
return Disposables.empty() return Disposables.empty()
} }
fun getSignedInAccount(): OauthSignIn? = null
fun signInFromIntent(data: Intent?): OauthSignIn? = null
} }

@ -0,0 +1,16 @@
package org.tasks.auth
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
class GoogleSignInAccount(
private val account: GoogleSignInAccount
) : OauthSignIn {
override val id: String?
get() = account.id
override val idToken: String?
get() = account.idToken
override val email: String?
get() = account.email
}

@ -2,11 +2,21 @@ package org.tasks.gtasks
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.widget.Toast import android.widget.Toast
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.Tasks
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.R import org.tasks.R
import org.tasks.auth.GoogleSignInAccount
import org.tasks.auth.OauthSignIn
import org.tasks.data.LocationDao import org.tasks.data.LocationDao
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import timber.log.Timber import timber.log.Timber
@ -53,6 +63,40 @@ class PlayServices @Inject constructor(
} }
} }
suspend fun getSignedInAccount(): OauthSignIn? {
return withContext(Dispatchers.IO) {
try {
Tasks
.await(client.silentSignIn())
?.let { GoogleSignInAccount(it) }
} catch (e: Exception) {
Timber.e(e)
null
}
}
}
fun signInFromIntent(data: Intent?): OauthSignIn? = try {
GoogleSignIn
.getSignedInAccountFromIntent(data)
.getResult(ApiException::class.java)
?.let { GoogleSignInAccount(it) }
} catch (e: ApiException) {
Timber.e(e)
null
}
val signInIntent: Intent
get() = client.signInIntent
private val client: GoogleSignInClient
get() = GoogleSignIn.getClient(
context,
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(context.getString(R.string.google_sign_in))
.build())
private val status: String private val status: String
get() = GoogleApiAvailability.getInstance().getErrorString(result) get() = GoogleApiAvailability.getInstance().getErrorString(result)

@ -0,0 +1,7 @@
package org.tasks.auth
interface OauthSignIn {
val idToken: String?
val email: String?
val id: String?
}

@ -4,8 +4,13 @@ import androidx.hilt.lifecycle.ViewModelInject
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class AddCaldavAccountViewModel @ViewModelInject constructor( class AddCaldavAccountViewModel @ViewModelInject constructor(
private val client: CaldavClient) : CompletableViewModel<String>() { private val provider: CaldavClientProvider
) : CompletableViewModel<String>() {
suspend fun addAccount(url: String, username: String, password: String) { suspend fun addAccount(url: String, username: String, password: String) {
run { client.setForeground().forUrl(url, username, password).homeSet() } run {
provider
.forUrl(url, username, password)
.setForeground()
.homeSet(username, password) }
} }
} }

@ -1,9 +1,7 @@
package org.tasks.caldav package org.tasks.caldav
import android.content.Context
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import at.bitfire.cert4android.CustomCertManager import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4jvm.BasicDigestAuthHandler
import at.bitfire.dav4jvm.DavResource import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response
import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.Response.HrefRelation
@ -16,20 +14,13 @@ import at.bitfire.dav4jvm.exception.HttpException
import at.bitfire.dav4jvm.property.* import at.bitfire.dav4jvm.property.*
import at.bitfire.dav4jvm.property.ResourceType.Companion.CALENDAR import at.bitfire.dav4jvm.property.ResourceType.Companion.CALENDAR
import com.todoroo.astrid.helper.UUIDHelper import com.todoroo.astrid.helper.UUIDHelper
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor
import org.tasks.R import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.CaldavAccount import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption
import org.tasks.ui.DisplayableException import org.tasks.ui.DisplayableException
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory import org.xmlpull.v1.XmlPullParserFactory
@ -40,95 +31,31 @@ import java.io.StringWriter
import java.security.KeyManagementException import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.net.ssl.SSLContext
class CaldavClient { class CaldavClient(
private val encryption: KeyStoreEncryption private val provider: CaldavClientProvider,
private val preferences: Preferences private val customCertManager: CustomCertManager,
private val interceptor: DebugNetworkInterceptor val httpClient: OkHttpClient,
val httpClient: OkHttpClient?
private val httpUrl: HttpUrl? private val httpUrl: HttpUrl?
private val context: Context ) {
private val basicDigestAuthHandler: BasicDigestAuthHandler? @Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
private var foreground = false suspend fun forAccount(account: CaldavAccount) =
provider.forAccount(account)
@Inject
internal constructor(
@ApplicationContext context: Context,
encryption: KeyStoreEncryption,
preferences: Preferences,
interceptor: DebugNetworkInterceptor) {
this.context = context
this.encryption = encryption
this.preferences = preferences
this.interceptor = interceptor
httpClient = null
httpUrl = null
basicDigestAuthHandler = null
}
private constructor( @Throws(KeyManagementException::class, NoSuchAlgorithmException::class)
context: Context, suspend fun forUrl(
encryption: KeyStoreEncryption,
preferences: Preferences,
interceptor: DebugNetworkInterceptor,
url: String?, url: String?,
username: String, username: String,
password: String, password: String,
foreground: Boolean) { token: String? = null
this.context = context ): CaldavClient = provider.forUrl(url, username, password, token)
this.encryption = encryption
this.preferences = preferences
this.interceptor = interceptor
val customCertManager = CustomCertManager(context)
customCertManager.appInForeground = foreground
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(customCertManager), null)
basicDigestAuthHandler = BasicDigestAuthHandler(null, username, password)
val builder = OkHttpClient()
.newBuilder()
.addNetworkInterceptor(basicDigestAuthHandler)
.authenticator(basicDigestAuthHandler)
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
if (preferences.isFlipperEnabled) {
interceptor.add(builder)
}
httpClient = builder.build()
httpUrl = url?.toHttpUrlOrNull()
}
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun forAccount(account: CaldavAccount): CaldavClient {
return forUrl(account.url, account.username!!, account.getPassword(encryption))
}
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun forCalendar(account: CaldavAccount, calendar: CaldavCalendar): CaldavClient {
return forUrl(calendar.url, account.username!!, account.getPassword(encryption))
}
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class)
suspend fun forUrl(url: String?, username: String, password: String): CaldavClient = withContext(Dispatchers.IO) {
CaldavClient(
context, encryption, preferences, interceptor, url, username, password, foreground)
}
@WorkerThread @WorkerThread
@Throws(DavException::class, IOException::class) @Throws(DavException::class, IOException::class)
private fun tryFindPrincipal(link: String): String? { private fun tryFindPrincipal(link: String): String? {
val url = httpUrl!!.resolve(link) val url = httpUrl!!.resolve(link)
Timber.d("Checking for principal: %s", url) Timber.d("Checking for principal: %s", url)
val davResource = DavResource(httpClient!!, url!!) val davResource = DavResource(httpClient, url!!)
val responses = ArrayList<Response>() val responses = ArrayList<Response>()
davResource.propfind(0, CurrentUserPrincipal.NAME) { response, _ -> davResource.propfind(0, CurrentUserPrincipal.NAME) { response, _ ->
responses.add(response) responses.add(response)
@ -149,7 +76,7 @@ class CaldavClient {
@WorkerThread @WorkerThread
@Throws(DavException::class, IOException::class) @Throws(DavException::class, IOException::class)
private fun findHomeset(): String { private fun findHomeset(): String {
val davResource = DavResource(httpClient!!, httpUrl!!) val davResource = DavResource(httpClient, httpUrl!!)
val responses = ArrayList<Response>() val responses = ArrayList<Response>()
davResource.propfind(0, CalendarHomeSet.NAME) { response, _ -> davResource.propfind(0, CalendarHomeSet.NAME) { response, _ ->
responses.add(response) responses.add(response)
@ -169,7 +96,11 @@ class CaldavClient {
} }
@Throws(IOException::class, DavException::class, NoSuchAlgorithmException::class, KeyManagementException::class) @Throws(IOException::class, DavException::class, NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun homeSet(): String = withContext(Dispatchers.IO) { suspend fun homeSet(
username: String? = null,
password: String? = null,
token: String? = null
): String = withContext(Dispatchers.IO) {
var principal: String? = null var principal: String? = null
try { try {
principal = tryFindPrincipal("/.well-known/caldav") principal = tryFindPrincipal("/.well-known/caldav")
@ -182,16 +113,17 @@ class CaldavClient {
if (principal == null) { if (principal == null) {
principal = tryFindPrincipal("") principal = tryFindPrincipal("")
} }
forUrl( provider.forUrl(
(if (isNullOrEmpty(principal)) httpUrl else httpUrl!!.resolve(principal!!)).toString(), (if (isNullOrEmpty(principal)) httpUrl else httpUrl!!.resolve(principal!!)).toString(),
basicDigestAuthHandler!!.username, username,
basicDigestAuthHandler.password) password,
token)
.findHomeset() .findHomeset()
} }
@Throws(IOException::class, DavException::class) @Throws(IOException::class, DavException::class)
suspend fun calendars(): List<Response> = withContext(Dispatchers.IO) { suspend fun calendars(): List<Response> = withContext(Dispatchers.IO) {
val davResource = DavResource(httpClient!!, httpUrl!!) val davResource = DavResource(httpClient, httpUrl!!)
val responses = ArrayList<Response>() val responses = ArrayList<Response>()
davResource.propfind( davResource.propfind(
1, 1,
@ -226,12 +158,12 @@ class CaldavClient {
@Throws(IOException::class, HttpException::class) @Throws(IOException::class, HttpException::class)
suspend fun deleteCollection() = withContext(Dispatchers.IO) { suspend fun deleteCollection() = withContext(Dispatchers.IO) {
DavResource(httpClient!!, httpUrl!!).delete(null) {} DavResource(httpClient, httpUrl!!).delete(null) {}
} }
@Throws(IOException::class, XmlPullParserException::class, HttpException::class) @Throws(IOException::class, XmlPullParserException::class, HttpException::class)
suspend fun makeCollection(displayName: String, color: Int): String = withContext(Dispatchers.IO) { suspend fun makeCollection(displayName: String, color: Int): String = withContext(Dispatchers.IO) {
val davResource = DavResource(httpClient!!, httpUrl!!.resolve(UUIDHelper.newUUID() + "/")!!) val davResource = DavResource(httpClient, httpUrl!!.resolve(UUIDHelper.newUUID() + "/")!!)
val mkcolString = getMkcolString(displayName, color) val mkcolString = getMkcolString(displayName, color)
davResource.mkCol(mkcolString) {} davResource.mkCol(mkcolString) {}
davResource.location.toString() davResource.location.toString()
@ -239,7 +171,7 @@ class CaldavClient {
@Throws(IOException::class, XmlPullParserException::class, HttpException::class) @Throws(IOException::class, XmlPullParserException::class, HttpException::class)
suspend fun updateCollection(displayName: String, color: Int): String = withContext(Dispatchers.IO) { suspend fun updateCollection(displayName: String, color: Int): String = withContext(Dispatchers.IO) {
val davResource = PatchableDavResource(httpClient!!, httpUrl!!) val davResource = PatchableDavResource(httpClient, httpUrl!!)
davResource.propPatch(getPropPatchString(displayName, color)) {} davResource.propPatch(getPropPatchString(displayName, color)) {}
davResource.location.toString() davResource.location.toString()
} }
@ -328,7 +260,7 @@ class CaldavClient {
} }
fun setForeground(): CaldavClient { fun setForeground(): CaldavClient {
foreground = true customCertManager.appInForeground = true
return this return this
} }
} }

@ -0,0 +1,107 @@
package org.tasks.caldav
import android.content.Context
import at.bitfire.cert4android.CustomCertManager
import at.bitfire.dav4jvm.BasicDigestAuthHandler
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Authenticator
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor
import org.tasks.billing.Inventory
import org.tasks.data.CaldavAccount
import org.tasks.gtasks.PlayServices
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.net.ssl.SSLContext
class CaldavClientProvider @Inject constructor(
@ApplicationContext private val context: Context,
private val encryption: KeyStoreEncryption,
private val preferences: Preferences,
private val interceptor: DebugNetworkInterceptor,
private val playServices: PlayServices,
private val inventory: Inventory
) {
suspend fun forUrl(
url: String?,
username: String? = null,
password: String? = null,
token: String? = null): CaldavClient {
val auth = getAuthInterceptor(username = username, password = password, token = token)
val customCertManager = newCertManager()
return CaldavClient(
this,
customCertManager,
createHttpClient(auth, customCertManager),
url?.toHttpUrlOrNull()
)
}
suspend fun forAccount(account: CaldavAccount, url: String? = account.url): CaldavClient {
val auth = getAuthInterceptor(account)
val customCertManager = newCertManager()
return CaldavClient(
this,
customCertManager,
createHttpClient(auth, customCertManager),
url?.toHttpUrlOrNull()
)
}
private suspend fun newCertManager() = withContext(Dispatchers.Default) {
CustomCertManager(context)
}
private suspend fun getAuthInterceptor(
account: CaldavAccount? = null,
username: String? = account?.username,
password: String? = account?.getPassword(encryption),
token: String? = null
): Interceptor? {
return when {
account?.isTasksOrg == true -> playServices.getSignedInAccount()?.let {
TokenInterceptor(it.idToken!!, inventory)
}
username?.isNotBlank() == true && password?.isNotBlank() == true ->
BasicDigestAuthHandler(null, username, password)
token?.isNotBlank() == true ->
TokenInterceptor(token, inventory)
else -> null
}
}
private fun createHttpClient(auth: Interceptor?, customCertManager: CustomCertManager, foreground: Boolean = false): OkHttpClient {
customCertManager.appInForeground = foreground
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(customCertManager), null)
val builder = OkHttpClient()
.newBuilder()
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
auth?.let {
builder.addNetworkInterceptor(it)
if (it is Authenticator) {
builder.authenticator(it)
}
}
if (preferences.isFlipperEnabled) {
interceptor.apply(builder)
}
return builder.build()
}
}

@ -54,7 +54,7 @@ class CaldavSynchronizer @Inject constructor(
private val taskDeleter: TaskDeleter, private val taskDeleter: TaskDeleter,
private val inventory: Inventory, private val inventory: Inventory,
private val firebase: Firebase, private val firebase: Firebase,
private val client: CaldavClient, private val provider: CaldavClientProvider,
private val iCal: iCalendar) { private val iCal: iCalendar) {
companion object { companion object {
init { init {
@ -105,7 +105,7 @@ class CaldavSynchronizer @Inject constructor(
@Throws(IOException::class, DavException::class, KeyManagementException::class, NoSuchAlgorithmException::class) @Throws(IOException::class, DavException::class, KeyManagementException::class, NoSuchAlgorithmException::class)
private suspend fun synchronize(account: CaldavAccount) { private suspend fun synchronize(account: CaldavAccount) {
val caldavClient = client.forAccount(account) val caldavClient = provider.forAccount(account)
val resources = caldavClient.calendars() val resources = caldavClient.calendars()
val urls = resources.map { it.href.toString() }.toHashSet() val urls = resources.map { it.href.toString() }.toHashSet()
Timber.d("Found calendars: %s", urls) Timber.d("Found calendars: %s", urls)
@ -132,7 +132,7 @@ class CaldavSynchronizer @Inject constructor(
caldavDao.update(calendar) caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList() localBroadcastManager.broadcastRefreshList()
} }
sync(calendar, resource, caldavClient.httpClient!!) sync(calendar, resource, caldavClient.httpClient)
} }
setError(account, "") setError(account, "")
} }

@ -5,8 +5,9 @@ import org.tasks.data.CaldavAccount
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class CreateCalendarViewModel @ViewModelInject constructor( class CreateCalendarViewModel @ViewModelInject constructor(
private val client: CaldavClient): CompletableViewModel<String?>() { private val provider: CaldavClientProvider
): CompletableViewModel<String?>() {
suspend fun createCalendar(account: CaldavAccount, name: String, color: Int) { suspend fun createCalendar(account: CaldavAccount, name: String, color: Int) {
run { client.forAccount(account).makeCollection(name, color) } run { provider.forAccount(account).makeCollection(name, color) }
} }
} }

@ -6,8 +6,11 @@ import org.tasks.data.CaldavCalendar
import org.tasks.ui.ActionViewModel import org.tasks.ui.ActionViewModel
class DeleteCalendarViewModel @ViewModelInject constructor( class DeleteCalendarViewModel @ViewModelInject constructor(
private val client: CaldavClient) : ActionViewModel() { private val provider: CaldavClientProvider
) : ActionViewModel() {
suspend fun deleteCalendar(account: CaldavAccount, calendar: CaldavCalendar) { suspend fun deleteCalendar(account: CaldavAccount, calendar: CaldavCalendar) {
run { client.forCalendar(account, calendar).deleteCollection() } run {
calendar.url?.let { provider.forAccount(account, it).deleteCollection() }
}
} }
} }

@ -0,0 +1,25 @@
package org.tasks.caldav
import okhttp3.Interceptor
import okhttp3.Response
import org.tasks.billing.Inventory
class TokenInterceptor(
private val token: String,
private val inventory: Inventory
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder().header(AUTHORIZATION, "Bearer $token")
inventory.subscription?.let {
builder.header(SKU, it.sku)
builder.header(TOKEN, it.purchaseToken)
}
return chain.proceed(builder.build())
}
companion object {
private const val AUTHORIZATION = "Authorization"
private const val SKU = "tasks-sku"
private const val TOKEN = "tasks-token"
}
}

@ -4,8 +4,9 @@ import androidx.hilt.lifecycle.ViewModelInject
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class UpdateCaldavAccountViewModel @ViewModelInject constructor( class UpdateCaldavAccountViewModel @ViewModelInject constructor(
private val client: CaldavClient) : CompletableViewModel<String>() { private val provider: CaldavClientProvider
) : CompletableViewModel<String>() {
suspend fun updateCaldavAccount(url: String, username: String, password: String) { suspend fun updateCaldavAccount(url: String, username: String, password: String) {
run { client.forUrl(url, username, password).homeSet() } run { provider.forUrl(url, username, password).homeSet(username, password) }
} }
} }

@ -6,8 +6,11 @@ import org.tasks.data.CaldavCalendar
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class UpdateCalendarViewModel @ViewModelInject constructor( class UpdateCalendarViewModel @ViewModelInject constructor(
private val client: CaldavClient) : CompletableViewModel<String?>() { private val provider: CaldavClientProvider
) : CompletableViewModel<String?>() {
suspend fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) { suspend fun updateCalendar(account: CaldavAccount, calendar: CaldavCalendar, name: String, color: Int) {
run { client.forCalendar(account, calendar).updateCollection(name, color) } run {
calendar.url?.let { provider.forAccount(account, it).updateCollection(name, color) }
}
} }
} }

@ -104,7 +104,7 @@ class EteSyncClient {
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS) .readTimeout(120, TimeUnit.SECONDS)
if (preferences.isFlipperEnabled) { if (preferences.isFlipperEnabled) {
interceptor.add(builder) interceptor.apply(builder)
} }
httpClient = builder.build() httpClient = builder.build()
httpUrl = url?.toHttpUrlOrNull() httpUrl = url?.toHttpUrlOrNull()

@ -115,7 +115,7 @@ class Preferences @JvmOverloads constructor(
} }
fun setPurchases(purchases: Collection<Purchase>) { fun setPurchases(purchases: Collection<Purchase>) {
setPurchases(purchases.map(Purchase::toJson).toHashSet()) setPurchases(purchases.mapNotNull(Purchase::toJson).toHashSet())
} }
fun setPurchases(set: HashSet<String>) { fun setPurchases(set: HashSet<String>) {

@ -6,7 +6,7 @@ import okhttp3.OkHttpClient
import javax.inject.Inject import javax.inject.Inject
class DebugNetworkInterceptor @Inject constructor() { class DebugNetworkInterceptor @Inject constructor() {
fun add(builder: OkHttpClient.Builder?) {} fun apply(builder: OkHttpClient.Builder?) {}
fun <T> execute(httpRequest: HttpRequest?, responseClass: Class<T>?): T? = null fun <T> execute(httpRequest: HttpRequest?, responseClass: Class<T>?): T? = null
fun <T> report(httpResponse: HttpResponse?, responseClass: Class<T>?, start: Long, finish: Long): T? = null fun <T> report(httpResponse: HttpResponse?, responseClass: Class<T>?, start: Long, finish: Long): T? = null
} }
Loading…
Cancel
Save