Merge branch 'release/2.0.1'

This commit is contained in:
Mikołaj Pich 2023-05-12 00:21:28 +02:00
commit 9697a39464
27 changed files with 468 additions and 15 deletions

View File

@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 33 targetSdkVersion 33
versionCode 122 versionCode 123
versionName "2.0.0" versionName "2.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
@ -161,7 +161,7 @@ play {
defaultToAppBundles = false defaultToAppBundles = false
track = 'production' track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
userFraction = 0.50d userFraction = 0.10d
updatePriority = 2 updatePriority = 2
enabled.set(false) enabled.set(false)
} }
@ -186,7 +186,7 @@ ext {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:2.0.0" implementation 'io.github.wulkanowy:sdk:2.0.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.* import androidx.room.*
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentName
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import javax.inject.Singleton import javax.inject.Singleton
@ -19,6 +20,9 @@ abstract class StudentDao {
@Update(entity = Student::class) @Update(entity = Student::class)
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar) abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
@Update(entity = Student::class)
abstract suspend fun update(studentName: StudentName)
@Query("SELECT * FROM Students WHERE is_current = 1") @Query("SELECT * FROM Students WHERE is_current = 1")
abstract suspend fun loadCurrent(): Student? abstract suspend fun loadCurrent(): Student?

View File

@ -0,0 +1,18 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity
data class StudentName(
@ColumnInfo(name = "student_name")
val studentName: String
) : Serializable {
@PrimaryKey
var id: Long = 0
}

View File

@ -6,7 +6,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentName
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
@ -14,6 +16,7 @@ import io.github.wulkanowy.data.mappers.mapToPojo
import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.security.decrypt import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt import io.github.wulkanowy.utils.security.encrypt
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -146,4 +149,21 @@ class StudentRepository @Inject constructor(
suspend fun isOneUniqueStudent() = getSavedStudents(false) suspend fun isOneUniqueStudent() = getSavedStudents(false)
.distinctBy { it.student.studentName }.size == 1 .distinctBy { it.student.studentName }.size == 1
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.authorizePermission(pesel)
suspend fun refreshStudentName(student: Student, semester: Semester) {
val newCurrentApiStudent = sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getCurrentStudent() ?: return
val studentName = StudentName(
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
).apply { id = student.id }
studentDb.update(studentName)
}
} }

View File

@ -10,6 +10,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
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.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
@ -76,6 +77,10 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
.show() .show()
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(supportFragmentManager, "auth_dialog")
}
override fun showChangePasswordSnackbar(redirectUrl: String) { override fun showChangePasswordSnackbar(redirectUrl: String) {
messageContainer?.let { messageContainer?.let {
Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG) Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG)

View File

@ -5,10 +5,10 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.annotation.CallSuper
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
@ -40,17 +40,19 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
(activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl)
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun showErrorDetailsDialog(error: Throwable) { override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
@CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext())) view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext()))
} }
@CallSuper
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,

View File

@ -7,6 +7,7 @@ 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),
@ -42,6 +43,10 @@ abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : Fragme
(activity as? BaseActivity<*, *>)?.showExpiredDialog() (activity as? BaseActivity<*, *>)?.showExpiredDialog()
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun openClearLoginView() { override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView() (activity as? BaseActivity<*, *>)?.openClearLoginView()
} }

View File

@ -1,10 +1,15 @@
package io.github.wulkanowy.ui.base package io.github.wulkanowy.ui.base
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
open class BasePresenter<T : BaseView>( open class BasePresenter<T : BaseView>(
@ -26,6 +31,7 @@ open class BasePresenter<T : BaseView>(
onSessionExpired = view::showExpiredDialog onSessionExpired = view::showExpiredDialog
onNoCurrentStudent = view::openClearLoginView onNoCurrentStudent = view::openClearLoginView
onPasswordChangeRequired = view::showChangePasswordSnackbar onPasswordChangeRequired = view::showChangePasswordSnackbar
onAuthorizationRequired = view::showAuthDialog
} }
} }

