kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale-android
Merge branch 'enhancement/80-login-error-messages' into 'develop'
#80: Display error messages for user when login failes Closes #80 See merge request funkwhale/funkwhale-android!63enhancement/speed-up-pipelines
commit
45c9d28dda
|
@ -11,10 +11,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import audio.funkwhale.ffa.R
|
||||
import audio.funkwhale.ffa.databinding.ActivityLoginBinding
|
||||
import audio.funkwhale.ffa.fragments.LoginDialog
|
||||
import audio.funkwhale.ffa.utils.AppContext
|
||||
import audio.funkwhale.ffa.utils.OAuth
|
||||
import audio.funkwhale.ffa.utils.Userinfo
|
||||
import audio.funkwhale.ffa.utils.log
|
||||
import audio.funkwhale.ffa.utils.*
|
||||
import com.github.kittinunf.fuel.Fuel
|
||||
import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult
|
||||
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
||||
|
@ -22,6 +19,7 @@ import com.github.kittinunf.result.Result
|
|||
import com.preference.PowerPreference
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
|
||||
data class FwCredentials(val token: String, val non_field_errors: List<String>?)
|
||||
|
@ -74,27 +72,27 @@ class LoginActivity : AppCompatActivity() {
|
|||
var hostname = hostname.text.toString().trim()
|
||||
|
||||
try {
|
||||
if (hostname.isEmpty()) throw Exception(getString(R.string.login_error_hostname))
|
||||
validateHostname(hostname, cleartext.isChecked)?.let {
|
||||
hostnameField.error = it
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
Uri.parse(hostname).apply {
|
||||
if (!cleartext.isChecked && scheme == "http") {
|
||||
throw Exception(getString(R.string.login_error_hostname_https))
|
||||
}
|
||||
|
||||
if (scheme == null) {
|
||||
hostname = when (cleartext.isChecked) {
|
||||
true -> "http://$hostname"
|
||||
false -> "https://$hostname"
|
||||
}
|
||||
val uri = Uri.parse(hostname)
|
||||
if (uri.scheme == null) {
|
||||
hostname = when (cleartext.isChecked) {
|
||||
true -> "http://$hostname"
|
||||
false -> "https://$hostname"
|
||||
}
|
||||
}
|
||||
|
||||
hostnameField.error = ""
|
||||
|
||||
when (anonymous.isChecked) {
|
||||
val fuelResult = when (anonymous.isChecked) {
|
||||
false -> authedLogin(hostname)
|
||||
true -> anonymousLogin(hostname)
|
||||
}
|
||||
|
||||
hostnameField.error = mapFuelResultToError(fuelResult)
|
||||
} catch (e: Exception) {
|
||||
val message =
|
||||
if (e.message?.isEmpty() == true) getString(R.string.login_error_hostname)
|
||||
|
@ -106,56 +104,66 @@ class LoginActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun mapFuelResultToError(fuelResult: FuelResult) = when {
|
||||
fuelResult.httpStatus == 404 ->
|
||||
getString(R.string.login_error_funkwhale_not_found)
|
||||
|
||||
!fuelResult.success ->
|
||||
getString(R.string.login_error, fuelResult.message)
|
||||
|
||||
else -> ""
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
limitContainerWidth()
|
||||
}
|
||||
|
||||
private fun authedLogin(hostname: String) {
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).setString("hostname", hostname)
|
||||
private fun validateHostname(hostname: String, cleartext: Boolean): String? {
|
||||
if (hostname.isEmpty()) {
|
||||
return getString(R.string.login_error_hostname)
|
||||
}
|
||||
if (!cleartext && hostname.startsWith("http")) {
|
||||
return getString(R.string.login_error_hostname_https)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun authedLogin(hostname: String): FuelResult {
|
||||
oAuth.init(hostname)
|
||||
oAuth.register {
|
||||
return oAuth.register {
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).setString("hostname", hostname)
|
||||
oAuth.authorize(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun anonymousLogin(hostname: String) {
|
||||
private fun anonymousLogin(hostname: String): FuelResult {
|
||||
val dialog = LoginDialog().apply {
|
||||
show(supportFragmentManager, "LoginDialog")
|
||||
}
|
||||
|
||||
lifecycleScope.launch(Main) {
|
||||
try {
|
||||
val (_, _, result) = Fuel.get("$hostname/api/v1/tracks/")
|
||||
.awaitObjectResponseResult(gsonDeserializerOf(FwCredentials::class.java))
|
||||
val uri = "$hostname/api/v1/tracks/"
|
||||
val (_, _, result) = runBlocking {
|
||||
Fuel.get(uri).awaitObjectResponseResult(gsonDeserializerOf(FwCredentials::class.java))
|
||||
}
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).apply {
|
||||
setString("hostname", hostname)
|
||||
setBoolean("anonymous", true)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
startActivity(Intent(this@LoginActivity, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
is Result.Failure -> {
|
||||
dialog.dismiss()
|
||||
|
||||
binding.hostnameField.error = result.error.localizedMessage
|
||||
}
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).apply {
|
||||
setString("hostname", hostname)
|
||||
setBoolean("anonymous", true)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
dialog.dismiss()
|
||||
startActivity(Intent(this@LoginActivity, MainActivity::class.java))
|
||||
finish()
|
||||
return FuelResult.ok()
|
||||
}
|
||||
|
||||
val message =
|
||||
if (e.message?.isEmpty() == true) getString(R.string.login_error_hostname)
|
||||
else e.message
|
||||
|
||||
binding.hostnameField.error = message
|
||||
is Result.Failure -> {
|
||||
dialog.dismiss()
|
||||
return FuelResult.from(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
|
|||
import audio.funkwhale.ffa.R
|
||||
import audio.funkwhale.ffa.fragments.BrowseFragment
|
||||
import audio.funkwhale.ffa.repositories.Repository
|
||||
import com.github.kittinunf.fuel.core.FuelError
|
||||
import com.github.kittinunf.fuel.core.Request
|
||||
import com.google.android.exoplayer2.offline.Download
|
||||
import com.google.gson.Gson
|
||||
|
@ -93,5 +94,9 @@ fun Request.authorize(context: Context, oAuth: OAuth): Request {
|
|||
}
|
||||
}
|
||||
|
||||
fun FuelError.formatResponseMessage(): String {
|
||||
return "${response.statusCode}: ${response.url}"
|
||||
}
|
||||
|
||||
fun Download.getMetadata(): DownloadInfo? =
|
||||
Gson().fromJson(String(this.request.data), DownloadInfo::class.java)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package audio.funkwhale.ffa.utils
|
||||
|
||||
import com.github.kittinunf.fuel.core.FuelError
|
||||
import com.github.kittinunf.result.Result
|
||||
|
||||
data class FuelResult(val httpStatus: Int? = null, val message: String? = null) {
|
||||
|
||||
val success: Boolean get() = httpStatus == 200
|
||||
|
||||
companion object {
|
||||
|
||||
fun ok() = FuelResult(200)
|
||||
|
||||
fun from(result: Result.Failure<FuelError>): FuelResult {
|
||||
return FuelResult(result.error.response.statusCode, result.error.response.responseMessage)
|
||||
}
|
||||
|
||||
fun failure(): FuelResult {
|
||||
return FuelResult()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,16 +13,7 @@ import com.github.kittinunf.fuel.gson.jsonBody
|
|||
import com.github.kittinunf.result.Result
|
||||
import com.preference.PowerPreference
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.openid.appauth.AuthState
|
||||
import net.openid.appauth.AuthorizationException
|
||||
import net.openid.appauth.AuthorizationRequest
|
||||
import net.openid.appauth.AuthorizationResponse
|
||||
import net.openid.appauth.AuthorizationService
|
||||
import net.openid.appauth.AuthorizationServiceConfiguration
|
||||
import net.openid.appauth.ClientSecretPost
|
||||
import net.openid.appauth.RegistrationRequest
|
||||
import net.openid.appauth.RegistrationResponse
|
||||
import net.openid.appauth.ResponseTypeValues
|
||||
import net.openid.appauth.*
|
||||
|
||||
fun AuthState.save() {
|
||||
PowerPreference.getFileByName(AppContext.PREFS_CREDENTIALS).apply {
|
||||
|
@ -84,8 +75,8 @@ class OAuth(private val authorizationServiceFactory: AuthorizationServiceFactory
|
|||
state: AuthState,
|
||||
context: Context
|
||||
): Boolean {
|
||||
if (state.needsTokenRefresh.also { it.log("needsTokenRefresh()") }
|
||||
&& state.refreshToken != null) {
|
||||
if (state.needsTokenRefresh.also { it.log("needsTokenRefresh()") } &&
|
||||
state.refreshToken != null) {
|
||||
val refreshRequest = state.createTokenRefreshRequest()
|
||||
val auth = ClientSecretPost(state.clientSecret)
|
||||
runBlocking {
|
||||
|
@ -119,39 +110,46 @@ class OAuth(private val authorizationServiceFactory: AuthorizationServiceFactory
|
|||
fun service(context: Context): AuthorizationService =
|
||||
authorizationServiceFactory.create(context)
|
||||
|
||||
fun register(authState: AuthState? = null, callback: () -> Unit) {
|
||||
fun register(authState: AuthState? = null, callback: () -> Unit): FuelResult {
|
||||
(authState ?: state()).authorizationServiceConfiguration?.let { config ->
|
||||
runBlocking {
|
||||
val (_, _, result: Result<App, FuelError>) = Fuel.post(config.registrationEndpoint.toString())
|
||||
|
||||
val (_, _, result: Result<App, FuelError>) = runBlocking {
|
||||
Fuel.post(config.registrationEndpoint.toString())
|
||||
.header("Content-Type", "application/json")
|
||||
.jsonBody(registrationBody())
|
||||
.awaitObjectResponseResult(gsonDeserializerOf(App::class.java))
|
||||
}
|
||||
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
val app = result.get()
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
Log.i("OAuth", "OAuth client app created.")
|
||||
val app = result.get()
|
||||
|
||||
val response = RegistrationResponse.Builder(registration()!!)
|
||||
.setClientId(app.client_id)
|
||||
.setClientSecret(app.client_secret)
|
||||
.setClientIdIssuedAt(0)
|
||||
.setClientSecretExpiresAt(null)
|
||||
.build()
|
||||
val response = RegistrationResponse.Builder(registration()!!)
|
||||
.setClientId(app.client_id)
|
||||
.setClientSecret(app.client_secret)
|
||||
.setClientIdIssuedAt(0)
|
||||
.setClientSecretExpiresAt(null)
|
||||
.build()
|
||||
|
||||
state().apply {
|
||||
update(response)
|
||||
save()
|
||||
|
||||
callback()
|
||||
}
|
||||
state().apply {
|
||||
update(response)
|
||||
save()
|
||||
callback()
|
||||
return FuelResult.ok()
|
||||
}
|
||||
}
|
||||
|
||||
is Result.Failure -> {
|
||||
result.log("register()")
|
||||
}
|
||||
is Result.Failure -> {
|
||||
Log.i(
|
||||
"OAuth", "Couldn't register client application ${result.error.formatResponseMessage()}"
|
||||
)
|
||||
return FuelResult.from(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i("OAuth", "Missing AuthorizationServiceConfiguration")
|
||||
return FuelResult.failure()
|
||||
}
|
||||
|
||||
private fun registrationBody(): Map<String, String> {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
<string name="login_password">Passwort</string>
|
||||
<string name="login_submit">Anmelden</string>
|
||||
<string name="login_logging_in">Einloggen</string>
|
||||
<string name="login_error">Anmeldung fehlgeschlagen: %s</string>
|
||||
<string name="login_error_funkwhale_not_found">Keine Funkwhale Pod gefunden</string>
|
||||
<string name="login_error_hostname">Das ist keine gültige URL</string>
|
||||
<string name="login_error_hostname_https">Der Zugriff auf den Funkwhale Server sollte über HTTPS erfolgen</string>
|
||||
<string name="toolbar_search">Suche</string>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<color name="colorPrimary">#476d85</color>
|
||||
<color name="colorPrimaryDark">#646568</color>
|
||||
<color name="colorAccent">#d35400</color>
|
||||
<color name="colorError">#fdcfbb</color>
|
||||
<color name="colorError">#F75D28</color>
|
||||
|
||||
<color name="colorSelected">#dadada</color>
|
||||
<color name="colorFavorite">#d35400</color>
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
<string name="login_password">Password</string>
|
||||
<string name="login_submit">Log in</string>
|
||||
<string name="login_logging_in">Logging in</string>
|
||||
<string name="login_error">Login failed: %s</string>
|
||||
<string name="login_error_funkwhale_not_found">No Funkwhale pod found</string>
|
||||
<string name="login_error_hostname">This could not be understood as a valid URL</string>
|
||||
<string name="login_error_hostname_https">The Funkwhale hostname should be secure through HTTPS</string>
|
||||
<string name="login_error_userinfo">We could not retrieve information about your user</string>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Show error messages to user when login failes (#80)
|
Ładowanie…
Reference in New Issue