diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index fa126ae3a..a6ca8de8c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.data.implementation import android.content.Context +import android.util.Log import com.google.gson.JsonSyntaxException import com.habitrpg.android.habitica.BuildConfig import com.habitrpg.android.habitica.HabiticaBaseApplication @@ -55,6 +56,7 @@ import okhttp3.Cache import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor +import org.json.JSONObject import retrofit2.Converter import retrofit2.HttpException import retrofit2.Retrofit @@ -155,6 +157,35 @@ class ApiClientImpl( // Modify cache control for 4xx or 5xx range - effectively "do not cache", preventing caching of 4xx and 5xx responses if (response.code in 400..599) { when (response.code) { + 401 -> { + val path = response.request.url.encodedPath + if (!path.contains("/user/auth/update-password")) { + val bodyStr = try { + response.peekBody(1024).string() + } catch (_: Exception) { + "" + } + + val (errField, msgField) = try { + val obj = JSONObject(bodyStr) + obj.optString("error", "") to obj.optString("message", "") + } catch (_: Exception) { + "" to "" + } + + val shouldLogout = errField.equals("missingAuthHeaders", ignoreCase = true) + || errField.equals("invalidCredentials", ignoreCase = true) + || msgField.contains("invalidCredentials", ignoreCase = true) + || msgField.contains("Missing authentication headers", ignoreCase = true) + || msgField.contains("There is no account that uses those credentials", ignoreCase = true) + + if (shouldLogout) { + HabiticaBaseApplication.logout(context) + } + } + return@addNetworkInterceptor response + } + 404 -> { // The server is returning a 404 error, which means the requested resource was not found. // In this case - we want to actually cache the response, and handle it in the app diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AccountPreferenceFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AccountPreferenceFragment.kt index 80eddc841..82b954ba2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AccountPreferenceFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AccountPreferenceFragment.kt @@ -271,6 +271,10 @@ class AccountPreferenceFragment : ) response?.apiToken?.let { viewModel.saveTokens(it, user?.id ?: "") + (activity as? SnackbarActivity)?.showSnackbar( + content = getString(R.string.password_changed), + displayType = HabiticaSnackbar.SnackbarDisplayType.SUCCESS, + ) sheet.dismiss() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ApiTokenBottomSheetFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ApiTokenBottomSheetFragment.kt index 7b090c557..df8827953 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ApiTokenBottomSheetFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ApiTokenBottomSheetFragment.kt @@ -1,5 +1,7 @@ package com.habitrpg.android.habitica.ui.fragments.preferences +import android.content.ClipData +import android.content.ClipboardManager import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater @@ -7,6 +9,7 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService import androidx.core.view.WindowInsetsControllerCompat import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.habitrpg.android.habitica.R @@ -61,6 +64,9 @@ class ApiTokenBottomSheetFragment : BottomSheetDialogFragment() { content = getString(R.string.copied_to_clipboard, copiedToken), displayType = HabiticaSnackbar.SnackbarDisplayType.SUCCESS, ) + val clipboard: ClipboardManager? = + context?.let { getSystemService(it, ClipboardManager::class.java) } + clipboard?.setPrimaryClip(ClipData.newPlainText("API Token", copiedToken)) dismiss() }) } diff --git a/common/src/main/java/com/habitrpg/common/habitica/models/auth/UserAuthResponse.kt b/common/src/main/java/com/habitrpg/common/habitica/models/auth/UserAuthResponse.kt index dde393ead..dfc92ecaa 100644 --- a/common/src/main/java/com/habitrpg/common/habitica/models/auth/UserAuthResponse.kt +++ b/common/src/main/java/com/habitrpg/common/habitica/models/auth/UserAuthResponse.kt @@ -1,15 +1,12 @@ package com.habitrpg.common.habitica.models.auth -class UserAuthResponse { - // we need apiToken and token, as both are possible returns - var apiToken: String = "" - var token: String - get() { - return apiToken - } - set(value) { - apiToken = value - } - var newUser = false - var id: String = "" +data class UserAuthResponse( + val apiToken: String = "", + val id: String = "", + val newUser: Boolean = false +) { + + val token: String + get() = apiToken } +