diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt b/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt index 05e45f8c8..78688cac0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/SyncModule.kt @@ -3,10 +3,13 @@ package org.koitharu.kotatsu.sync import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module +import org.koitharu.kotatsu.sync.data.SyncAuthApi import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel val syncModule get() = module { - viewModel { SyncAuthViewModel(androidContext(), get()) } + factory { SyncAuthApi(androidContext(), get()) } + + viewModel { SyncAuthViewModel(get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/data/AccountAuthenticator.kt b/app/src/main/java/org/koitharu/kotatsu/sync/data/AccountAuthenticator.kt new file mode 100644 index 000000000..38a2c8696 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/sync/data/AccountAuthenticator.kt @@ -0,0 +1,38 @@ +package org.koitharu.kotatsu.sync.data + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.Context +import kotlinx.coroutines.runBlocking +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import org.koitharu.kotatsu.R + +class AccountAuthenticator( + context: Context, + private val account: Account, + private val authApi: SyncAuthApi, +) : Authenticator { + + private val accountManager = AccountManager.get(context) + private val tokenType = context.getString(R.string.account_type_sync) + + override fun authenticate(route: Route?, response: Response): Request? { + val newToken = tryRefreshToken() ?: return null + accountManager.setAuthToken(account, tokenType, newToken) + return response.request.newBuilder() + .header("Authorization", "Bearer $newToken") + .build() + } + + private fun tryRefreshToken() = runCatching { + runBlocking { + authApi.authenticate( + account.name, + accountManager.getPassword(account), + ) + } + }.getOrNull() +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt b/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt new file mode 100644 index 000000000..744670526 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/sync/data/SyncAuthApi.kt @@ -0,0 +1,30 @@ +package org.koitharu.kotatsu.sync.data + +import android.content.Context +import okhttp3.OkHttpClient +import okhttp3.Request +import org.json.JSONObject +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.parsers.util.await +import org.koitharu.kotatsu.parsers.util.parseJson +import org.koitharu.kotatsu.utils.ext.toRequestBody + +class SyncAuthApi( + context: Context, + private val okHttpClient: OkHttpClient, +) { + + private val baseUrl = context.getString(R.string.url_sync_server) + + suspend fun authenticate(email: String, password: String): String { + val body = JSONObject( + mapOf("email" to email, "password" to password) + ).toRequestBody() + val request = Request.Builder() + .url("$baseUrl/auth") + .post(body) + .build() + val response = okHttpClient.newCall(request).await().parseJson() + return response.getString("token") + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncHelper.kt b/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncHelper.kt index 2b78a3e9d..8bba4be33 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncHelper.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/domain/SyncHelper.kt @@ -18,7 +18,9 @@ import org.koitharu.kotatsu.core.db.MangaDatabase.Companion.TABLE_MANGA_TAGS import org.koitharu.kotatsu.core.db.MangaDatabase.Companion.TABLE_TAGS import org.koitharu.kotatsu.parsers.util.json.mapJSONTo import org.koitharu.kotatsu.parsers.util.parseJson +import org.koitharu.kotatsu.sync.data.AccountAuthenticator import org.koitharu.kotatsu.sync.data.AccountInterceptor +import org.koitharu.kotatsu.sync.data.SyncAuthApi import org.koitharu.kotatsu.utils.GZipInterceptor import org.koitharu.kotatsu.utils.ext.toContentValues import org.koitharu.kotatsu.utils.ext.toJson @@ -40,6 +42,7 @@ class SyncHelper( ) { private val httpClient = OkHttpClient.Builder() + .authenticator(AccountAuthenticator(context, account, SyncAuthApi(context, OkHttpClient()))) .addInterceptor(AccountInterceptor(context, account)) .addInterceptor(GZipInterceptor()) .build() diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt index bfe0ec079..da6dfa32c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthViewModel.kt @@ -1,44 +1,22 @@ package org.koitharu.kotatsu.sync.ui -import android.content.Context import kotlinx.coroutines.Dispatchers -import okhttp3.OkHttpClient -import okhttp3.Request -import org.json.JSONObject -import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseViewModel -import org.koitharu.kotatsu.parsers.util.await -import org.koitharu.kotatsu.parsers.util.parseJson +import org.koitharu.kotatsu.sync.data.SyncAuthApi import org.koitharu.kotatsu.sync.domain.SyncAuthResult import org.koitharu.kotatsu.utils.SingleLiveEvent -import org.koitharu.kotatsu.utils.ext.toRequestBody -import java.util.* class SyncAuthViewModel( - context: Context, - private val okHttpClient: OkHttpClient, + private val api: SyncAuthApi, ) : BaseViewModel() { - private val baseUrl = context.getString(R.string.url_sync_server) val onTokenObtained = SingleLiveEvent() fun obtainToken(email: String, password: String) { launchLoadingJob(Dispatchers.Default) { - authenticate(email, password) - val token = UUID.randomUUID().toString() + val token = api.authenticate(email, password) val result = SyncAuthResult(email, password, token) onTokenObtained.postCall(result) } } - - private suspend fun authenticate(email: String, password: String) { - val body = JSONObject( - mapOf("email" to email, "password" to password) - ).toRequestBody() - val request = Request.Builder() - .url("$baseUrl/register") - .post(body) - .build() - val response = okHttpClient.newCall(request).await().parseJson() - } } \ No newline at end of file