mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-04-01 22:34:28 +02:00
[API/Usos] Implement OAuth authorization flow.
This commit is contained in:
parent
6c96875c83
commit
2ff784066e
@ -99,3 +99,12 @@ const val PODLASIE_API_VERSION = "1.0.62"
|
|||||||
const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api"
|
const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api"
|
||||||
const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia"
|
const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia"
|
||||||
const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia"
|
const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia"
|
||||||
|
|
||||||
|
const val USOS_API_OAUTH_REDIRECT_URL = "szkolny://redirect/usos"
|
||||||
|
|
||||||
|
val USOS_API_SCOPES by lazy { listOf(
|
||||||
|
"offline_access",
|
||||||
|
"studies",
|
||||||
|
"grades",
|
||||||
|
"events",
|
||||||
|
) }
|
||||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
|
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
|
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.Usos
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
|
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||||
@ -113,6 +114,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
|
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
|
||||||
LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback)
|
LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback)
|
||||||
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
|
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
|
||||||
|
LOGIN_TYPE_USOS -> Usos(app, profile, loginStore, taskCallback)
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (edziennikInterface == null) {
|
if (edziennikInterface == null) {
|
||||||
|
@ -27,11 +27,21 @@ class DataUsos(
|
|||||||
|
|
||||||
override fun generateUserCode() = "USOS:TEST"
|
override fun generateUserCode() = "USOS:TEST"
|
||||||
|
|
||||||
|
var schoolId: String?
|
||||||
|
get() { mSchoolId = mSchoolId ?: loginStore.getLoginData("schoolId", null); return mSchoolId }
|
||||||
|
set(value) { loginStore.putLoginData("schoolId", value); mSchoolId = value }
|
||||||
|
private var mSchoolId: String? = null
|
||||||
|
|
||||||
var instanceUrl: String?
|
var instanceUrl: String?
|
||||||
get() { mInstanceUrl = mInstanceUrl ?: loginStore.getLoginData("instanceUrl", null); return mInstanceUrl }
|
get() { mInstanceUrl = mInstanceUrl ?: loginStore.getLoginData("instanceUrl", null); return mInstanceUrl }
|
||||||
set(value) { loginStore.putLoginData("instanceUrl", value); mInstanceUrl = value }
|
set(value) { loginStore.putLoginData("instanceUrl", value); mInstanceUrl = value }
|
||||||
private var mInstanceUrl: String? = null
|
private var mInstanceUrl: String? = null
|
||||||
|
|
||||||
|
var oauthLoginResponse: String?
|
||||||
|
get() { mOauthLoginResponse = mOauthLoginResponse ?: loginStore.getLoginData("oauthLoginResponse", null); return mOauthLoginResponse }
|
||||||
|
set(value) { loginStore.putLoginData("oauthLoginResponse", value); mOauthLoginResponse = value }
|
||||||
|
private var mOauthLoginResponse: String? = null
|
||||||
|
|
||||||
var oauthConsumerKey: String?
|
var oauthConsumerKey: String?
|
||||||
get() { mOauthConsumerKey = mOauthConsumerKey ?: loginStore.getLoginData("oauthConsumerKey", null); return mOauthConsumerKey }
|
get() { mOauthConsumerKey = mOauthConsumerKey ?: loginStore.getLoginData("oauthConsumerKey", null); return mOauthConsumerKey }
|
||||||
set(value) { loginStore.putLoginData("oauthConsumerKey", value); mOauthConsumerKey = value }
|
set(value) { loginStore.putLoginData("oauthConsumerKey", value); mOauthConsumerKey = value }
|
||||||
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.usos
|
|||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.firstlogin.UsosFirstLogin
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLogin
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLogin
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||||
@ -71,9 +72,9 @@ class Usos(
|
|||||||
override fun getEvent(eventFull: EventFull) {}
|
override fun getEvent(eventFull: EventFull) {}
|
||||||
|
|
||||||
override fun firstLogin() {
|
override fun firstLogin() {
|
||||||
/*UsosFirstLogin(data) {
|
UsosFirstLogin(data) {
|
||||||
completed()
|
completed()
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancel() {
|
override fun cancel() {
|
||||||
|
@ -78,7 +78,7 @@ open class UsosApi(open val data: DataUsos, open val lastSync: Long?) {
|
|||||||
responseType: ResponseType,
|
responseType: ResponseType,
|
||||||
onSuccess: (data: T) -> Unit,
|
onSuccess: (data: T) -> Unit,
|
||||||
) {
|
) {
|
||||||
val url = "${data.instanceUrl}/services/$service"
|
val url = "${data.instanceUrl}services/$service"
|
||||||
d(tag, "Request: Usos/Api - $url")
|
d(tag, "Request: Usos/Api - $url")
|
||||||
val formData = params.mapValues {
|
val formData = params.mapValues {
|
||||||
valueToString(it.value)
|
valueToString(it.value)
|
||||||
@ -92,7 +92,11 @@ open class UsosApi(open val data: DataUsos, open val lastSync: Long?) {
|
|||||||
"oauth_token" to (data.oauthTokenKey ?: ""),
|
"oauth_token" to (data.oauthTokenKey ?: ""),
|
||||||
"oauth_version" to "1.0",
|
"oauth_version" to "1.0",
|
||||||
)
|
)
|
||||||
val signature = buildSignature("POST", url, formData + auth)
|
val signature = buildSignature(
|
||||||
|
method = "POST",
|
||||||
|
url = url,
|
||||||
|
params = formData + auth.filterKeys { it.startsWith("oauth_") },
|
||||||
|
)
|
||||||
auth["oauth_signature"] = signature
|
auth["oauth_signature"] = signature
|
||||||
|
|
||||||
val authString = auth.map {
|
val authString = auth.map {
|
||||||
@ -152,10 +156,10 @@ open class UsosApi(open val data: DataUsos, open val lastSync: Long?) {
|
|||||||
|
|
||||||
private fun <T> processResponse(
|
private fun <T> processResponse(
|
||||||
response: Response?,
|
response: Response?,
|
||||||
data: T?,
|
data: T,
|
||||||
onSuccess: (data: T) -> Unit,
|
onSuccess: (data: T) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
onSuccess(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processError(
|
private fun processError(
|
||||||
|
@ -41,7 +41,7 @@ class UsosData(val data: DataUsos, val onSuccess: () -> Unit) {
|
|||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_USOS_API_USER -> {
|
ENDPOINT_USOS_API_USER -> {
|
||||||
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
||||||
TemplateWebSample(data, lastSync, onSuccess)
|
// TemplateWebSample(data, lastSync, onSuccess)
|
||||||
}
|
}
|
||||||
else -> onSuccess(endpointId)
|
else -> onSuccess(endpointId)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2022-10-14.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.firstlogin
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.UsosApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.login.UsosLoginApi
|
||||||
|
|
||||||
|
class UsosFirstLogin(val data: DataUsos, val onSuccess: () -> Unit) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UsosFirstLogin"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val api = UsosApi(data, null)
|
||||||
|
|
||||||
|
init {
|
||||||
|
UsosLoginApi(data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,17 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login
|
package pl.szczodrzynski.edziennik.data.api.edziennik.usos.login
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_MISSING
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_OAUTH_LOGIN_REQUEST
|
import pl.szczodrzynski.edziennik.data.api.ERROR_USOS_OAUTH_LOGIN_REQUEST
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.USOS_API_OAUTH_REDIRECT_URL
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.USOS_API_SCOPES
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.DataUsos
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.data.UsosApi
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.ext.Bundle
|
||||||
|
import pl.szczodrzynski.edziennik.ext.fromQueryString
|
||||||
|
import pl.szczodrzynski.edziennik.ext.toBundle
|
||||||
|
import pl.szczodrzynski.edziennik.ext.toQueryString
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
|
|
||||||
class UsosLoginApi(val data: DataUsos, val onSuccess: () -> Unit) {
|
class UsosLoginApi(val data: DataUsos, val onSuccess: () -> Unit) {
|
||||||
companion object {
|
companion object {
|
||||||
@ -15,15 +22,47 @@ class UsosLoginApi(val data: DataUsos, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init { run {
|
init { run {
|
||||||
if (data.profile == null) {
|
|
||||||
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
|
|
||||||
return@run
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isApiLoginValid()) {
|
if (data.isApiLoginValid()) {
|
||||||
onSuccess()
|
onSuccess()
|
||||||
|
} else if (data.oauthLoginResponse != null) {
|
||||||
|
login()
|
||||||
} else {
|
} else {
|
||||||
data.error(ApiError(TAG, ERROR_USOS_OAUTH_LOGIN_REQUEST))
|
authorize()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
private fun authorize() {
|
||||||
|
val api = UsosApi(data, null)
|
||||||
|
|
||||||
|
api.apiRequest<String>(
|
||||||
|
tag = TAG,
|
||||||
|
service = "oauth/request_token",
|
||||||
|
params = mapOf(
|
||||||
|
"oauth_callback" to USOS_API_OAUTH_REDIRECT_URL,
|
||||||
|
"scopes" to USOS_API_SCOPES,
|
||||||
|
),
|
||||||
|
responseType = UsosApi.ResponseType.PLAIN,
|
||||||
|
) {
|
||||||
|
val response = it.fromQueryString()
|
||||||
|
data.oauthTokenKey = response["oauth_token"]
|
||||||
|
data.oauthTokenSecret = response["oauth_token_secret"]
|
||||||
|
|
||||||
|
val authUrl = "${data.instanceUrl}services/oauth/authorize"
|
||||||
|
val authParams = mapOf(
|
||||||
|
"interactivity" to "confirm_user",
|
||||||
|
"oauth_token" to (data.oauthTokenKey ?: ""),
|
||||||
|
)
|
||||||
|
val params = Bundle(
|
||||||
|
"authorizeUrl" to "$authUrl?${authParams.toQueryString()}",
|
||||||
|
"redirectUrl" to USOS_API_OAUTH_REDIRECT_URL,
|
||||||
|
"responseStoreKey" to "oauthLoginResponse",
|
||||||
|
"extras" to data.loginStore.data.toBundle(),
|
||||||
|
)
|
||||||
|
data.error(ApiError(TAG, ERROR_USOS_OAUTH_LOGIN_REQUEST).withParams(params))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun login() {
|
||||||
|
d(TAG, "Login to ${data.schoolId} with ${data.oauthLoginResponse} (${data.oauthTokenSecret})")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import android.text.style.StyleSpan
|
|||||||
import androidx.annotation.PluralsRes
|
import androidx.annotation.PluralsRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import com.mikepenz.materialdrawer.holder.StringHolder
|
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||||
|
import java.net.URLDecoder
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
|
||||||
fun CharSequence?.isNotNullNorEmpty(): Boolean {
|
fun CharSequence?.isNotNullNorEmpty(): Boolean {
|
||||||
@ -346,8 +347,14 @@ fun CharSequence.toStringHolder() = StringHolder(this)
|
|||||||
fun @receiver:StringRes Int.resolveString(context: Context) = context.getString(this)
|
fun @receiver:StringRes Int.resolveString(context: Context) = context.getString(this)
|
||||||
|
|
||||||
fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8").replace("+", "%20")
|
fun String.urlEncode(): String = URLEncoder.encode(this, "UTF-8").replace("+", "%20")
|
||||||
|
fun String.urlDecode(): String = URLDecoder.decode(this, "UTF-8")
|
||||||
|
|
||||||
fun Map<String, String>.toQueryString() = this
|
fun Map<String, String>.toQueryString() = this
|
||||||
.map { it.key.urlEncode() to it.value.urlEncode() }
|
.map { it.key.urlEncode() to it.value.urlEncode() }
|
||||||
.sortedBy { it.first }
|
.sortedBy { it.first }
|
||||||
.joinToString("&") { "${it.first}=${it.second}" }
|
.joinToString("&") { "${it.first}=${it.second}" }
|
||||||
|
|
||||||
|
fun String.fromQueryString() = this
|
||||||
|
.split("&")
|
||||||
|
.map { it.split("=") }
|
||||||
|
.associate { it[0].urlDecode() to it[1].urlDecode() }
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2022-10-14.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.ext.dp
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
|
|
||||||
|
class OAuthLoginDialog(
|
||||||
|
activity: AppCompatActivity,
|
||||||
|
private val authorizeUrl: String,
|
||||||
|
private val redirectUrl: String,
|
||||||
|
private val onSuccess: (responseUrl: String) -> Unit,
|
||||||
|
private val onFailure: (() -> Unit)?,
|
||||||
|
onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||||
|
) : ViewDialog<WebView>(activity, onShowListener, onDismissListener) {
|
||||||
|
|
||||||
|
override val TAG = "OAuthLoginDialog"
|
||||||
|
|
||||||
|
override fun getTitleRes() = R.string.oauth_dialog_title
|
||||||
|
override fun getPositiveButtonText() = R.string.close
|
||||||
|
|
||||||
|
private var isSuccessful = false
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
override fun getRootView(): WebView {
|
||||||
|
val webView = WebView(activity)
|
||||||
|
webView.webViewClient = object : WebViewClient() {
|
||||||
|
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||||
|
d(TAG, "Navigating to $url")
|
||||||
|
if (url.startsWith(redirectUrl)) {
|
||||||
|
isSuccessful = true
|
||||||
|
onSuccess(url)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webView.settings.javaScriptEnabled = true
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onShow() {
|
||||||
|
dialog.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
root.minimumHeight = activity.windowManager.defaultDisplay?.height?.div(2) ?: 300.dp
|
||||||
|
root.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
root.loadUrl(authorizeUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDismiss() {
|
||||||
|
root.stopLoading()
|
||||||
|
if (!isSuccessful)
|
||||||
|
onFailure?.invoke()
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
private val app: App by lazy { applicationContext as App }
|
private val app: App by lazy { applicationContext as App }
|
||||||
private lateinit var b: LoginActivityBinding
|
private lateinit var b: LoginActivityBinding
|
||||||
lateinit var navOptions: NavOptions
|
lateinit var navOptions: NavOptions
|
||||||
|
lateinit var navOptionsBuilder: NavOptions.Builder
|
||||||
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
|
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
|
||||||
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
|
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
|
||||||
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
||||||
@ -87,12 +88,12 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setTheme(R.style.AppTheme_Light)
|
setTheme(R.style.AppTheme_Light)
|
||||||
|
|
||||||
navOptions = NavOptions.Builder()
|
navOptionsBuilder = NavOptions.Builder()
|
||||||
.setEnterAnim(R.anim.slide_in_right)
|
.setEnterAnim(R.anim.slide_in_right)
|
||||||
.setExitAnim(R.anim.slide_out_left)
|
.setExitAnim(R.anim.slide_out_left)
|
||||||
.setPopEnterAnim(R.anim.slide_in_left)
|
.setPopEnterAnim(R.anim.slide_in_left)
|
||||||
.setPopExitAnim(R.anim.slide_out_right)
|
.setPopExitAnim(R.anim.slide_out_right)
|
||||||
.build()
|
navOptions = navOptionsBuilder.build()
|
||||||
|
|
||||||
b = LoginActivityBinding.inflate(layoutInflater)
|
b = LoginActivityBinding.inflate(layoutInflater)
|
||||||
setContentView(b.root)
|
setContentView(b.root)
|
||||||
|
@ -14,6 +14,8 @@ import android.widget.Toast
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.navigation.NavOptions
|
||||||
|
import androidx.navigation.navOptions
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
@ -304,6 +306,14 @@ class LoginFormFragment : Fragment(), CoroutineScope {
|
|||||||
if (hasErrors)
|
if (hasErrors)
|
||||||
return
|
return
|
||||||
|
|
||||||
nav.navigate(R.id.loginProgressFragment, payload, activity.navOptions)
|
val navOptions =
|
||||||
|
if (credentials.isEmpty())
|
||||||
|
activity.navOptionsBuilder
|
||||||
|
.setPopUpTo(R.id.loginPlatformListFragment, inclusive = false)
|
||||||
|
.build()
|
||||||
|
else
|
||||||
|
activity.navOptions
|
||||||
|
|
||||||
|
nav.navigate(R.id.loginProgressFragment, payload, navOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.api.events.UserActionRequiredEvent
|
|||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.ext.*
|
import pl.szczodrzynski.edziennik.ext.*
|
||||||
import pl.szczodrzynski.edziennik.ui.captcha.LibrusCaptchaDialog
|
import pl.szczodrzynski.edziennik.ui.captcha.LibrusCaptchaDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.OAuthLoginDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
|
|
||||||
class UserActionManager(val app: App) {
|
class UserActionManager(val app: App) {
|
||||||
@ -89,9 +90,13 @@ class UserActionManager(val app: App) {
|
|||||||
onFailure: (() -> Unit)? = null
|
onFailure: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
d(TAG, "Running user action ($type) with params: ${params?.toJsonObject()}")
|
d(TAG, "Running user action ($type) with params: ${params?.toJsonObject()}")
|
||||||
when (type) {
|
val isSuccessful = when (type) {
|
||||||
UserActionRequiredEvent.CAPTCHA_LIBRUS -> executeLibrus(activity, profileId, params, onSuccess, onFailure)
|
UserActionRequiredEvent.CAPTCHA_LIBRUS -> executeLibrus(activity, profileId, params, onSuccess, onFailure)
|
||||||
UserActionRequiredEvent.OAUTH_USOS -> executeOauth(activity, profileId, params, onSuccess, onFailure)
|
UserActionRequiredEvent.OAUTH_USOS -> executeOauth(activity, profileId, params, onSuccess, onFailure)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
if (!isSuccessful) {
|
||||||
|
onFailure?.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,9 +106,9 @@ class UserActionManager(val app: App) {
|
|||||||
params: Bundle?,
|
params: Bundle?,
|
||||||
onSuccess: ((params: Bundle) -> Unit)?,
|
onSuccess: ((params: Bundle) -> Unit)?,
|
||||||
onFailure: (() -> Unit)?,
|
onFailure: (() -> Unit)?,
|
||||||
) {
|
): Boolean {
|
||||||
if (profileId == null)
|
if (profileId == null)
|
||||||
return
|
return false
|
||||||
val extras = params?.getBundle("extras")
|
val extras = params?.getBundle("extras")
|
||||||
// show captcha dialog
|
// show captcha dialog
|
||||||
// use passed onSuccess listener, else sync profile
|
// use passed onSuccess listener, else sync profile
|
||||||
@ -117,14 +122,14 @@ class UserActionManager(val app: App) {
|
|||||||
if (extras != null)
|
if (extras != null)
|
||||||
args.putAll(extras)
|
args.putAll(extras)
|
||||||
|
|
||||||
if (onSuccess != null) {
|
if (onSuccess != null)
|
||||||
onSuccess(args)
|
onSuccess(args)
|
||||||
} else {
|
else
|
||||||
EdziennikTask.syncProfile(profileId, arguments = args.toJsonObject()).enqueue(activity)
|
EdziennikTask.syncProfile(profileId, arguments = args.toJsonObject()).enqueue(activity)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onFailure = onFailure
|
onFailure = onFailure,
|
||||||
).show()
|
).show()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeOauth(
|
private fun executeOauth(
|
||||||
@ -133,10 +138,30 @@ class UserActionManager(val app: App) {
|
|||||||
params: Bundle?,
|
params: Bundle?,
|
||||||
onSuccess: ((params: Bundle) -> Unit)?,
|
onSuccess: ((params: Bundle) -> Unit)?,
|
||||||
onFailure: (() -> Unit)?,
|
onFailure: (() -> Unit)?,
|
||||||
) {
|
): Boolean {
|
||||||
if (profileId == null || params == null)
|
if (profileId == null || params == null)
|
||||||
return
|
return false
|
||||||
val extras = params.getBundle("extras")
|
val extras = params.getBundle("extras")
|
||||||
|
val storeKey = params.getString("responseStoreKey") ?: return false
|
||||||
|
|
||||||
|
OAuthLoginDialog(
|
||||||
|
activity = activity,
|
||||||
|
authorizeUrl = params.getString("authorizeUrl") ?: return false,
|
||||||
|
redirectUrl = params.getString("redirectUrl") ?: return false,
|
||||||
|
onSuccess = { responseUrl ->
|
||||||
|
val args = Bundle(
|
||||||
|
storeKey to responseUrl,
|
||||||
|
)
|
||||||
|
if (extras != null)
|
||||||
|
args.putAll(extras)
|
||||||
|
|
||||||
|
if (onSuccess != null)
|
||||||
|
onSuccess(args)
|
||||||
|
else
|
||||||
|
EdziennikTask.syncProfile(profileId, arguments = args.toJsonObject()).enqueue(activity)
|
||||||
|
},
|
||||||
|
onFailure = onFailure,
|
||||||
|
).show()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1542,4 +1542,5 @@
|
|||||||
<string name="login_mode_usos_oauth">Logowanie z użyciem przeglądarki</string>
|
<string name="login_mode_usos_oauth">Logowanie z użyciem przeglądarki</string>
|
||||||
<string name="login_mode_usos_oauth_guide">TODO</string>
|
<string name="login_mode_usos_oauth_guide">TODO</string>
|
||||||
<string name="notification_user_action_required_oauth_usos">USOS - wymagane logowanie z użyciem przeglądarki</string>
|
<string name="notification_user_action_required_oauth_usos">USOS - wymagane logowanie z użyciem przeglądarki</string>
|
||||||
|
<string name="oauth_dialog_title">Zaloguj się</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user