View File

@ -8,6 +8,8 @@ interface BaseView {
fun showExpiredDialog() fun showExpiredDialog()
fun showAuthDialog()
fun openClearLoginView() fun openClearLoginView()
fun showErrorDetailsDialog(error: Throwable) fun showErrorDetailsDialog(error: Throwable)

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.base
import android.content.Context 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.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
@ -20,6 +21,8 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
var onPasswordChangeRequired: (String) -> Unit = {} var onPasswordChangeRequired: (String) -> Unit = {}
var onAuthorizationRequired: () -> 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)
@ -31,6 +34,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
is ScramblerException, is BadCredentialsException -> onSessionExpired() is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent() is NoCurrentStudentException -> onNoCurrentStudent()
is AuthorizationRequiredException -> onAuthorizationRequired()
} }
} }
@ -39,5 +43,6 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
onSessionExpired = {} onSessionExpired = {}
onNoCurrentStudent = {} onNoCurrentStudent = {}
onPasswordChangeRequired = {} onPasswordChangeRequired = {}
onAuthorizationRequired = {}
} }
} }

View File

@ -0,0 +1,81 @@
package io.github.wulkanowy.ui.modules.auth
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogAuthBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
import javax.inject.Inject
@AndroidEntryPoint
class AuthDialog : BaseDialogFragment<DialogAuthBinding>(), AuthView {
@Inject
lateinit var presenter: AuthPresenter
companion object {
fun newInstance() = AuthDialog()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.FullScreenDialogStyle)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return DialogAuthBinding.inflate(inflater).apply { binding = this }.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
presenter.onAttachView(this)
binding.authInput.doOnTextChanged { text, _, _, _ ->
presenter.onPeselChange(text?.toString())
}
binding.authButton.setOnClickListener { presenter.authorize() }
binding.authSuccessButton.setOnClickListener {
activity?.recreate()
dismiss()
}
binding.authButtonSkip.setOnClickListener { dismiss() }
}
override fun enableAuthButton(isEnabled: Boolean) {
binding.authButton.isEnabled = isEnabled
}
override fun showProgress(show: Boolean) {
binding.authProgress.isVisible = show
}
override fun showPeselError(show: Boolean) {
binding.authInputLayout.error = getString(R.string.auth_api_error).takeIf { show }
}
override fun showInvalidPeselError(show: Boolean) {
binding.authInputLayout.error = getString(R.string.auth_invalid_error).takeIf { show }
}
override fun showSuccess(show: Boolean) {
binding.authSuccess.isVisible = show
}
override fun showContent(show: Boolean) {
binding.authForm.isVisible = show
}
override fun showDescriptionWithName(name: String) {
binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml()
}
}

View File

