forked from github/wulkanowy-mirror
Merge branch 'release/2.3.4'
This commit is contained in:
commit
1fe464a289
@ -27,8 +27,8 @@ android {
|
|||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 143
|
versionCode 144
|
||||||
versionName "2.3.3"
|
versionName "2.3.4"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
@ -163,7 +163,7 @@ play {
|
|||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||||
userFraction = 0.99d
|
userFraction = 0.99d
|
||||||
updatePriority = 3
|
updatePriority = 1
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.github.wulkanowy:sdk:2.3.5'
|
implementation 'io.github.wulkanowy:sdk:2.3.6'
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||||
|
|
||||||
@ -238,6 +238,7 @@ dependencies {
|
|||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
||||||
|
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
||||||
|
|
||||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||||
implementation 'com.github.Faierbel:slf4j-timber:2.0'
|
implementation 'com.github.Faierbel:slf4j-timber:2.0'
|
||||||
|
@ -21,6 +21,7 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||||
|
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -43,6 +44,7 @@ internal class DataModule {
|
|||||||
buildTag = android.os.Build.MODEL
|
buildTag = android.os.Build.MODEL
|
||||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||||
setSimpleHttpLogger { Timber.d(it) }
|
setSimpleHttpLogger { Timber.d(it) }
|
||||||
|
setAdditionalCookieManager(WebkitCookieManagerProxy())
|
||||||
|
|
||||||
// for debug only
|
// for debug only
|
||||||
addInterceptor(chuckerInterceptor, network = true)
|
addInterceptor(chuckerInterceptor, network = true)
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.emitAll
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -131,7 +141,7 @@ inline fun <ResultType, RequestType> networkBoundResource(
|
|||||||
query().map { Resource.Success(filterResult(it)) }
|
query().map { Resource.Success(filterResult(it)) }
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
onFetchFailed(throwable)
|
onFetchFailed(throwable)
|
||||||
query().map { Resource.Error(throwable) }
|
flowOf(Resource.Error(throwable))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
query().map { Resource.Success(filterResult(it)) }
|
query().map { Resource.Success(filterResult(it)) }
|
||||||
@ -165,7 +175,7 @@ inline fun <ResultType, RequestType, T> networkBoundResource(
|
|||||||
query().map { Resource.Success(mapResult(it)) }
|
query().map { Resource.Success(mapResult(it)) }
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
onFetchFailed(throwable)
|
onFetchFailed(throwable)
|
||||||
query().map { Resource.Error(throwable) }
|
flowOf(Resource.Error(throwable))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
query().map { Resource.Success(mapResult(it)) }
|
query().map { Resource.Success(mapResult(it)) }
|
||||||
|
@ -65,8 +65,6 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
range = lesson.start..lesson.end,
|
range = lesson.start..lesson.end,
|
||||||
requestCode = getRequestCode(lesson.start, studentId)
|
requestCode = getRequestCode(lesson.start, studentId)
|
||||||
)
|
)
|
||||||
|
|
||||||
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import com.google.android.material.snackbar.Snackbar
|
|||||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||||
|
import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
@ -77,6 +78,10 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
CaptchaDialog.newInstance(url).show(supportFragmentManager, "captcha_dialog")
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_session_expired)
|
.setTitle(R.string.main_session_expired)
|
||||||
|
@ -8,7 +8,6 @@ import android.widget.Toast
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.elevation.SurfaceColors
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -32,6 +31,10 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
@ -45,7 +48,7 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showErrorDetailsDialog(error: Throwable) {
|
override fun showErrorDetailsDialog(error: Throwable) {
|
||||||
|
@ -7,7 +7,6 @@ import androidx.viewbinding.ViewBinding
|
|||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
|
|
||||||
abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragment(layoutId),
|
abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragment(layoutId),
|
||||||
@ -43,12 +42,16 @@ abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragme
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openClearLoginView() {
|
override fun openClearLoginView() {
|
||||||
|
@ -29,6 +29,7 @@ open class BasePresenter<T : BaseView>(
|
|||||||
errorHandler.apply {
|
errorHandler.apply {
|
||||||
showErrorMessage = view::showError
|
showErrorMessage = view::showError
|
||||||
onExpiredCredentials = view::showExpiredCredentialsDialog
|
onExpiredCredentials = view::showExpiredCredentialsDialog
|
||||||
|
onCaptchaVerificationRequired = view::onCaptchaVerificationRequired
|
||||||
onDecryptionFailed = view::showDecryptionFailedDialog
|
onDecryptionFailed = view::showDecryptionFailedDialog
|
||||||
onNoCurrentStudent = view::openClearLoginView
|
onNoCurrentStudent = view::openClearLoginView
|
||||||
onPasswordChangeRequired = view::showChangePasswordSnackbar
|
onPasswordChangeRequired = view::showChangePasswordSnackbar
|
||||||
|
@ -8,6 +8,8 @@ interface BaseView {
|
|||||||
|
|
||||||
fun showExpiredCredentialsDialog()
|
fun showExpiredCredentialsDialog()
|
||||||
|
|
||||||
|
fun onCaptchaVerificationRequired(url: String?)
|
||||||
|
|
||||||
fun showDecryptionFailedDialog()
|
fun showDecryptionFailedDialog()
|
||||||
|
|
||||||
fun showAuthDialog()
|
fun showAuthDialog()
|
||||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||||
import io.github.wulkanowy.utils.getErrorString
|
import io.github.wulkanowy.utils.getErrorString
|
||||||
@ -25,6 +26,8 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||||||
|
|
||||||
var onAuthorizationRequired: () -> Unit = {}
|
var onAuthorizationRequired: () -> Unit = {}
|
||||||
|
|
||||||
|
var onCaptchaVerificationRequired: (url: String?) -> Unit = {}
|
||||||
|
|
||||||
fun dispatch(error: Throwable) {
|
fun dispatch(error: Throwable) {
|
||||||
Timber.e(error, "An exception occurred while the Wulkanowy was running")
|
Timber.e(error, "An exception occurred while the Wulkanowy was running")
|
||||||
proceed(error)
|
proceed(error)
|
||||||
@ -38,6 +41,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||||||
is BadCredentialsException -> onExpiredCredentials()
|
is BadCredentialsException -> onExpiredCredentials()
|
||||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||||
is AuthorizationRequiredException -> onAuthorizationRequired()
|
is AuthorizationRequiredException -> onAuthorizationRequired()
|
||||||
|
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.captcha
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.WebView
|
||||||
|
import android.webkit.WebViewClient
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.databinding.DialogCaptchaBinding
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var sdk: Sdk
|
||||||
|
|
||||||
|
private var webView: WebView? = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CAPTCHA_SUCCESS = "captcha_success"
|
||||||
|
private const val CAPTCHA_URL = "captcha_url"
|
||||||
|
private const val CAPTCHA_CHECK_JS = "document.getElementById('challenge-running') == null"
|
||||||
|
|
||||||
|
fun newInstance(url: String?): CaptchaDialog {
|
||||||
|
return CaptchaDialog().apply {
|
||||||
|
arguments = bundleOf(CAPTCHA_URL to url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View = DialogCaptchaBinding.inflate(inflater).apply { binding = this }.root
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
isCancelable = false
|
||||||
|
binding.captchaRefresh.setOnClickListener {
|
||||||
|
binding.captchaWebview.loadUrl(arguments?.getString(CAPTCHA_URL).orEmpty())
|
||||||
|
}
|
||||||
|
binding.captchaClose.setOnClickListener { dismiss() }
|
||||||
|
|
||||||
|
with(binding.captchaWebview) {
|
||||||
|
webView = this
|
||||||
|
with(settings) {
|
||||||
|
javaScriptEnabled = true
|
||||||
|
userAgentString = sdk.userAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
webViewClient = object : WebViewClient() {
|
||||||
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
|
view?.evaluateJavascript(CAPTCHA_CHECK_JS) {
|
||||||
|
if (it == "true") {
|
||||||
|
onChallengeAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUrl(arguments?.getString(CAPTCHA_URL).orEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onChallengeAccepted() {
|
||||||
|
runCatching { parentFragmentManager.setFragmentResult(CAPTCHA_SUCCESS, bundleOf()) }
|
||||||
|
.onFailure { Timber.e(it) }
|
||||||
|
showMessage(getString(R.string.captcha_verified_message))
|
||||||
|
dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
webView?.destroy()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import io.github.wulkanowy.databinding.FragmentDashboardBinding
|
|||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
||||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog.Companion.CAPTCHA_SUCCESS
|
||||||
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
|
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
|
||||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
@ -36,6 +37,7 @@ import io.github.wulkanowy.utils.getErrorString
|
|||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import timber.log.Timber
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -62,6 +64,9 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt()
|
return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isViewEmpty
|
||||||
|
get() = dashboardAdapter.itemCount == 0
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance() = DashboardFragment()
|
fun newInstance() = DashboardFragment()
|
||||||
@ -77,6 +82,13 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentDashboardBinding.bind(view)
|
binding = FragmentDashboardBinding.bind(view)
|
||||||
presenter.onAttachView(this)
|
presenter.onAttachView(this)
|
||||||
|
initializeCaptchaResultObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeCaptchaResultObserver() {
|
||||||
|
childFragmentManager.setFragmentResultListener(CAPTCHA_SUCCESS, this) { _, _ ->
|
||||||
|
presenter.onRetryAfterCaptcha()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -239,6 +239,14 @@ class DashboardPresenter @Inject constructor(
|
|||||||
loadData(selectedDashboardTiles, forceRefresh = true)
|
loadData(selectedDashboardTiles, forceRefresh = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onRetryAfterCaptcha() {
|
||||||
|
view?.run {
|
||||||
|
showErrorView(false)
|
||||||
|
showProgress(true)
|
||||||
|
}
|
||||||
|
loadData(selectedDashboardTiles, forceRefresh = true)
|
||||||
|
}
|
||||||
|
|
||||||
fun onViewReselected() {
|
fun onViewReselected() {
|
||||||
Timber.i("Dashboard view is reselected")
|
Timber.i("Dashboard view is reselected")
|
||||||
view?.run {
|
view?.run {
|
||||||
@ -316,7 +324,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
) { luckyNumberResource, messageResource, attendanceResource ->
|
) { luckyNumberResource, messageResource, attendanceResource ->
|
||||||
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
|
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
|
||||||
|
|
||||||
DashboardItem.HorizontalGroup(
|
resList to DashboardItem.HorizontalGroup(
|
||||||
isLoading = resList.any { it is Resource.Loading },
|
isLoading = resList.any { it is Resource.Loading },
|
||||||
error = resList.map { it.errorOrNull }.let { errors ->
|
error = resList.map { it.errorOrNull }.let { errors ->
|
||||||
if (errors.all { it != null }) {
|
if (errors.all { it != null }) {
|
||||||
@ -341,9 +349,9 @@ class DashboardPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
.filterNot { it.isLoading && forceRefresh }
|
.filterNot { (_, it) -> it.isLoading && forceRefresh }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.onEach {
|
.onEach { (_, it) ->
|
||||||
updateData(it, forceRefresh)
|
updateData(it, forceRefresh)
|
||||||
|
|
||||||
if (it.isLoading) {
|
if (it.isLoading) {
|
||||||
@ -361,7 +369,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
errorHandler.dispatch(it)
|
errorHandler.dispatch(it)
|
||||||
}
|
}
|
||||||
.launch("horizontal_group ${if (forceRefresh) "-forceRefresh" else ""}")
|
.launchWithUniqueRefreshJob("horizontal_group", forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadGrades(student: Student, forceRefresh: Boolean) {
|
private fun loadGrades(student: Student, forceRefresh: Boolean) {
|
||||||
@ -854,6 +862,28 @@ class DashboardPresenter @Inject constructor(
|
|||||||
onEach {
|
onEach {
|
||||||
if (it is Resource.Success) {
|
if (it is Resource.Success) {
|
||||||
cancelJobs(jobName)
|
cancelJobs(jobName)
|
||||||
|
} else if (it is Resource.Error) {
|
||||||
|
cancelJobs(jobName)
|
||||||
|
}
|
||||||
|
}.launch(jobName)
|
||||||
|
} else {
|
||||||
|
launch(jobName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("launchWithUniqueRefreshJobHorizontalGroup")
|
||||||
|
private fun Flow<Pair<List<Resource<*>>, *>>.launchWithUniqueRefreshJob(
|
||||||
|
name: String,
|
||||||
|
forceRefresh: Boolean
|
||||||
|
) {
|
||||||
|
val jobName = if (forceRefresh) "$name-forceRefresh" else name
|
||||||
|
|
||||||
|
if (forceRefresh) {
|
||||||
|
onEach { (resources, _) ->
|
||||||
|
if (resources.all { it is Resource.Success<*> }) {
|
||||||
|
cancelJobs(jobName)
|
||||||
|
} else if (resources.any { it is Resource.Error<*> }) {
|
||||||
|
cancelJobs(jobName)
|
||||||
}
|
}
|
||||||
}.launch(jobName)
|
}.launch(jobName)
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,6 +6,8 @@ interface DashboardView : BaseView {
|
|||||||
|
|
||||||
val tileWidth: Int
|
val tileWidth: Int
|
||||||
|
|
||||||
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(data: List<DashboardItem>)
|
fun updateData(data: List<DashboardItem>)
|
||||||
@ -27,6 +29,5 @@ interface DashboardView : BaseView {
|
|||||||
fun popViewToRoot()
|
fun popViewToRoot()
|
||||||
|
|
||||||
fun openNotificationsCenterView()
|
fun openNotificationsCenterView()
|
||||||
|
|
||||||
fun openInternetBrowser(url: String)
|
fun openInternetBrowser(url: String)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.debug
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.webkit.CookieManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
@ -58,6 +59,10 @@ class DebugFragment : BaseFragment<FragmentDebugBinding>(R.layout.fragment_debug
|
|||||||
(activity as? MainActivity)?.pushView(NotificationDebugFragment.newInstance())
|
(activity as? MainActivity)?.pushView(NotificationDebugFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearWebkitCookies() {
|
||||||
|
CookieManager.getInstance().removeAllCookies(null)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -15,6 +15,7 @@ class DebugPresenter @Inject constructor(
|
|||||||
val items = listOf(
|
val items = listOf(
|
||||||
DebugItem(R.string.logviewer_title),
|
DebugItem(R.string.logviewer_title),
|
||||||
DebugItem(R.string.notification_debug_title),
|
DebugItem(R.string.notification_debug_title),
|
||||||
|
DebugItem(R.string.debug_cookies_clear),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun onAttachView(view: DebugView) {
|
override fun onAttachView(view: DebugView) {
|
||||||
@ -31,6 +32,7 @@ class DebugPresenter @Inject constructor(
|
|||||||
when (item.title) {
|
when (item.title) {
|
||||||
R.string.logviewer_title -> view?.openLogViewer()
|
R.string.logviewer_title -> view?.openLogViewer()
|
||||||
R.string.notification_debug_title -> view?.openNotificationsDebug()
|
R.string.notification_debug_title -> view?.openNotificationsDebug()
|
||||||
|
R.string.debug_cookies_clear -> view?.clearWebkitCookies()
|
||||||
else -> Timber.d("Unknown debug item: $item")
|
else -> Timber.d("Unknown debug item: $item")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,6 @@ interface DebugView : BaseView {
|
|||||||
fun openLogViewer()
|
fun openLogViewer()
|
||||||
|
|
||||||
fun openNotificationsDebug()
|
fun openNotificationsDebug()
|
||||||
|
|
||||||
|
fun clearWebkitCookies()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.view.View.GONE
|
|||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.widget.doOnTextChanged
|
import androidx.core.widget.doOnTextChanged
|
||||||
|
import androidx.fragment.app.setFragmentResultListener
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||||
@ -14,6 +15,7 @@ import io.github.wulkanowy.data.pojos.RegisterUser
|
|||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
|
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||||
@ -72,6 +74,13 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentLoginFormBinding.bind(view)
|
binding = FragmentLoginFormBinding.bind(view)
|
||||||
presenter.onAttachView(this)
|
presenter.onAttachView(this)
|
||||||
|
initializeCaptchaResultObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeCaptchaResultObserver() {
|
||||||
|
setFragmentResultListener(CaptchaDialog.CAPTCHA_SUCCESS) { _, _ ->
|
||||||
|
presenter.onRetryAfterCaptcha()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
|
@ -152,6 +152,10 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onRetryAfterCaptcha() {
|
||||||
|
onSignInClick()
|
||||||
|
}
|
||||||
|
|
||||||
fun onSignInClick() {
|
fun onSignInClick() {
|
||||||
val loginData = getLoginData()
|
val loginData = getLoginData()
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
@ -30,6 +31,8 @@ import io.github.wulkanowy.databinding.ActivityMainBinding
|
|||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.modules.Destination
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
|
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
|
||||||
|
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||||
|
import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog
|
||||||
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
|
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
@ -40,10 +43,17 @@ import io.github.wulkanowy.utils.dpToPx
|
|||||||
import io.github.wulkanowy.utils.nickOrName
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import io.github.wulkanowy.utils.safelyPopFragments
|
import io.github.wulkanowy.utils.safelyPopFragments
|
||||||
import io.github.wulkanowy.utils.setOnViewChangeListener
|
import io.github.wulkanowy.utils.setOnViewChangeListener
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainView,
|
class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainView,
|
||||||
@ -73,6 +83,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
private val navController =
|
private val navController =
|
||||||
FragNavController(supportFragmentManager, R.id.main_fragment_container)
|
FragNavController(supportFragmentManager, R.id.main_fragment_container)
|
||||||
|
|
||||||
|
private val captchaVerificationEvent = MutableSharedFlow<String?>()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val EXTRA_START_DESTINATION = "start_destination_json"
|
private const val EXTRA_START_DESTINATION = "start_destination_json"
|
||||||
@ -144,6 +156,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
initializeToolbar()
|
initializeToolbar()
|
||||||
initializeBottomNavigation(startMenuIndex, rootAppMenuItems)
|
initializeBottomNavigation(startMenuIndex, rootAppMenuItems)
|
||||||
initializeNavController(startMenuIndex, rootUpdatedDestinations)
|
initializeNavController(startMenuIndex, rootUpdatedDestinations)
|
||||||
|
initializeCaptchaVerificationEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeNavController(
|
private fun initializeNavController(
|
||||||
@ -323,6 +336,27 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
|
private fun initializeCaptchaVerificationEvent() {
|
||||||
|
captchaVerificationEvent
|
||||||
|
.debounce(1.seconds)
|
||||||
|
.onEach { url ->
|
||||||
|
Timber.d("Showing captcha dialog for: $url")
|
||||||
|
showDialogFragment(CaptchaDialog.newInstance(url))
|
||||||
|
}
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
captchaVerificationEvent.emit(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showAuthDialog() {
|
||||||
|
showDialogFragment(AuthDialog.newInstance())
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
navController.onSaveInstanceState(outState)
|
navController.onSaveInstanceState(outState)
|
||||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.settings
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, Settin
|
|||||||
|
|
||||||
override fun showExpiredCredentialsDialog() {}
|
override fun showExpiredCredentialsDialog() {}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) = Unit
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {}
|
override fun showDecryptionFailedDialog() {}
|
||||||
|
|
||||||
override fun openClearLoginView() {}
|
override fun openClearLoginView() {}
|
||||||
|
@ -8,7 +8,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.base.ErrorDialog
|
import io.github.wulkanowy.ui.base.ErrorDialog
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -51,6 +50,10 @@ class AdvancedFragment : PreferenceFragmentCompat(),
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
@ -68,7 +71,7 @@ class AdvancedFragment : PreferenceFragmentCompat(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -9,7 +9,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.base.ErrorDialog
|
import io.github.wulkanowy.ui.base.ErrorDialog
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -67,6 +66,10 @@ class AppearanceFragment : PreferenceFragmentCompat(),
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
@ -84,7 +87,7 @@ class AppearanceFragment : PreferenceFragmentCompat(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -21,7 +21,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.base.ErrorDialog
|
import io.github.wulkanowy.ui.base.ErrorDialog
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
@ -137,6 +136,10 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
@ -154,7 +157,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showFixSyncDialog() {
|
override fun showFixSyncDialog() {
|
||||||
|
@ -10,7 +10,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.base.BaseActivity
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.base.ErrorDialog
|
import io.github.wulkanowy.ui.base.ErrorDialog
|
||||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -88,6 +87,10 @@ class SyncFragment : PreferenceFragmentCompat(),
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
@ -105,7 +108,7 @@ class SyncFragment : PreferenceFragmentCompat(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showAuthDialog() {
|
override fun showAuthDialog() {
|
||||||
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
|
(activity as? BaseActivity<*, *>)?.showAuthDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
|
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
|
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
|
||||||
@ -34,6 +35,7 @@ fun Resources.getErrorString(error: Throwable): String = when (error) {
|
|||||||
is FeatureNotAvailableException -> R.string.error_feature_not_available
|
is FeatureNotAvailableException -> R.string.error_feature_not_available
|
||||||
is VulcanException -> R.string.error_unknown_uonet
|
is VulcanException -> R.string.error_unknown_uonet
|
||||||
is ScrapperException -> R.string.error_unknown_app
|
is ScrapperException -> R.string.error_unknown_app
|
||||||
|
is CloudflareVerificationException -> R.string.error_cloudflare_captcha
|
||||||
is SSLHandshakeException -> when {
|
is SSLHandshakeException -> when {
|
||||||
error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime
|
error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime
|
||||||
else -> R.string.error_timeout
|
else -> R.string.error_timeout
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import java.net.CookiePolicy
|
||||||
|
import java.net.CookieStore
|
||||||
|
import java.net.HttpCookie
|
||||||
|
import java.net.URI
|
||||||
|
import android.webkit.CookieManager as WebkitCookieManager
|
||||||
|
import java.net.CookieManager as JavaCookieManager
|
||||||
|
|
||||||
|
class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL) {
|
||||||
|
|
||||||
|
private val webkitCookieManager: WebkitCookieManager = WebkitCookieManager.getInstance()
|
||||||
|
|
||||||
|
override fun put(uri: URI?, responseHeaders: Map<String?, List<String?>>?) {
|
||||||
|
if (uri == null || responseHeaders == null) return
|
||||||
|
val url = uri.toString()
|
||||||
|
for (headerKey in responseHeaders.keys) {
|
||||||
|
if (headerKey == null || !(
|
||||||
|
headerKey.equals("Set-Cookie2", ignoreCase = true) ||
|
||||||
|
headerKey.equals("Set-Cookie", ignoreCase = true)
|
||||||
|
)
|
||||||
|
) continue
|
||||||
|
|
||||||
|
// process each of the headers
|
||||||
|
for (headerValue in responseHeaders[headerKey].orEmpty()) {
|
||||||
|
webkitCookieManager.setCookie(url, headerValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun get(
|
||||||
|
uri: URI?,
|
||||||
|
requestHeaders: Map<String?, List<String?>?>?
|
||||||
|
): Map<String, List<String>> {
|
||||||
|
require(!(uri == null || requestHeaders == null)) { "Argument is null" }
|
||||||
|
val res = mutableMapOf<String, List<String>>()
|
||||||
|
val cookie = webkitCookieManager.getCookie(uri.toString())
|
||||||
|
if (cookie != null) res["Cookie"] = listOf(cookie)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCookieStore(): CookieStore {
|
||||||
|
val cookies = super.getCookieStore()
|
||||||
|
return object : CookieStore {
|
||||||
|
override fun add(uri: URI?, cookie: HttpCookie?) = cookies.add(uri, cookie)
|
||||||
|
override fun get(uri: URI?): List<HttpCookie> = cookies.get(uri)
|
||||||
|
override fun getCookies(): List<HttpCookie> = cookies.cookies
|
||||||
|
override fun getURIs(): List<URI> = cookies.urIs
|
||||||
|
override fun remove(uri: URI?, cookie: HttpCookie?): Boolean =
|
||||||
|
cookies.remove(uri, cookie)
|
||||||
|
|
||||||
|
override fun removeAll(): Boolean {
|
||||||
|
webkitCookieManager.removeAllCookies(null)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
Wersja 2.3.3
|
Wersja 2.3.4
|
||||||
|
|
||||||
— poprawiliśmy kolejne usterki przy odświeżaniu danych (teraz to powinno działać już dużo lepiej)
|
— dodaliśmy obsługę captchy, co umożliwi używanie apki np. na odmianie ResMan Rzeszów
|
||||||
|
— naprawiliśmy wyświetlanie frekwencji w szkołach używających eduOne (piszcie, jeśli nadal nie działa)
|
||||||
|
|
||||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||||
|
51
app/src/main/res/layout/dialog_captcha.xml
Normal file
51
app/src/main/res/layout/dialog_captcha.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minWidth="350dp"
|
||||||
|
tools:context=".ui.modules.captcha.CaptchaDialog">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/captcha_dialog_title"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/captcha_close"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/captcha_refresh"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/captcha_refresh"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:contentDescription="@string/logviewer_refresh"
|
||||||
|
app:icon="@drawable/ic_refresh"
|
||||||
|
app:iconTint="?colorOnSurface"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/captcha_close"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/captcha_close"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:contentDescription="@string/all_close"
|
||||||
|
app:icon="@drawable/ic_all_close_circle"
|
||||||
|
app:iconTint="?colorOnSurface"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/captcha_webview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/captcha_close" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Prohlížeč protokolů</string>
|
<string name="logviewer_title">Prohlížeč protokolů</string>
|
||||||
<string name="debug_title">Ladění</string>
|
<string name="debug_title">Ladění</string>
|
||||||
<string name="notification_debug_title">Ladění oznámení</string>
|
<string name="notification_debug_title">Ladění oznámení</string>
|
||||||
|
<string name="debug_cookies_clear">Vymazat soubory cookie webview</string>
|
||||||
<string name="contributors_title">Tvůrci</string>
|
<string name="contributors_title">Tvůrci</string>
|
||||||
<string name="license_title">Licence</string>
|
<string name="license_title">Licence</string>
|
||||||
<string name="message_title">Zprávy</string>
|
<string name="message_title">Zprávy</string>
|
||||||
@ -833,6 +834,9 @@
|
|||||||
<string name="auth_title">Autorizace</string>
|
<string name="auth_title">Autorizace</string>
|
||||||
<string name="auth_description">Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli</string>
|
<string name="auth_description">Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli</string>
|
||||||
<string name="auth_button_skip">Zatím přeskočit</string>
|
<string name="auth_button_skip">Zatím přeskočit</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Probíhá ověřování. Počkejte…</string>
|
||||||
|
<string name="captcha_verified_message">Úspěšně ověřeno</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Žádné internetové připojení</string>
|
<string name="error_no_internet">Žádné internetové připojení</string>
|
||||||
<string name="error_invalid_device_datetime">Vyskytla se chyba. Zkontrolujte hodiny svého zařízení</string>
|
<string name="error_invalid_device_datetime">Vyskytla se chyba. Zkontrolujte hodiny svého zařízení</string>
|
||||||
@ -842,6 +846,7 @@
|
|||||||
<string name="error_service_unavailable">Probíhá údržba deníku UONET+. Zkuste to později znovu</string>
|
<string name="error_service_unavailable">Probíhá údržba deníku UONET+. Zkuste to později znovu</string>
|
||||||
<string name="error_unknown_uonet">Neznámá chyba deniku UONET+. Prosím zkuste to znovu později</string>
|
<string name="error_unknown_uonet">Neznámá chyba deniku UONET+. Prosím zkuste to znovu později</string>
|
||||||
<string name="error_unknown_app">Neznámá chyba aplikace. Prosím zkuste to znovu později</string>
|
<string name="error_unknown_app">Neznámá chyba aplikace. Prosím zkuste to znovu později</string>
|
||||||
|
<string name="error_cloudflare_captcha">Vyžadováno ověření Captcha</string>
|
||||||
<string name="error_unknown">Vyskytla se neočekávaná chyba</string>
|
<string name="error_unknown">Vyskytla se neočekávaná chyba</string>
|
||||||
<string name="error_feature_disabled">Funkce je deaktivována přes vaší školou</string>
|
<string name="error_feature_disabled">Funkce je deaktivována přes vaší školou</string>
|
||||||
<string name="error_feature_not_available">Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API</string>
|
<string name="error_feature_not_available">Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Log viewer</string>
|
<string name="logviewer_title">Log viewer</string>
|
||||||
<string name="debug_title">Debug</string>
|
<string name="debug_title">Debug</string>
|
||||||
<string name="notification_debug_title">Notification debug</string>
|
<string name="notification_debug_title">Notification debug</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Contributors</string>
|
<string name="contributors_title">Contributors</string>
|
||||||
<string name="license_title">Licenses</string>
|
<string name="license_title">Licenses</string>
|
||||||
<string name="message_title">Messages</string>
|
<string name="message_title">Messages</string>
|
||||||
@ -743,6 +744,9 @@
|
|||||||
<string name="auth_title">Authorization</string>
|
<string name="auth_title">Authorization</string>
|
||||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||||
<string name="auth_button_skip">Skip for now</string>
|
<string name="auth_button_skip">Skip for now</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">No internet connection</string>
|
<string name="error_no_internet">No internet connection</string>
|
||||||
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
||||||
@ -752,6 +756,7 @@
|
|||||||
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
||||||
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
||||||
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">An unexpected error occurred</string>
|
<string name="error_unknown">An unexpected error occurred</string>
|
||||||
<string name="error_feature_disabled">Feature disabled by your school</string>
|
<string name="error_feature_disabled">Feature disabled by your school</string>
|
||||||
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Log Viewer</string>
|
<string name="logviewer_title">Log Viewer</string>
|
||||||
<string name="debug_title">Debuggen</string>
|
<string name="debug_title">Debuggen</string>
|
||||||
<string name="notification_debug_title">Benachrichtigungen debuggen</string>
|
<string name="notification_debug_title">Benachrichtigungen debuggen</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Mitarbeiter</string>
|
<string name="contributors_title">Mitarbeiter</string>
|
||||||
<string name="license_title">Lizenzen</string>
|
<string name="license_title">Lizenzen</string>
|
||||||
<string name="message_title">Nachrichten</string>
|
<string name="message_title">Nachrichten</string>
|
||||||
@ -743,6 +744,9 @@
|
|||||||
<string name="auth_title">Authorization</string>
|
<string name="auth_title">Authorization</string>
|
||||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||||
<string name="auth_button_skip">Skip for now</string>
|
<string name="auth_button_skip">Skip for now</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Keine Internetverbindung</string>
|
<string name="error_no_internet">Keine Internetverbindung</string>
|
||||||
<string name="error_invalid_device_datetime">Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr</string>
|
<string name="error_invalid_device_datetime">Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr</string>
|
||||||
@ -752,6 +756,7 @@
|
|||||||
<string name="error_service_unavailable">Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal</string>
|
<string name="error_service_unavailable">Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal</string>
|
||||||
<string name="error_unknown_uonet">Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut</string>
|
<string name="error_unknown_uonet">Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut</string>
|
||||||
<string name="error_unknown_app">Unbekannter Anwendungsfehler. Bitte versuchen Sie es später noch einmal</string>
|
<string name="error_unknown_app">Unbekannter Anwendungsfehler. Bitte versuchen Sie es später noch einmal</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">Ein unerwarteter Fehler ist aufgetreten</string>
|
<string name="error_unknown">Ein unerwarteter Fehler ist aufgetreten</string>
|
||||||
<string name="error_feature_disabled">Funktion, die von Ihrer Schule deaktiviert wurde</string>
|
<string name="error_feature_disabled">Funktion, die von Ihrer Schule deaktiviert wurde</string>
|
||||||
<string name="error_feature_not_available">Feature in diesem Modus nicht verfügbar</string>
|
<string name="error_feature_not_available">Feature in diesem Modus nicht verfügbar</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Log viewer</string>
|
<string name="logviewer_title">Log viewer</string>
|
||||||
<string name="debug_title">Debug</string>
|
<string name="debug_title">Debug</string>
|
||||||
<string name="notification_debug_title">Notification debug</string>
|
<string name="notification_debug_title">Notification debug</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Contributors</string>
|
<string name="contributors_title">Contributors</string>
|
||||||
<string name="license_title">Licenses</string>
|
<string name="license_title">Licenses</string>
|
||||||
<string name="message_title">Messages</string>
|
<string name="message_title">Messages</string>
|
||||||
@ -743,6 +744,9 @@
|
|||||||
<string name="auth_title">Authorization</string>
|
<string name="auth_title">Authorization</string>
|
||||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||||
<string name="auth_button_skip">Skip for now</string>
|
<string name="auth_button_skip">Skip for now</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">No internet connection</string>
|
<string name="error_no_internet">No internet connection</string>
|
||||||
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
||||||
@ -752,6 +756,7 @@
|
|||||||
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
||||||
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
||||||
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">An unexpected error occurred</string>
|
<string name="error_unknown">An unexpected error occurred</string>
|
||||||
<string name="error_feature_disabled">Feature disabled by your school</string>
|
<string name="error_feature_disabled">Feature disabled by your school</string>
|
||||||
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Log viewer</string>
|
<string name="logviewer_title">Log viewer</string>
|
||||||
<string name="debug_title">Debug</string>
|
<string name="debug_title">Debug</string>
|
||||||
<string name="notification_debug_title">Notification debug</string>
|
<string name="notification_debug_title">Notification debug</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Contributors</string>
|
<string name="contributors_title">Contributors</string>
|
||||||
<string name="license_title">Licenses</string>
|
<string name="license_title">Licenses</string>
|
||||||
<string name="message_title">Messages</string>
|
<string name="message_title">Messages</string>
|
||||||
@ -743,6 +744,9 @@
|
|||||||
<string name="auth_title">Authorization</string>
|
<string name="auth_title">Authorization</string>
|
||||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||||
<string name="auth_button_skip">Skip for now</string>
|
<string name="auth_button_skip">Skip for now</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">No internet connection</string>
|
<string name="error_no_internet">No internet connection</string>
|
||||||
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
||||||
@ -752,6 +756,7 @@
|
|||||||
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
||||||
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
||||||
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">An unexpected error occurred</string>
|
<string name="error_unknown">An unexpected error occurred</string>
|
||||||
<string name="error_feature_disabled">Feature disabled by your school</string>
|
<string name="error_feature_disabled">Feature disabled by your school</string>
|
||||||
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Przeglądarka logów</string>
|
<string name="logviewer_title">Przeglądarka logów</string>
|
||||||
<string name="debug_title">Debugowanie</string>
|
<string name="debug_title">Debugowanie</string>
|
||||||
<string name="notification_debug_title">Debugowanie powiadomień</string>
|
<string name="notification_debug_title">Debugowanie powiadomień</string>
|
||||||
|
<string name="debug_cookies_clear">Wyczyść ciasteczka webview</string>
|
||||||
<string name="contributors_title">Twórcy</string>
|
<string name="contributors_title">Twórcy</string>
|
||||||
<string name="license_title">Licencje</string>
|
<string name="license_title">Licencje</string>
|
||||||
<string name="message_title">Wiadomości</string>
|
<string name="message_title">Wiadomości</string>
|
||||||
@ -833,6 +834,9 @@
|
|||||||
<string name="auth_title">Autoryzacja</string>
|
<string name="auth_title">Autoryzacja</string>
|
||||||
<string name="auth_description">Rodzicu, musimy mieć pewność, że Twój adres e-mail został powiązany z prawidłowym kontem ucznia. W celu autoryzacji konta podaj numer PESEL ucznia <b>%1$s</b> w polu poniżej</string>
|
<string name="auth_description">Rodzicu, musimy mieć pewność, że Twój adres e-mail został powiązany z prawidłowym kontem ucznia. W celu autoryzacji konta podaj numer PESEL ucznia <b>%1$s</b> w polu poniżej</string>
|
||||||
<string name="auth_button_skip">Na razie pomiń</string>
|
<string name="auth_button_skip">Na razie pomiń</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Trwa weryfikacja. Czekaj…</string>
|
||||||
|
<string name="captcha_verified_message">Pomyślnie zweryfikowano</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Brak połączenia z internetem</string>
|
<string name="error_no_internet">Brak połączenia z internetem</string>
|
||||||
<string name="error_invalid_device_datetime">Wystąpił błąd. Sprawdź poprawność daty w urządzeniu</string>
|
<string name="error_invalid_device_datetime">Wystąpił błąd. Sprawdź poprawność daty w urządzeniu</string>
|
||||||
@ -842,6 +846,7 @@
|
|||||||
<string name="error_service_unavailable">Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później</string>
|
<string name="error_service_unavailable">Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później</string>
|
||||||
<string name="error_unknown_uonet">Nieznany błąd dziennika UONET+. Spróbuj ponownie później</string>
|
<string name="error_unknown_uonet">Nieznany błąd dziennika UONET+. Spróbuj ponownie później</string>
|
||||||
<string name="error_unknown_app">Nieznany błąd aplikacji. Spróbuj ponownie później</string>
|
<string name="error_unknown_app">Nieznany błąd aplikacji. Spróbuj ponownie później</string>
|
||||||
|
<string name="error_cloudflare_captcha">Wymagana weryfikacja captcha</string>
|
||||||
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
|
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
|
||||||
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
|
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
|
||||||
<string name="error_feature_not_available">Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API</string>
|
<string name="error_feature_not_available">Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Просмотр журнала</string>
|
<string name="logviewer_title">Просмотр журнала</string>
|
||||||
<string name="debug_title">Отладка</string>
|
<string name="debug_title">Отладка</string>
|
||||||
<string name="notification_debug_title">Отладка уведомлений</string>
|
<string name="notification_debug_title">Отладка уведомлений</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Разработчики</string>
|
<string name="contributors_title">Разработчики</string>
|
||||||
<string name="license_title">Лицензии</string>
|
<string name="license_title">Лицензии</string>
|
||||||
<string name="message_title">Сообщения</string>
|
<string name="message_title">Сообщения</string>
|
||||||
@ -833,6 +834,9 @@
|
|||||||
<string name="auth_title">Авторизация</string>
|
<string name="auth_title">Авторизация</string>
|
||||||
<string name="auth_description">Для работы приложения нам необходимо подтвердить вашу личность. Введите PESEL учащегося <b>%1$s</b> в поле ниже</string>
|
<string name="auth_description">Для работы приложения нам необходимо подтвердить вашу личность. Введите PESEL учащегося <b>%1$s</b> в поле ниже</string>
|
||||||
<string name="auth_button_skip">Пропустить сейчас</string>
|
<string name="auth_button_skip">Пропустить сейчас</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Интернет-соединение отсутствует</string>
|
<string name="error_no_internet">Интернет-соединение отсутствует</string>
|
||||||
<string name="error_invalid_device_datetime">Произошла ошибка. Проверьте время на вашем устройстве</string>
|
<string name="error_invalid_device_datetime">Произошла ошибка. Проверьте время на вашем устройстве</string>
|
||||||
@ -842,6 +846,7 @@
|
|||||||
<string name="error_service_unavailable">UONET+ проводит техническое обслуживание, повторите попытку позже</string>
|
<string name="error_service_unavailable">UONET+ проводит техническое обслуживание, повторите попытку позже</string>
|
||||||
<string name="error_unknown_uonet">Неизвестная ошибка дневника UONET+, повторите попытку позже</string>
|
<string name="error_unknown_uonet">Неизвестная ошибка дневника UONET+, повторите попытку позже</string>
|
||||||
<string name="error_unknown_app">Неизвестная ошибка приложения, повторите попытку позже</string>
|
<string name="error_unknown_app">Неизвестная ошибка приложения, повторите попытку позже</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">Произошла непредвиденная ошибка</string>
|
<string name="error_unknown">Произошла непредвиденная ошибка</string>
|
||||||
<string name="error_feature_disabled">Функция отключена вашей школой</string>
|
<string name="error_feature_disabled">Функция отключена вашей школой</string>
|
||||||
<string name="error_feature_not_available">Функция недоступна в режиме Mobile API. Воспользуйтесь другим режимом</string>
|
<string name="error_feature_not_available">Функция недоступна в режиме Mobile API. Воспользуйтесь другим режимом</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Prehliadač protokolov</string>
|
<string name="logviewer_title">Prehliadač protokolov</string>
|
||||||
<string name="debug_title">Ladenie</string>
|
<string name="debug_title">Ladenie</string>
|
||||||
<string name="notification_debug_title">Ladenie oznámení</string>
|
<string name="notification_debug_title">Ladenie oznámení</string>
|
||||||
|
<string name="debug_cookies_clear">Vymazať súbory cookie webview</string>
|
||||||
<string name="contributors_title">Tvorcovia</string>
|
<string name="contributors_title">Tvorcovia</string>
|
||||||
<string name="license_title">Licencie</string>
|
<string name="license_title">Licencie</string>
|
||||||
<string name="message_title">Správy</string>
|
<string name="message_title">Správy</string>
|
||||||
@ -833,6 +834,9 @@
|
|||||||
<string name="auth_title">Autorizácia</string>
|
<string name="auth_title">Autorizácia</string>
|
||||||
<string name="auth_description">Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli</string>
|
<string name="auth_description">Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli</string>
|
||||||
<string name="auth_button_skip">Zatiaľ preskočiť</string>
|
<string name="auth_button_skip">Zatiaľ preskočiť</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Overovanie prebieha. Počkajte…</string>
|
||||||
|
<string name="captcha_verified_message">Úspešne overené</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Žiadne internetové pripojenie</string>
|
<string name="error_no_internet">Žiadne internetové pripojenie</string>
|
||||||
<string name="error_invalid_device_datetime">Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia</string>
|
<string name="error_invalid_device_datetime">Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia</string>
|
||||||
@ -842,6 +846,7 @@
|
|||||||
<string name="error_service_unavailable">Prebieha údržba denníka UONET+. Skúste to neskôr znova</string>
|
<string name="error_service_unavailable">Prebieha údržba denníka UONET+. Skúste to neskôr znova</string>
|
||||||
<string name="error_unknown_uonet">Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr</string>
|
<string name="error_unknown_uonet">Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr</string>
|
||||||
<string name="error_unknown_app">Neznáma chyba aplikácie. Prosím skúste to znova neskôr</string>
|
<string name="error_unknown_app">Neznáma chyba aplikácie. Prosím skúste to znova neskôr</string>
|
||||||
|
<string name="error_cloudflare_captcha">Vyžaduje sa overenie Captcha</string>
|
||||||
<string name="error_unknown">Vyskytla sa neočakávaná chyba</string>
|
<string name="error_unknown">Vyskytla sa neočakávaná chyba</string>
|
||||||
<string name="error_feature_disabled">Funkcia je deaktivovaná cez vašou školou</string>
|
<string name="error_feature_disabled">Funkcia je deaktivovaná cez vašou školou</string>
|
||||||
<string name="error_feature_not_available">Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API</string>
|
<string name="error_feature_not_available">Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<string name="logviewer_title">Переглядач логів</string>
|
<string name="logviewer_title">Переглядач логів</string>
|
||||||
<string name="debug_title">Відладка</string>
|
<string name="debug_title">Відладка</string>
|
||||||
<string name="notification_debug_title">Відладка сповіщень</string>
|
<string name="notification_debug_title">Відладка сповіщень</string>
|
||||||
|
<string name="debug_cookies_clear">Очистити кукі веб - перегляду</string>
|
||||||
<string name="contributors_title">Розробники</string>
|
<string name="contributors_title">Розробники</string>
|
||||||
<string name="license_title">Ліцензії</string>
|
<string name="license_title">Ліцензії</string>
|
||||||
<string name="message_title">Листи</string>
|
<string name="message_title">Листи</string>
|
||||||
@ -833,6 +834,9 @@
|
|||||||
<string name="auth_title">Авторизувати</string>
|
<string name="auth_title">Авторизувати</string>
|
||||||
<string name="auth_description">Для роботи програми нам потрібно підтвердити вашу особу. Будь ласка, введіть число PESEL <b>%1$s</b> студента в поле нижче</string>
|
<string name="auth_description">Для роботи програми нам потрібно підтвердити вашу особу. Будь ласка, введіть число PESEL <b>%1$s</b> студента в поле нижче</string>
|
||||||
<string name="auth_button_skip">Поки що пропустити</string>
|
<string name="auth_button_skip">Поки що пропустити</string>
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Верифікація в процесі. Чекайте…</string>
|
||||||
|
<string name="captcha_verified_message">Верифікація завершена</string>
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">Немає з\'єднання з інтернетом</string>
|
<string name="error_no_internet">Немає з\'єднання з інтернетом</string>
|
||||||
<string name="error_invalid_device_datetime">Сталася помилка. Перевірте годинник пристрою</string>
|
<string name="error_invalid_device_datetime">Сталася помилка. Перевірте годинник пристрою</string>
|
||||||
@ -842,6 +846,7 @@
|
|||||||
<string name="error_service_unavailable">UONET+ проводить технічне осблуговування, спробуйте пізніше</string>
|
<string name="error_service_unavailable">UONET+ проводить технічне осблуговування, спробуйте пізніше</string>
|
||||||
<string name="error_unknown_uonet">Невідома помилка щоденника UONET+, спробуйте пізніше</string>
|
<string name="error_unknown_uonet">Невідома помилка щоденника UONET+, спробуйте пізніше</string>
|
||||||
<string name="error_unknown_app">Невідома помилка програми, спробуйте пізніше</string>
|
<string name="error_unknown_app">Невідома помилка програми, спробуйте пізніше</string>
|
||||||
|
<string name="error_cloudflare_captcha">Необхідна перевірка Captcha</string>
|
||||||
<string name="error_unknown">Відбулася несподівана помилка</string>
|
<string name="error_unknown">Відбулася несподівана помилка</string>
|
||||||
<string name="error_feature_disabled">Функція вимкнена вашою школою</string>
|
<string name="error_feature_disabled">Функція вимкнена вашою школою</string>
|
||||||
<string name="error_feature_not_available">Функція недоступна в режимі Mobile API. Увійдіть в інший режим</string>
|
<string name="error_feature_not_available">Функція недоступна в режимі Mobile API. Увійдіть в інший режим</string>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<string name="logviewer_title">Log viewer</string>
|
<string name="logviewer_title">Log viewer</string>
|
||||||
<string name="debug_title">Debug</string>
|
<string name="debug_title">Debug</string>
|
||||||
<string name="notification_debug_title">Notification debug</string>
|
<string name="notification_debug_title">Notification debug</string>
|
||||||
|
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||||
<string name="contributors_title">Contributors</string>
|
<string name="contributors_title">Contributors</string>
|
||||||
<string name="license_title">Licenses</string>
|
<string name="license_title">Licenses</string>
|
||||||
<string name="message_title">Messages</string>
|
<string name="message_title">Messages</string>
|
||||||
@ -833,6 +834,11 @@
|
|||||||
<string name="auth_button_skip">Skip for now</string>
|
<string name="auth_button_skip">Skip for now</string>
|
||||||
|
|
||||||
|
|
||||||
|
<!--Captcha-->
|
||||||
|
<string name="captcha_dialog_title">Verification is in progress. Wait…</string>
|
||||||
|
<string name="captcha_verified_message">Verified successfully</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
<string name="error_no_internet">No internet connection</string>
|
<string name="error_no_internet">No internet connection</string>
|
||||||
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
<string name="error_invalid_device_datetime">An error occurred. Check your device clock</string>
|
||||||
@ -842,6 +848,7 @@
|
|||||||
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
<string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
|
||||||
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
<string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
|
||||||
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
<string name="error_unknown_app">Unknown application error. Please try again later</string>
|
||||||
|
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||||
<string name="error_unknown">An unexpected error occurred</string>
|
<string name="error_unknown">An unexpected error occurred</string>
|
||||||
<string name="error_feature_disabled">Feature disabled by your school</string>
|
<string name="error_feature_disabled">Feature disabled by your school</string>
|
||||||
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
<string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>
|
||||||
|
@ -105,6 +105,10 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
|
|||||||
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
(activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCaptchaVerificationRequired(url: String?) {
|
||||||
|
(activity as? BaseActivity<*, *>)?.onCaptchaVerificationRequired(url)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
(activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user