@ -0,0 +1,100 @@
package io.github.wulkanowy.ui.modules.auth
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import kotlinx.coroutines.launch
import javax.inject.Inject
class AuthPresenter @Inject constructor(
private val semesterRepository: SemesterRepository,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<AuthView>(errorHandler, studentRepository) {
private var pesel: String = ""
override fun onAttachView(view: AuthView) {
super.onAttachView(view)
view.enableAuthButton(pesel.length == 11)
view.showSuccess(false)
view.showProgress(false)
loadName()
}
private fun loadName() {
presenterScope.launch {
runCatching { studentRepository.getCurrentStudent(false) }
.onSuccess { view?.showDescriptionWithName(it.studentName) }
.onFailure { errorHandler.dispatch(it) }
}
}
fun onPeselChange(newPesel: String?) {
pesel = newPesel.orEmpty()
view?.enableAuthButton(pesel.length == 11)
view?.showPeselError(false)
view?.showInvalidPeselError(false)
}
fun authorize() {
presenterScope.launch {
view?.showProgress(true)
view?.showContent(false)
if (!isValidPESEL(pesel)) {
view?.showInvalidPeselError(true)
view?.showProgress(false)
view?.showContent(true)
return@launch
}
runCatching {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
if (isSuccess) {
studentRepository.refreshStudentName(student, semester)
}
isSuccess
}
.onFailure { errorHandler.dispatch(it) }
.onSuccess {
if (it) {
view?.showSuccess(true)
view?.showContent(false)
view?.showPeselError(false)
} else {
view?.showSuccess(false)
view?.showContent(true)
view?.showPeselError(true)
}
}
view?.showProgress(false)
}
}
private fun isValidPESEL(peselString: String): Boolean {
if (peselString.length != 11) {
return false
}
val weights = intArrayOf(1, 3, 7, 9, 1, 3, 7, 9, 1, 3)
var sum = 0
for (i in 0 until 10) {
sum += weights[i] * Character.getNumericValue(peselString[i])
}
sum %= 10
sum = 10 - sum
sum %= 10
return sum == Character.getNumericValue(peselString[10])
}
}

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.ui.modules.auth
import io.github.wulkanowy.ui.base.BaseView
interface AuthView : BaseView {
fun enableAuthButton(isEnabled: Boolean)
fun showProgress(show: Boolean)
fun showPeselError(show: Boolean)
fun showInvalidPeselError(show: Boolean)
fun showSuccess(show: Boolean)
fun showContent(show: Boolean)
fun showDescriptionWithName(name: String)
}

View File

@ -142,10 +142,15 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
private fun initializeMessageContainer() { private fun initializeMessageContainer() {
ViewCompat.setOnApplyWindowInsetsListener(binding.sendMessageScroll) { view, insets -> ViewCompat.setOnApplyWindowInsetsListener(binding.sendMessageScroll) { view, insets ->
val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) val navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime())
view.updateLayoutParams<ViewGroup.MarginLayoutParams> { view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = bottomInsets.bottom bottomMargin = if (imeInsets.bottom > navigationBarInsets.bottom) {
imeInsets.bottom
} else {
navigationBarInsets.bottom
}
} }
WindowInsetsCompat.CONSUMED WindowInsetsCompat.CONSUMED
} }

View File

@ -31,4 +31,6 @@ class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, Settin
override fun showErrorDetailsDialog(error: Throwable) {} override fun showErrorDetailsDialog(error: Throwable) {}
override fun showChangePasswordSnackbar(redirectUrl: String) {} override fun showChangePasswordSnackbar(redirectUrl: String) {}
override fun showAuthDialog() {}
} }

View File

@ -8,6 +8,7 @@ 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
@ -62,6 +63,10 @@ class AdvancedFragment : PreferenceFragmentCompat(),
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)

View File

@ -9,6 +9,7 @@ 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
@ -78,6 +79,10 @@ class AppearanceFragment : PreferenceFragmentCompat(),
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)

View File

@ -21,6 +21,7 @@ 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
@ -148,6 +149,10 @@ class NotificationsFragment : PreferenceFragmentCompat(),
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun showFixSyncDialog() { override fun showFixSyncDialog() {
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.pref_notify_fix_sync_issues) .setTitle(R.string.pref_notify_fix_sync_issues)

View File

@ -10,6 +10,7 @@ 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
@ -99,6 +100,10 @@ class SyncFragment : PreferenceFragmentCompat(),
ErrorDialog.newInstance(error).show(childFragmentManager, "error_details") ErrorDialog.newInstance(error).show(childFragmentManager, "error_details")
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)

View File

@ -2,4 +2,4 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
inline val Student.nickOrName get() = if (nick.isBlank()) studentName else nick inline val Student.nickOrName get() = nick.ifBlank { studentName }

View File

@ -4,5 +4,6 @@ Wersja 2.0.0
— dodaliśmy możliwość zmiany kolejności pozycji w menu dolnym — dodaliśmy możliwość zmiany kolejności pozycji w menu dolnym
— poprawiliśmy sposób wyświetlania błędu o nieprawidłowym haśle na ekranie logowania — poprawiliśmy sposób wyświetlania błędu o nieprawidłowym haśle na ekranie logowania
— od teraz zmiana ustawień liczenia średniej automatycznie odświeży listę ocen — od teraz zmiana ustawień liczenia średniej automatycznie odświeży listę ocen
— dodaliśmy okienko na wpisanie numeru PESEL, gdy dziennik wymaga dodatkowej autoryzacji dostępu
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -0,0 +1,10 @@
<vector android:height="24dp"
android:tint="#000000"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM16.59,7.58L10,14.17l-2.59,-2.58L6,13l4,4 8,-8z" />
</vector>

View File

@ -22,17 +22,15 @@
<io.github.wulkanowy.materialchipsinput.ConsumedNestedScrollView <io.github.wulkanowy.materialchipsinput.ConsumedNestedScrollView
android:id="@+id/sendMessageScroll" android:id="@+id/sendMessageScroll"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:fillViewport="true" android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/send_app_bar"> app:layout_constraintTop_toBottomOf="@id/send_app_bar">
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sendMessageContent" android:id="@+id/sendMessageContent"

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:fillViewport="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/auth_form"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/auth_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="32dp"
android:text="@string/auth_title"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/auth_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="8dp"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/auth_title"
app:lineHeight="24sp"
tools:text="@string/auth_description" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/auth_input_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
android:hint="@string/auth_pesel"
app:errorEnabled="true"
app:layout_constraintTop_toBottomOf="@id/auth_description">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/auth_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:maxLength="11" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/auth_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:text="@string/auth_button"
app:layout_constraintBottom_toTopOf="@id/auth_button_skip"
app:layout_constraintTop_toBottomOf="@id/auth_input_layout"
app:layout_constraintVertical_bias="1" />
<com.google.android.material.button.MaterialButton
android:id="@+id/auth_button_skip"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:text="@string/auth_button_skip"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/auth_success"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/auth_success_icon"
android:layout_width="62dp"
android:layout_height="62dp"
android:layout_marginTop="220dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_auth_success"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?colorOnBackground" />
<TextView
android:id="@+id/auth_success_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
android:gravity="center"
android:text="@string/auth_success"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@id/auth_success_icon"
app:lineHeight="32sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/auth_success_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:text="@android:string/ok"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/auth_success_description"
app:layout_constraintVertical_bias="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
android:id="@+id/auth_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
</ScrollView>

View File

@ -808,6 +808,17 @@
<string name="menu_order_confirm_restart">Restart</string> <string name="menu_order_confirm_restart">Restart</string>
<!--Auth-->
<string name="auth_api_error">Authorization has been rejected. The data provided does not match the records in the secretary\'s office.</string>
<string name="auth_invalid_error">Invalid PESEL</string>
<string name="auth_pesel">PESEL</string>
<string name="auth_button">Authorize</string>
<string name="auth_success">Authorization completed successfully</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 &lt;b>%1$s&lt;/b> in the field below</string>
<string name="auth_button_skip">Skip for now</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>

View File

@ -78,4 +78,9 @@
<item name="android:background">@drawable/background_material_alert_dialog</item> <item name="android:background">@drawable/background_material_alert_dialog</item>
<item name="android:layout" tools:ignore="PrivateResource">@layout/m3_alert_dialog</item> <item name="android:layout" tools:ignore="PrivateResource">@layout/m3_alert_dialog</item>
</style> </style>
<style name="FullScreenDialogStyle" parent="WulkanowyTheme">
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsFloating">false</item>
</style>
</resources> </resources>

View File

@ -13,6 +13,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogAdsConsentBinding import io.github.wulkanowy.databinding.DialogAdsConsentBinding
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.openInternetBrowser import io.github.wulkanowy.utils.openInternetBrowser
import javax.inject.Inject import javax.inject.Inject
@ -146,6 +147,10 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
(activity as? BaseActivity<*, *>)?.openClearLoginView() (activity as? BaseActivity<*, *>)?.openClearLoginView()
} }
override fun showAuthDialog() {
AuthDialog.newInstance().show(childFragmentManager, "auth_dialog")
}
override fun showErrorDetailsDialog(error: Throwable) { override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }