diff --git a/app/sampledata/vulcan/edu.lublin.eu.png b/app/sampledata/vulcan/edu.lublin.eu.png new file mode 100644 index 00000000..ece8b925 Binary files /dev/null and b/app/sampledata/vulcan/edu.lublin.eu.png differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cc82fed3..a146711d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,9 +135,6 @@ android:configChanges="orientation|screenSize" android:launchMode="singleTop" android:theme="@style/AppTheme.Light" /> - Unit) { ).apply { studentData["isPremium"] = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true studentData["accountId"] = account.getInt("Id") ?: 0 - studentData["accountLogin"] = login + studentData["accountLogin"] = data.apiLogin ?: login + studentData["accountPassword"] = data.apiPassword studentData["accountToken"] = data.apiAccessToken studentData["accountTokenTime"] = data.apiTokenExpiryTime studentData["accountRefreshToken"] = data.apiRefreshToken diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt index 166a4ae5..5198d155 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginPortal.kt @@ -146,12 +146,14 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } val error = if (response.code() == 200) null else json.getJsonArray("errors")?.getString(0) + ?: json.getJsonObject("errors")?.entrySet()?.firstOrNull()?.value?.asString error?.let { code -> when { code.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED code.contains("Upewnij się, że nie") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN // this doesn't work anyway: `errors` is an object with `g-recaptcha-response` set code.contains("robotem") -> ERROR_CAPTCHA_LIBRUS_PORTAL + code.contains("Podany adres e-mail jest nieprawidłowy.") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN else -> ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR }.let { errorCode -> data.error(ApiError(TAG, errorCode) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt index 5e998425..ed1fba00 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt @@ -197,6 +197,13 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt profile.userCode = generateUserCode() + // update profile subname with class name, school year and account type + profile.subname = joinNotNullStrings( + " - ", + profile.studentClassName, + "${profile.studentSchoolYearStart}/${profile.studentSchoolYearStart + 1}" + ) + " " + app.getString(if (profile.isParent) R.string.account_type_parent else R.string.account_type_child) + db.profileDao().add(profile) db.loginStoreDao().add(loginStore) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt index bdd14f33..ae82ff8f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt @@ -27,6 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDetailsDialog import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar +import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time import retrofit2.Response @@ -73,7 +74,7 @@ class SzkolnyApi(val app: App) : CoroutineScope { suspend inline fun runCatching(errorSnackbar: ErrorSnackbar, crossinline block: SzkolnyApi.() -> T?): T? { return try { - withContext(Dispatchers.Default) { block() } + withContext(Dispatchers.Default) { block.invoke(this@SzkolnyApi) } } catch (e: Exception) { errorSnackbar.addError(e.toApiError(TAG)).show() @@ -82,7 +83,7 @@ class SzkolnyApi(val app: App) : CoroutineScope { } suspend inline fun runCatching(activity: AppCompatActivity, crossinline block: SzkolnyApi.() -> T?): T? { return try { - withContext(Dispatchers.Default) { block() } + withContext(Dispatchers.Default) { block.invoke(this@SzkolnyApi) } } catch (e: Exception) { ErrorDetailsDialog( @@ -95,7 +96,7 @@ class SzkolnyApi(val app: App) : CoroutineScope { } inline fun runCatching(block: SzkolnyApi.() -> T, onError: (e: Throwable) -> Unit): T? { return try { - block() + block.invoke(this@SzkolnyApi) } catch (e: Exception) { onError(e) @@ -327,4 +328,11 @@ class SzkolnyApi(val app: App) : CoroutineScope { return parseResponse(response).message } + + @Throws(Exception::class) + fun getPlatforms(registerName: String): List { + val response = api.appLoginPlatforms(registerName).execute() + + return parseResponse(response) + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt index 0e3dd574..bce3aef0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyService.kt @@ -6,11 +6,9 @@ package pl.szczodrzynski.edziennik.data.api.szkolny import pl.szczodrzynski.edziennik.data.api.szkolny.request.* import pl.szczodrzynski.edziennik.data.api.szkolny.response.* +import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.POST -import retrofit2.http.Query +import retrofit2.http.* interface SzkolnyService { @@ -34,4 +32,7 @@ interface SzkolnyService { @POST("feedbackMessage") fun feedbackMessage(@Body request: FeedbackMessageRequest): Call> + + @GET("appLogin/platforms/{registerName}") + fun appLoginPlatforms(@Path("registerName") registerName: String): Call>> } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt index 1442f40c..aa28faf7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Profile.kt @@ -80,7 +80,7 @@ open class Profile( var dateYearEnd = Date(studentSchoolYearStart + 1, 6, 30) fun getSemesterStart(semester: Int) = if (semester == 1) dateSemester1Start else dateSemester2Start fun getSemesterEnd(semester: Int) = if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd - fun dateToSemester(date: Date) = if (date.value >= getSemesterStart(2).value) 2 else 1 + fun dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1 @delegate:Ignore val currentSemester by lazy { dateToSemester(Date.getToday()) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt index 7f65279f..c686b857 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.kt @@ -1,3 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + package pl.szczodrzynski.edziennik.ui.modules.login import android.app.Activity @@ -14,20 +18,19 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.databinding.ActivityLoginBinding +import pl.szczodrzynski.edziennik.databinding.LoginActivityBinding import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import kotlin.coroutines.CoroutineContext class LoginActivity : AppCompatActivity(), CoroutineScope { companion object { private const val TAG = "LoginActivity" - @JvmField - var navOptions: NavOptions? = null var thisOneIsTricky = 0 } private val app: App by lazy { applicationContext as App } - private lateinit var b: ActivityLoginBinding + private lateinit var b: LoginActivityBinding + lateinit var navOptions: NavOptions val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) } val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) } @@ -36,7 +39,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope { get() = job + Dispatchers.Main var lastError: ApiError? = null - val profiles = mutableListOf() + val profiles = mutableListOf() val loginStores = mutableListOf() override fun onBackPressed() { @@ -50,7 +53,9 @@ class LoginActivity : AppCompatActivity(), CoroutineScope { return if (destination.id == R.id.loginSyncFragment) return - if (destination.id == R.id.loginChooserFragment) { + if (destination.id == R.id.loginFinishFragment) + return + if (destination.id == R.id.loginChooserFragment && loginStores.isEmpty()) { setResult(Activity.RESULT_CANCELED) finish() return @@ -83,7 +88,7 @@ class LoginActivity : AppCompatActivity(), CoroutineScope { .setPopExitAnim(R.anim.slide_out_right) .build() - b = ActivityLoginBinding.inflate(layoutInflater) + b = LoginActivityBinding.inflate(layoutInflater) setContentView(b.root) errorSnackbar.setCoordinator(b.coordinator, b.snackbarAnchor) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserAdapter.kt new file mode 100644 index 00000000..c9ed2d6e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserAdapter.kt @@ -0,0 +1,135 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.ui.modules.login.viewholder.ModeViewHolder +import pl.szczodrzynski.edziennik.ui.modules.login.viewholder.RegisterViewHolder +import kotlin.coroutines.CoroutineContext + +class LoginChooserAdapter( + val activity: AppCompatActivity, + val onModeClick: ((loginType: LoginInfo.Register, loginMode: LoginInfo.Mode) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "LoginChooserAdapter" + private const val ITEM_TYPE_REGISTER = 0 + private const val ITEM_TYPE_MODE = 1 + const val STATE_CLOSED = 0 + const val STATE_OPENED = 1 + } + + private val app = activity.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + ITEM_TYPE_REGISTER -> RegisterViewHolder(inflater, parent) + ITEM_TYPE_MODE -> ModeViewHolder(inflater, parent) + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + override fun getItemViewType(position: Int): Int { + return when (items[position]) { + is LoginInfo.Register -> ITEM_TYPE_REGISTER + is LoginInfo.Mode -> ITEM_TYPE_MODE + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + private val onClickListener = View.OnClickListener { view -> + val model = view.getTag(R.string.tag_key_model) + if (model is LoginInfo.Register && model.loginModes.size == 1) { + onModeClick?.invoke(model, model.loginModes.first()) + return@OnClickListener + } + if (model is LoginInfo.Mode) { + val loginInfo = items.firstOrNull { + it is LoginInfo.Register && it.loginModes.contains(model) + } as? LoginInfo.Register + ?: return@OnClickListener + + onModeClick?.invoke(loginInfo, model) + return@OnClickListener + } + if (model !is LoginInfo.Register) + return@OnClickListener + expandModel(model, view) + } + + private fun expandModel(model: LoginInfo.Register, view: View?, notifyAdapter: Boolean = true) { + val position = items.indexOf(model) + if (position == -1) + return + + if (model.state == STATE_CLOSED) { + + val subItems = model.items + + model.state = STATE_OPENED + items.addAll(position + 1, subItems) + if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size) + } + else { + val start = position + 1 + var end: Int = items.size + for (i in start until items.size) { + val model1 = items[i] + val level = (model1 as? ExpandableItemModel<*>)?.level ?: 3 + if (level <= model.level) { + end = i + break + } else { + if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) { + model1.state = STATE_CLOSED + } + } + } + + if (end != -1) { + items.subList(start, end).clear() + if (notifyAdapter) notifyItemRangeRemoved(start, end - start) + } + + model.state = STATE_CLOSED + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = items[position] + if (holder !is BindableViewHolder<*, *>) + return + + holder.itemView.setTag(R.string.tag_key_model, item) + + when { + holder is RegisterViewHolder && item is LoginInfo.Register -> holder.onBind(activity, app, item, position, this) + holder is ModeViewHolder && item is LoginInfo.Mode -> holder.onBind(activity, app, item, position, this) + } + + holder.itemView.setOnClickListener(onClickListener) + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt index a522d427..37391924 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt @@ -1,78 +1,90 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + package pl.szczodrzynski.edziennik.ui.modules.login import android.app.Activity -import android.content.Intent import android.os.Bundle -import android.os.Process import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.animation.AnimationUtils -import android.widget.CompoundButton +import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import com.afollestad.materialdialogs.DialogAction -import com.afollestad.materialdialogs.MaterialDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.Bundle import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding -import pl.szczodrzynski.edziennik.onChange +import pl.szczodrzynski.edziennik.databinding.LoginChooserFragmentBinding import pl.szczodrzynski.edziennik.onClick -import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity -import pl.szczodrzynski.edziennik.utils.Anim -import kotlin.system.exitProcess +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import kotlin.coroutines.CoroutineContext - -class LoginChooserFragment : Fragment() { +class LoginChooserFragment : Fragment(), CoroutineScope { companion object { private const val TAG = "LoginChooserFragment" - var fakeLogin = false } private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginChooserBinding + private lateinit var b: LoginChooserFragmentBinding private val nav by lazy { activity.nav } + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginChooserBinding.inflate(inflater) + b = LoginChooserFragmentBinding.inflate(inflater) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - b.topLogo.onClick { - if (LoginActivity.thisOneIsTricky <= -1) { - LoginActivity.thisOneIsTricky = 999 - } - if (LoginActivity.thisOneIsTricky in 0..7) { - LoginActivity.thisOneIsTricky++ - if (LoginActivity.thisOneIsTricky == 7) { - b.topLogo.startAnimation(AnimationUtils.loadAnimation(activity, R.anim.shake)); - if (b.devMode.visibility != View.VISIBLE) - Anim.expand(b.devMode, 500, null); - LoginActivity.thisOneIsTricky = 3 - } + if (!isAdded) return + + val adapter = LoginChooserAdapter(activity) { loginType, loginMode -> + if (loginMode.isPlatformSelection) { + nav.navigate(R.id.loginPlatformListFragment, Bundle( + "loginType" to loginType.loginType, + "loginMode" to loginMode.loginMode + ), activity.navOptions) + return@LoginChooserAdapter } + + nav.navigate(R.id.loginFormFragment, Bundle( + "loginType" to loginType.loginType, + "loginMode" to loginMode.loginMode + ), activity.navOptions) + } + + LoginInfo.chooserList = LoginInfo.chooserList + ?: LoginInfo.list.toMutableList() + + adapter.items = LoginInfo.chooserList!! + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) } - b.loginMobidziennikLogo.onClick { nav.navigate(R.id.loginMobidziennikFragment, null, LoginActivity.navOptions) } - b.loginLibrusLogo.onClick { nav.navigate(R.id.loginLibrusFragment, null, LoginActivity.navOptions) } - b.loginVulcanLogo.onClick { nav.navigate(R.id.loginVulcanFragment, null, LoginActivity.navOptions) } - b.loginIuczniowieLogo.onClick { nav.navigate(R.id.loginIuczniowieFragment, null, LoginActivity.navOptions) } - b.loginLibrusJstLogo.onClick { nav.navigate(R.id.loginLibrusJstFragment, null, LoginActivity.navOptions) } - b.loginEdudziennikLogo.onClick { nav.navigate(R.id.loginEdudziennikFragment, null, LoginActivity.navOptions) } when { activity.loginStores.isNotEmpty() -> { // we are navigated here from LoginSummary - b.cancelButton.visibility = View.VISIBLE + b.cancelButton.isVisible = true b.cancelButton.onClick { nav.navigateUp() } } app.config.loginFinished -> { // we are navigated here from AppDrawer - b.cancelButton.visibility = View.VISIBLE + b.cancelButton.isVisible = true b.cancelButton.onClick { activity.setResult(Activity.RESULT_CANCELED) activity.finish() @@ -80,57 +92,8 @@ class LoginChooserFragment : Fragment() { } else -> { // there are no profiles - b.cancelButton.visibility = View.GONE + b.cancelButton.isVisible = false } } - - b.devMode.visibility = if (App.debugMode) View.VISIBLE else View.GONE - b.devMode.isChecked = app.config.debugMode - b.devMode.onChange { v, isChecked -> - if (isChecked) { - MaterialDialog.Builder(activity) - .title(R.string.are_you_sure) - .content(R.string.dev_mode_enable_warning) - .positiveText(R.string.yes) - .negativeText(R.string.no) - .onPositive { _: MaterialDialog?, _: DialogAction? -> - app.config.debugMode = true - App.devMode = true - MaterialAlertDialogBuilder(activity) - .setTitle("Restart") - .setMessage("Wymagany restart aplikacji") - .setPositiveButton("OK") { _, _ -> - Process.killProcess(Process.myPid()) - Runtime.getRuntime().exit(0) - exitProcess(0) - } - .setCancelable(false) - .show() - } - .onNegative { _: MaterialDialog?, _: DialogAction? -> - app.config.debugMode = false - App.devMode = false - b.devMode.isChecked = app.config.debugMode - b.devMode.jumpDrawablesToCurrentState() - Anim.collapse(b.devMode, 1000, null) - } - .show() - } else { - app.config.debugMode = false - App.devMode = false - /*if (b.devModeLayout.getVisibility() === View.VISIBLE) { - Anim.collapse(b.devModeTitle, 500, null) - Anim.collapse(b.devModeLayout, 500, null) - }*/ - } - } - - b.fakeLogin.visibility = if (App.devMode) View.VISIBLE else View.GONE - b.fakeLogin.isChecked = fakeLogin - b.fakeLogin.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> - fakeLogin = isChecked - } - - b.helpButton.onClick { startActivity(Intent(activity, FeedbackActivity::class.java)) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginEdudziennikFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginEdudziennikFragment.kt deleted file mode 100644 index be1a83a7..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginEdudziennikFragment.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.animation.ArgbEvaluator -import android.animation.ObjectAnimator -import android.graphics.Color -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN -import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK -import pl.szczodrzynski.edziennik.databinding.FragmentLoginEdudziennikBinding -import java.util.* -import kotlin.coroutines.CoroutineContext - - -class LoginEdudziennikFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginEdudziennikFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginEdudziennikBinding - private val nav by lazy { activity.nav } - private var hehe = 0 - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginEdudziennikBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN -> - b.loginPasswordLayout.error = getString(R.string.login_error_incorrect_login_or_password) - } - } - } - - b.topText.onClick { - if (LoginActivity.thisOneIsTricky != -1) - return@onClick - hehe++ - if (hehe >= 5) { - LoginActivity.thisOneIsTricky = 3 - val colorAnim = ObjectAnimator.ofInt( - b.topText, - "textColor", - Color.BLACK, - Color.RED, - Color.BLACK, - Color.RED, - Color.BLACK, - Color.RED, - Color.BLACK - ) - colorAnim.setEvaluator(ArgbEvaluator()) - colorAnim.duration = 1500L - colorAnim.start() - } - } - - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginEmailLayout.error = null - b.loginPasswordLayout.error = null - - val email = b.loginEmail.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val password = b.loginPassword.text?.toString() ?: "" - - if (email.isBlank()) { - b.loginEmailLayout.error = getString(R.string.login_error_no_email) - errors = true - } - if (password.isBlank()) { - b.loginPasswordLayout.error = getString(R.string.login_error_no_password) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginEmail.setText(email) - if (!"([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+".toRegex().matches(email)) { - b.loginEmailLayout.error = getString(R.string.login_error_incorrect_email) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_EDUDZIENNIK, - "email" to email, - "password" to password - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFinishFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFinishFragment.kt index 8f52b41a..7b2e66b8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFinishFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFinishFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kuba Szczodrzyński 2020-1-4. + * Copyright (c) Kuba Szczodrzyński 2020-4-16. */ package pl.szczodrzynski.edziennik.ui.modules.login @@ -14,7 +14,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.databinding.FragmentLoginFinishBinding +import pl.szczodrzynski.edziennik.databinding.LoginFinishFragmentBinding import kotlin.coroutines.CoroutineContext class LoginFinishFragment : Fragment(), CoroutineScope { @@ -24,27 +24,29 @@ class LoginFinishFragment : Fragment(), CoroutineScope { private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginFinishBinding + private lateinit var b: LoginFinishFragmentBinding private val nav by lazy { activity.nav } private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main + // local/private variables go here + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginFinishBinding.inflate(inflater) + b = LoginFinishFragmentBinding.inflate(inflater) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val firstRun = !App.config.loginFinished - App.config.loginFinished = true + val firstRun = !app.config.loginFinished + app.config.loginFinished = true if (!firstRun) { - b.loginFinishSubtitle.setText(R.string.login_finish_subtitle_not_first_run) + b.subTitle.setText(R.string.login_finish_subtitle_not_first_run) } b.finishButton.onClick { @@ -74,4 +76,4 @@ class LoginFinishFragment : Fragment(), CoroutineScope { } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt new file mode 100644 index 00000000..d0676105 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginFormFragment.kt @@ -0,0 +1,177 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.annotation.SuppressLint +import android.os.Bundle +import android.text.InputType +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.core.widget.addTextChangedListener +import androidx.fragment.app.Fragment +import com.google.android.material.textfield.TextInputLayout +import com.google.gson.JsonParser +import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.utils.paddingDp +import com.mikepenz.iconics.utils.sizeDp +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.databinding.LoginFormFragmentBinding +import pl.szczodrzynski.edziennik.databinding.LoginFormItemBinding +import pl.szczodrzynski.navlib.colorAttr +import java.util.* +import kotlin.coroutines.CoroutineContext + +class LoginFormFragment : Fragment(), CoroutineScope { + companion object { + private const val TAG = "LoginFormFragment" + } + + private lateinit var app: App + private lateinit var activity: LoginActivity + private lateinit var b: LoginFormFragmentBinding + private val nav by lazy { activity.nav } + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as LoginActivity?) ?: return null + context ?: return null + app = activity.application as App + b = LoginFormFragmentBinding.inflate(inflater) + return b.root + } + + @SuppressLint("ResourceType") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (!isAdded) return + b.backButton.onClick { nav.navigateUp() } + + b.errorLayout.isVisible = false + b.errorLayout.background?.setTintColor(R.attr.colorError.resolveAttr(activity)) + + val loginType = arguments?.getInt("loginType") ?: return + val register = LoginInfo.list.firstOrNull { it.loginType == loginType } ?: return + val loginMode = arguments?.getInt("loginMode") ?: return + val mode = register.loginModes.firstOrNull { it.loginMode == loginMode } ?: return + + val platformName = arguments?.getString("platformName") + val platformGuideText = arguments?.getString("platformGuideText") + val platformDescription = arguments?.getString("platformDescription") + val platformFormFields = arguments?.getString("platformFormFields")?.split(";") + val platformApiData = arguments?.getString("platformApiData")?.let { JsonParser().parse(it)?.asJsonObject } + + b.title.setText(R.string.login_form_title_format, app.getString(register.registerName)) + b.subTitle.text = platformName ?: app.getString(mode.name) + b.text.text = platformGuideText ?: app.getString(mode.guideText) + + val credentials = mutableMapOf() + + for (credential in mode.credentials) { + if (platformFormFields?.contains(credential.keyName) == false) + continue + + val b = LoginFormItemBinding.inflate(layoutInflater) + b.textLayout.hint = app.getString(credential.name) + if (credential.hideText) { + b.textEdit.inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD + b.textLayout.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE + } + b.textEdit.addTextChangedListener { + b.textLayout.error = null + } + + b.textEdit.id = credential.name + + b.textEdit.setText(arguments?.getString(credential.keyName) ?: "") + b.textLayout.startIconDrawable = IconicsDrawable(activity) + .icon(credential.icon) + .sizeDp(24) + .paddingDp(2) + .colorAttr(activity, R.attr.colorOnBackground) + + this.b.formContainer.addView(b.root) + credentials[credential] = b + } + + activity.lastError?.let { error -> + activity.lastError = null + startCoroutineTimer(delayMillis = 200L) { + for (credential in credentials) { + credential.key.errorCodes[error.errorCode]?.let { + credential.value.textLayout.error = app.getString(it) + return@startCoroutineTimer + } + } + mode.errorCodes[error.errorCode]?.let { + b.errorText.text = app.getString(it) + b.errorLayout.isVisible = true + return@startCoroutineTimer + } + } + } + + b.loginButton.onClick { + val payload = Bundle( + "loginType" to loginType, + "loginMode" to loginMode + ) + + if (App.devMode && b.fakeLogin.isChecked) { + payload.putBoolean("fakeLogin", true) + } + + platformApiData?.entrySet()?.forEach { + payload.putString(it.key, it.value.asString) + } + + var hasErrors = false + credentials.forEach { (credential, b) -> + var text = b.textEdit.text?.toString() ?: return@forEach + if (!credential.hideText) + text = text.trim() + + if (credential.caseMode == LoginInfo.Credential.CaseMode.UPPER_CASE) + text = text.toUpperCase(Locale.getDefault()) + if (credential.caseMode == LoginInfo.Credential.CaseMode.LOWER_CASE) + text = text.toLowerCase(Locale.getDefault()) + + credential.stripTextRegex?.let { + text = text.replace(it.toRegex(), "") + } + + b.textEdit.setText(text) + + if (credential.isRequired && text.isBlank()) { + b.textLayout.error = app.getString(credential.emptyText) + hasErrors = true + return@forEach + } + + if (!text.matches(credential.validationRegex.toRegex())) { + b.textLayout.error = app.getString(credential.invalidText) + hasErrors = true + return@forEach + } + + payload.putString(credential.keyName, text) + arguments?.putString(credential.keyName, text) + } + + if (hasErrors) + return@onClick + + nav.navigate(R.id.loginProgressFragment, payload, activity.navOptions) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt new file mode 100644 index 00000000..29624d40 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginInfo.kt @@ -0,0 +1,358 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import com.google.gson.JsonObject +import com.mikepenz.iconics.typeface.IIcon +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.api.* +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +object LoginInfo { + + private fun getEmailCredential(keyName: String) = Credential( + keyName = keyName, + name = R.string.login_hint_email, + icon = CommunityMaterial.Icon.cmd_at, + emptyText = R.string.login_error_no_email, + invalidText = R.string.login_error_incorrect_email, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+", + caseMode = Credential.CaseMode.LOWER_CASE + ) + private fun getPasswordCredential(keyName: String) = Credential( + keyName = keyName, + name = R.string.login_hint_password, + icon = CommunityMaterial.Icon2.cmd_lock_outline, + emptyText = R.string.login_error_no_password, + invalidText = R.string.login_error_incorrect_login_or_password, + errorCodes = mapOf(), + isRequired = true, + validationRegex = ".*", + hideText = true + ) + + val list by lazy { listOf( + Register( + loginType = LOGIN_TYPE_LIBRUS, + internalName = "librus", + registerName = R.string.login_register_librus, + registerLogo = R.drawable.login_logo_librus, + loginModes = listOf( + Mode( + loginMode = LOGIN_MODE_LIBRUS_EMAIL, + name = R.string.login_mode_librus_email, + icon = R.drawable.login_mode_librus_email, + hintText = R.string.login_mode_librus_email_hint, + guideText = R.string.login_mode_librus_email_guide, + isRecommended = true, + credentials = listOf( + getEmailCredential("email"), + getPasswordCredential("password") + ), + errorCodes = mapOf( + ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED to R.string.login_error_account_not_activated, + ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN to R.string.login_error_incorrect_login_or_password, + ERROR_CAPTCHA_LIBRUS_PORTAL to R.string.error_3001_reason + ) + ), + Mode( + loginMode = LOGIN_MODE_LIBRUS_SYNERGIA, + name = R.string.login_mode_librus_synergia, + icon = R.drawable.login_mode_librus_synergia, + hintText = R.string.login_mode_librus_synergia_hint, + guideText = R.string.login_mode_librus_synergia_guide, + credentials = listOf( + Credential( + keyName = "accountLogin", + name = R.string.login_hint_login, + icon = CommunityMaterial.Icon.cmd_account_outline, + emptyText = R.string.login_error_no_login, + invalidText = R.string.login_error_incorrect_login, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "[A-z0-9._\\-+]+", + caseMode = Credential.CaseMode.LOWER_CASE + ), + getPasswordCredential("accountPassword") + ), + errorCodes = mapOf( + ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN to R.string.login_error_incorrect_login_or_password, + ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST to R.string.login_error_incorrect_login_or_password + ) + ), + Mode( + loginMode = LOGIN_MODE_LIBRUS_JST, + name = R.string.login_mode_librus_jst, + icon = R.drawable.login_mode_librus_jst, + hintText = R.string.login_mode_librus_jst_hint, + guideText = R.string.login_mode_librus_jst_guide, + credentials = listOf( + Credential( + keyName = "accountCode", + name = R.string.login_hint_token, + icon = CommunityMaterial.Icon.cmd_code_braces, + emptyText = R.string.login_error_no_token, + invalidText = R.string.login_error_incorrect_token, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "[A-Z0-9_]+", + caseMode = Credential.CaseMode.UPPER_CASE + ), + Credential( + keyName = "accountPin", + name = R.string.login_hint_pin, + icon = CommunityMaterial.Icon2.cmd_lock, + emptyText = R.string.login_error_no_pin, + invalidText = R.string.login_error_incorrect_pin, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "[a-z0-9_]+", + caseMode = Credential.CaseMode.LOWER_CASE + ) + ), + errorCodes = mapOf( + ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN to R.string.login_error_incorrect_code_or_pin, + ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST to R.string.login_error_incorrect_code_or_pin + ) + ) + ) + ), + Register( + loginType = LOGIN_TYPE_VULCAN, + internalName = "vulcan", + registerName = R.string.login_type_vulcan, + registerLogo = R.drawable.login_logo_vulcan, + loginModes = listOf( + Mode( + loginMode = LOGIN_MODE_VULCAN_API, + name = R.string.login_mode_vulcan_api, + icon = R.drawable.login_mode_vulcan_api, + hintText = R.string.login_mode_vulcan_api_hint, + guideText = R.string.login_mode_vulcan_api_guide, + isRecommended = true, + credentials = listOf( + Credential( + keyName = "deviceToken", + name = R.string.login_hint_token, + icon = CommunityMaterial.Icon.cmd_code_braces, + emptyText = R.string.login_error_no_token, + invalidText = R.string.login_error_incorrect_token, + errorCodes = mapOf( + ERROR_LOGIN_VULCAN_INVALID_TOKEN to R.string.login_error_incorrect_token + ), + isRequired = true, + validationRegex = "[A-Z0-9]{5,12}", + caseMode = Credential.CaseMode.UPPER_CASE + ), + Credential( + keyName = "deviceSymbol", + name = R.string.login_hint_symbol, + icon = CommunityMaterial.Icon2.cmd_school, + emptyText = R.string.login_error_no_symbol, + invalidText = R.string.login_error_incorrect_symbol, + errorCodes = mapOf( + ERROR_LOGIN_VULCAN_INVALID_SYMBOL to R.string.login_error_incorrect_symbol + ), + isRequired = true, + validationRegex = "[a-z0-9_-]+", + caseMode = Credential.CaseMode.LOWER_CASE + ), + Credential( + keyName = "devicePin", + name = R.string.login_hint_pin, + icon = CommunityMaterial.Icon2.cmd_lock, + emptyText = R.string.login_error_no_pin, + invalidText = R.string.login_error_incorrect_pin, + errorCodes = mapOf( + ERROR_LOGIN_VULCAN_INVALID_PIN to R.string.login_error_incorrect_pin + ), + isRequired = true, + validationRegex = "[0-9]+", + caseMode = Credential.CaseMode.LOWER_CASE + ) + ), + errorCodes = mapOf( + ERROR_LOGIN_VULCAN_EXPIRED_TOKEN to R.string.login_error_expired_token + ) + ), + Mode( + loginMode = LOGIN_MODE_VULCAN_WEB, + name = R.string.login_mode_vulcan_web, + icon = R.drawable.login_mode_vulcan_web, + hintText = R.string.login_mode_vulcan_web_hint, + guideText = R.string.login_mode_vulcan_web_guide, + isTesting = true, + isPlatformSelection = true, + credentials = listOf( + getEmailCredential("webEmail"), + Credential( + keyName = "webUsername", + name = R.string.login_hint_username, + icon = CommunityMaterial.Icon.cmd_account_outline, + emptyText = R.string.login_error_no_username, + invalidText = R.string.login_error_incorrect_username, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "[A-Z]{7}[0-9]+", + caseMode = Credential.CaseMode.UPPER_CASE + ), + Credential( + keyName = "webPassword", + name = R.string.login_hint_password, + icon = CommunityMaterial.Icon2.cmd_lock_outline, + emptyText = R.string.login_error_no_password, + invalidText = R.string.login_error_incorrect_login_or_password, + errorCodes = mapOf(), + isRequired = true, + validationRegex = ".*", + hideText = true + ) + ), + errorCodes = mapOf() + ) + ) + ), + Register( + loginType = LOGIN_TYPE_MOBIDZIENNIK, + internalName = "mobidziennik", + registerName = R.string.login_type_mobidziennik, + registerLogo = R.drawable.login_logo_mobidziennik, + loginModes = listOf( + Mode( + loginMode = LOGIN_MODE_MOBIDZIENNIK_WEB, + name = R.string.login_mode_mobidziennik_web, + icon = R.drawable.login_mode_mobidziennik_web, + hintText = R.string.login_mode_mobidziennik_web_hint, + guideText = R.string.login_mode_mobidziennik_web_guide, + credentials = listOf( + Credential( + keyName = "username", + name = R.string.login_hint_login_email, + icon = CommunityMaterial.Icon.cmd_account_outline, + emptyText = R.string.login_error_no_login, + invalidText = R.string.login_error_incorrect_login, + errorCodes = mapOf(), + isRequired = true, + validationRegex = "^[a-z0-9_\\-@+.]+$", + caseMode = Credential.CaseMode.LOWER_CASE + ), + Credential( + keyName = "password", + name = R.string.login_hint_password, + icon = CommunityMaterial.Icon2.cmd_lock_outline, + emptyText = R.string.login_error_no_password, + invalidText = R.string.login_error_incorrect_login_or_password, + errorCodes = mapOf( + ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD to R.string.login_error_old_password + ), + isRequired = true, + validationRegex = ".*", + hideText = true + ), + Credential( + keyName = "serverName", + name = R.string.login_hint_address, + icon = CommunityMaterial.Icon2.cmd_web, + emptyText = R.string.login_error_no_address, + invalidText = R.string.login_error_incorrect_address, + errorCodes = mapOf( + ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS to R.string.login_error_incorrect_address + ), + isRequired = true, + validationRegex = "^[a-z0-9_\\-]+\$", + caseMode = Credential.CaseMode.LOWER_CASE + ) + ), + errorCodes = mapOf( + ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN to R.string.login_error_incorrect_login_or_password, + ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED to R.string.sync_error_archived + ) + ) + ) + ) + /*Register( + loginType = LOGIN_TYPE_IDZIENNIK, + + )*/ + ) } + + data class Register( + val loginType: Int, + val internalName: String, + val registerName: Int, + @DrawableRes + val registerLogo: Int, + + val loginModes: List + ) : ExpandableItemModel(loginModes.toMutableList()) { + override var level = 1 + } + + data class Mode( + val loginMode: Int, + + @StringRes + val name: Int, + @DrawableRes + val icon: Int, + @StringRes + val hintText: Int? = null, + @StringRes + val guideText: Int, + + val isRecommended: Boolean = false, + val isTesting: Boolean = false, + val isPlatformSelection: Boolean = false, + + val credentials: List, + val errorCodes: Map + ) + + data class Platform( + val id: Int, + val loginType: Int, + val loginMode: Int, + val name: String, + val description: String?, + val guideText: String?, + val icon: String, + val screenshot: String?, + val formFields: List, + val apiData: JsonObject + ) + + data class Credential( + val keyName: String, + + @StringRes + val name: Int, + val icon: IIcon, + @StringRes + val placeholder: Int? = null, + @StringRes + val emptyText: Int, + @StringRes + val invalidText: Int, + val errorCodes: Map, + @StringRes + val hintText: Int? = null, + + val isRequired: Boolean = true, + val validationRegex: String, + val caseMode: CaseMode = CaseMode.UNCHANGED, + val hideText: Boolean = false, + val stripTextRegex: String? = null + ) { + enum class CaseMode { UNCHANGED, UPPER_CASE, LOWER_CASE } + } + + var chooserList: MutableList? = null + var platformList: MutableMap> = mutableMapOf() +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieFragment.kt deleted file mode 100644 index 379eee1a..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieFragment.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_IDZIENNIK -import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieBinding -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginIuczniowieFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginIuczniowieFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginIuczniowieBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginIuczniowieBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME -> - b.loginSchoolNameLayout.error = getString(R.string.login_error_incorrect_school_name) - ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -> - b.loginPasswordLayout.error = getString(R.string.login_error_incorrect_login_or_password) - } - } - } - - b.helpButton.onClick { nav.navigate(R.id.loginIuczniowieHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginSchoolNameLayout.error = null - b.loginUsernameLayout.error = null - b.loginPasswordLayout.error = null - - val schoolName = b.loginSchoolName.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val username = b.loginUsername.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val password = b.loginPassword.text?.toString() ?: "" - - if (schoolName.isBlank()) { - b.loginSchoolNameLayout.error = getString(R.string.login_error_no_school_name) - errors = true - } - if (username.isBlank()) { - b.loginUsernameLayout.error = getString(R.string.login_error_no_username) - errors = true - } - if (password.isBlank()) { - b.loginPasswordLayout.error = getString(R.string.login_error_no_password) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginSchoolName.setText(schoolName) - b.loginUsername.setText(username) - if (!"[a-z0-9_\\-]+".toRegex().matches(schoolName)) { - b.loginSchoolNameLayout.error = getString(R.string.login_error_incorrect_school_name) - errors = true - } - if (!"[a-z0-9_\\-]+".toRegex().matches(username)) { - b.loginUsernameLayout.error = getString(R.string.login_error_incorrect_username) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_IDZIENNIK, - "schoolName" to schoolName, - "username" to username, - "password" to password - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieHelpFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieHelpFragment.java deleted file mode 100644 index 7b5c9d96..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginIuczniowieHelpFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import androidx.databinding.DataBindingUtil; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieHelpBinding; - -public class LoginIuczniowieHelpFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginIuczniowieHelpBinding b; - private static final String TAG = "LoginIuczniowieHelp"; - - public LoginIuczniowieHelpFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_iuczniowie_help, container, false); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - b.backButton.setOnClickListener((v) -> nav.navigateUp()); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt deleted file mode 100644 index 3cbf29ed..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt +++ /dev/null @@ -1,116 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.annotation.SuppressLint -import android.graphics.Color -import android.os.Build -import android.os.Bundle -import android.util.Base64 -import android.webkit.JavascriptInterface -import android.webkit.WebView -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import com.afollestad.materialdialogs.MaterialDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.data.api.LIBRUS_USER_AGENT -import pl.szczodrzynski.edziennik.utils.Themes -import pl.szczodrzynski.edziennik.utils.Utils.hexFromColorInt -import java.nio.charset.Charset - -class LoginLibrusCaptchaActivity : AppCompatActivity() { - companion object { - private const val TAG = "LoginLibrusCaptchaActivity" - } - - private lateinit var webView: WebView - private lateinit var dialog: AlertDialog - private lateinit var jsInterface: CaptchaCallbackInterface - - @SuppressLint("AddJavascriptInterface", "SetJavaScriptEnabled") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setTheme(Themes.appThemeNoDisplay) - setFinishOnTouchOutside(false) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - WebView.setWebContentsDebuggingEnabled(true) - } - - val base64Content = """ -PCFET0NUWVBFIGh0bWw+PGh0bWw+PGhlYWQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vd3d3Lmdvb2ds -ZS5jb20vcmVjYXB0Y2hhL2FwaS5qcz9vbmxvYWQ9cmVhZHkmcmVuZGVyPWV4cGxpY2l0Ij48L3Nj -cmlwdD48L2hlYWQ+PGJvZHk+PGJyPjxjZW50ZXIgaWQ9ImdyIj48L2NlbnRlcj48YnI+PHNjcmlw -dD5mdW5jdGlvbiByZWFkeSgpe2dyZWNhcHRjaGEucmVuZGVyKCdncicse3NpdGVrZXk6JzZMZjQ4 -bW9VQUFBQUFCOUNsaGR2SHI0NmdSV1ItQ04zMUNYUVBHMlUnLHRoZW1lOidUSEVNRScsY2FsbGJh -Y2s6ZnVuY3Rpb24oZSl7d2luZG93LmlmLmNhbGxiYWNrKGUpO30sImV4cGlyZWQtY2FsbGJhY2si -OmZ1bmN0aW9uKCl7d2luZG93LmlmLmV4cGlyZWRDYWxsYmFjayhlKTt9LCJlcnJvci1jYWxsYmFj -ayI6ZnVuY3Rpb24oKXt3aW5kb3cuaWYuZXJyb3JDYWxsYmFjayhlKTt9fSk7fTwvc2NyaXB0Pjwv -Ym9keT48L2h0bWw+""" - - val backgroundColor = if (Themes.isDark) 0x424242 else 0xffffff - val backgroundColorString = hexFromColorInt(backgroundColor) - - val htmlContent = Base64.decode(base64Content, Base64.DEFAULT) - .toString(Charset.defaultCharset()) - .replace("COLOR", backgroundColorString, true) - .replace("THEME", if (Themes.isDark) "dark" else "light") - - jsInterface = object : CaptchaCallbackInterface { - @JavascriptInterface - override fun callback(recaptchaResponse: String) { - MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) - .title("Captcha checked") - .content("Response: $recaptchaResponse") - .positiveText("OK") - .show() - } - - @JavascriptInterface - override fun expiredCallback() { - MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) - .title("Captcha expired") - .content("Captcha expired") - .positiveText("OK") - .show() - } - - @JavascriptInterface - override fun errorCallback() { - MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) - .title("Captcha error") - .content("Captcha error") - .positiveText("OK") - .show() - } - } - - webView = WebView(this).apply { - //setBackgroundColor((backgroundColor.toLong() or 0xff000000).toInt()) - setBackgroundColor(Color.TRANSPARENT) - settings.javaScriptEnabled = true - settings.userAgentString = LIBRUS_USER_AGENT - addJavascriptInterface(jsInterface, "if") - loadDataWithBaseURL("https://portal.librus.pl/rodzina/login/", htmlContent, "text/html", "UTF-8", null) - setLayerType(WebView.LAYER_TYPE_SOFTWARE, null) - } - - dialog = MaterialAlertDialogBuilder(this) - .setTitle(R.string.login_librus_captcha_title) - .setView(webView) - .setNegativeButton(R.string.cancel) { dialog, _ -> - dialog.dismiss() - finish() - } - .setCancelable(false) - .show() - } - - interface CaptchaCallbackInterface { - @JavascriptInterface - fun callback(recaptchaResponse: String) - @JavascriptInterface - fun expiredCallback() - @JavascriptInterface - fun errorCallback() - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusFragment.kt deleted file mode 100644 index 11c2bdd2..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusFragment.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS -import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusBinding -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginLibrusFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginLibrusFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginLibrusBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginLibrusBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN -> - b.loginPasswordLayout.error = getString(R.string.login_error_incorrect_login_or_password) - ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -> - b.loginEmailLayout.error = getString(R.string.login_error_account_not_activated) - } - } - } - - b.helpButton.onClick { nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginEmailLayout.error = null - b.loginPasswordLayout.error = null - - val email = b.loginEmail.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val password = b.loginPassword.text?.toString() ?: "" - - if (email.isBlank()) { - b.loginEmailLayout.error = getString(R.string.login_error_no_email) - errors = true - } - if (password.isBlank()) { - b.loginPasswordLayout.error = getString(R.string.login_error_no_password) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginEmail.setText(email) - if (!"([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+".toRegex().matches(email)) { - b.loginEmailLayout.error = getString(R.string.login_error_incorrect_email) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_LIBRUS, - "email" to email, - "password" to password - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusHelpFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusHelpFragment.java deleted file mode 100644 index 6984498a..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusHelpFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import androidx.databinding.DataBindingUtil; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusHelpBinding; - -public class LoginLibrusHelpFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginLibrusHelpBinding b; - private static final String TAG = "LoginLibrusHelp"; - - public LoginLibrusHelpFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_librus_help, container, false); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - b.backButton.setOnClickListener((v) -> nav.navigateUp()); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusJstFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusJstFragment.kt deleted file mode 100644 index d4e3d445..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusJstFragment.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.graphics.Color -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.WindowManager -import androidx.fragment.app.Fragment -import com.mikepenz.iconics.IconicsDrawable -import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import com.mikepenz.iconics.utils.colorInt -import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST -import pl.szczodrzynski.edziennik.data.api.LOGIN_MODE_LIBRUS_JST -import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS -import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusJstBinding -import pl.szczodrzynski.edziennik.ui.dialogs.QrScannerDialog -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginLibrusJstFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginLibrusJstFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginLibrusJstBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginLibrusJstBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN, - ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST -> - b.loginCodeLayout.error = getString(R.string.login_error_incorrect_code_or_pin) - } - } - } - - b.loginQrScan.setImageDrawable(IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_qrcode_scan) - .colorInt(Color.BLACK) - .sizeDp(72)) - b.loginQrScan.onClick { - QrScannerDialog(activity, { code -> - b.loginCode.setText(code) - if (b.loginPin.requestFocus()) { - activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } - }) - } - - b.helpButton.onClick { nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginCodeLayout.error = null - b.loginPinLayout.error = null - - val code = b.loginCode.text?.toString()?.toUpperCase(Locale.ROOT) ?: "" - val pin = b.loginPin.text?.toString() ?: "" - - if (code.isBlank()) { - b.loginCodeLayout.error = getString(R.string.login_error_no_code) - errors = true - } - if (pin.isBlank()) { - b.loginPinLayout.error = getString(R.string.login_error_no_pin) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginCode.setText(code) - if (!"[A-Z0-9_]+".toRegex().matches(code)) { - b.loginCodeLayout.error = getString(R.string.login_error_incorrect_code) - errors = true - } - if (!"[a-z0-9_]+".toRegex().matches(pin)) { - b.loginPinLayout.error = getString(R.string.login_error_incorrect_pin) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_LIBRUS, - "loginMode" to LOGIN_MODE_LIBRUS_JST, - "accountCode" to code, - "accountPin" to pin - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikFragment.kt deleted file mode 100644 index de0deb3d..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikFragment.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.* -import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikBinding -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginMobidziennikFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginMobidziennikFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginMobidziennikBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginMobidziennikBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN -> - b.loginPasswordLayout.error = getString(R.string.login_error_incorrect_login_or_password) - ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD -> - b.loginPasswordLayout.error = getString(R.string.login_error_old_password) - ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED -> - b.loginUsernameLayout.error = getString(R.string.sync_error_archived) - ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS -> - b.loginServerAddressLayout.error = getString(R.string.login_error_incorrect_address) - } - } - } - - b.helpButton.onClick { nav.navigate(R.id.loginMobidziennikHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginServerAddressLayout.error = null - b.loginUsernameLayout.error = null - b.loginPasswordLayout.error = null - - val serverName = b.loginServerAddress.text - ?.toString() - ?.toLowerCase(Locale.ROOT) - ?.replace("(?:http://|www.|mobidziennik\\.pl|wizja\\.net|\\.)".toRegex(), "") ?: "" - val username = b.loginUsername.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val password = b.loginPassword.text?.toString() ?: "" - - if (serverName.isBlank()) { - b.loginServerAddressLayout.error = getString(R.string.login_error_no_address) - errors = true - } - if (username.isBlank()) { - b.loginUsernameLayout.error = getString(R.string.login_error_no_login) - errors = true - } - if (password.isBlank()) { - b.loginPasswordLayout.error = getString(R.string.login_error_no_password) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginServerAddress.setText(serverName) - b.loginUsername.setText(username) - if (!"^[a-z0-9_\\-]+$".toRegex().matches(serverName)) { - b.loginServerAddressLayout.error = getString(R.string.login_error_incorrect_address) - errors = true - } - if (!"^[a-z0-9_\\-@+.]+$".toRegex().matches(username)) { - b.loginUsernameLayout.error = getString(R.string.login_error_incorrect_login) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_MOBIDZIENNIK, - "serverName" to serverName, - "username" to username, - "password" to password - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikHelpFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikHelpFragment.java deleted file mode 100644 index 511f6682..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMobidziennikHelpFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import androidx.databinding.DataBindingUtil; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikHelpBinding; - -public class LoginMobidziennikHelpFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginMobidziennikHelpBinding b; - private static final String TAG = "LoginMobidziennikHelp"; - - public LoginMobidziennikHelpFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_mobidziennik_help, container, false); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - b.backButton.setOnClickListener((v) -> nav.navigateUp()); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformAdapter.kt new file mode 100644 index 00000000..5bc80f36 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformAdapter.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.ui.modules.login.viewholder.PlatformViewHolder +import kotlin.coroutines.CoroutineContext + +class LoginPlatformAdapter( + val activity: AppCompatActivity, + val onPlatformClick: ((platform: LoginInfo.Platform) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "LoginPlatformAdapter" + } + + private val app = activity.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = listOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlatformViewHolder { + val inflater = LayoutInflater.from(parent.context) + return PlatformViewHolder(inflater, parent) + } + + override fun onBindViewHolder(holder: PlatformViewHolder, position: Int) { + val item = items[position] + holder.onBind(activity, app, item, position, this) + onPlatformClick?.let { + holder.b.root.onClick { _ -> it(item) } + } + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt new file mode 100644 index 00000000..9beb0860 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginPlatformListFragment.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi +import pl.szczodrzynski.edziennik.databinding.LoginPlatformListFragmentBinding +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import kotlin.coroutines.CoroutineContext + +class LoginPlatformListFragment : Fragment(), CoroutineScope { + companion object { + private const val TAG = "LoginPlatformListFragment" + } + + private lateinit var app: App + private lateinit var activity: LoginActivity + private lateinit var b: LoginPlatformListFragmentBinding + private val nav by lazy { activity.nav } + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + private val api by lazy { SzkolnyApi(app) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as LoginActivity?) ?: return null + context ?: return null + app = activity.application as App + b = LoginPlatformListFragmentBinding.inflate(inflater) + return b.root + } + + private lateinit var timeoutJob: Job + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (!isAdded) return + b.backButton.onClick { nav.navigateUp() } + + val loginType = arguments?.getInt("loginType") ?: return + val register = LoginInfo.list.firstOrNull { it.loginType == loginType } ?: return + val loginMode = arguments?.getInt("loginMode") ?: return + val mode = register.loginModes.firstOrNull { it.loginMode == loginMode } ?: return + + timeoutJob = startCoroutineTimer(5000L) { + b.timeoutText.isVisible = true + timeoutJob.cancel() + } + + val adapter = LoginPlatformAdapter(activity) { platform -> + nav.navigate(R.id.loginFormFragment, Bundle( + "loginType" to platform.loginType, + "loginMode" to platform.loginMode, + "platformName" to platform.name, + "platformDescription" to platform.description, + "platformFormFields" to platform.formFields.joinToString(";"), + "platformApiData" to platform.apiData.toString() + ), activity.navOptions) + } + + launch { + val platforms = LoginInfo.platformList[mode.name] + ?: run { + api.runCatching(activity) { + getPlatforms(register.internalName) + } ?: run { + nav.navigateUp() + return@launch + } + } + LoginInfo.platformList[mode.name] = platforms + + adapter.items = platforms + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) + } + timeoutJob.cancel() + b.loadingLayout.isVisible = false + b.list.isVisible = true + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProfileObject.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProfileObject.java deleted file mode 100644 index a86cfa86..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProfileObject.java +++ /dev/null @@ -1,29 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; -import pl.szczodrzynski.edziennik.data.db.entity.LoginStore; -import pl.szczodrzynski.edziennik.data.db.entity.Profile; - -public class LoginProfileObject { - LoginStore loginStore = null; - List profileList = new ArrayList<>(); - List selectedList = new ArrayList<>(); - - public LoginProfileObject(@NonNull LoginStore loginStore, @NonNull List profileList) { - this.loginStore = loginStore; - this.profileList = profileList; - for (Profile ignored : profileList) { - selectedList.add(true); - } - } - - public LoginProfileObject addProfile(Profile profile) { - profileList.add(profile); - selectedList.add(true); - return this; - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt index 929d9551..ab76327e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. + * Copyright (c) Kuba Szczodrzyński 2020-4-16. */ package pl.szczodrzynski.edziennik.ui.modules.login @@ -27,8 +27,10 @@ import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent import pl.szczodrzynski.edziennik.data.api.events.UserActionRequiredEvent import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding +import pl.szczodrzynski.edziennik.databinding.LoginProgressFragmentBinding +import pl.szczodrzynski.edziennik.joinNotNullStrings import kotlin.coroutines.CoroutineContext +import kotlin.math.max class LoginProgressFragment : Fragment(), CoroutineScope { companion object { @@ -37,22 +39,26 @@ class LoginProgressFragment : Fragment(), CoroutineScope { private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginProgressBinding + private lateinit var b: LoginProgressFragmentBinding private val nav by lazy { activity.nav } private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main + // local/private variables go here + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginProgressBinding.inflate(inflater) + b = LoginProgressFragmentBinding.inflate(inflater) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (!isAdded) return + val args = arguments ?: run { activity.error(ApiError(TAG, LOGIN_NO_ARGUMENTS)) nav.navigateUp() @@ -66,19 +72,21 @@ class LoginProgressFragment : Fragment(), CoroutineScope { launch { activity.errorSnackbar.dismiss() - val firstProfileId = (app.db.profileDao().lastId ?: 0) + 1 + val maxProfileId = max( + app.db.profileDao().lastId ?: 0, + activity.profiles.maxBy { it.profile.id }?.profile?.id ?: 0 + ) val loginType = args.getInt("loginType", -1) val loginMode = args.getInt("loginMode", 0) val loginStore = LoginStore( - id = firstProfileId, + id = maxProfileId + 1, type = loginType, mode = loginMode ) loginStore.copyFrom(args) - if (App.devMode && LoginChooserFragment.fakeLogin) { - loginStore.putLoginData("fakeLogin", true) - } + loginStore.removeLoginData("loginType") + loginStore.removeLoginData("loginMode") EdziennikTask.firstLogin(loginStore).enqueue(activity) } } @@ -94,10 +102,21 @@ class LoginProgressFragment : Fragment(), CoroutineScope { .show() return } + + // update subnames with school years and class name + for (profile in event.profileList) { + val schoolYearName = "${profile.studentSchoolYearStart}/${profile.studentSchoolYearStart + 1}" + profile.subname = joinNotNullStrings( + " - ", + profile.studentClassName, + schoolYearName + ) + } + activity.loginStores += event.loginStore - activity.profiles += event.profileList.map { LoginSummaryProfileAdapter.Item(it) } + activity.profiles += event.profileList.map { LoginSummaryAdapter.Item(it) } activity.errorSnackbar.dismiss() - nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions) + nav.navigate(R.id.loginSummaryFragment, null, activity.navOptions) } @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryAdapter.kt new file mode 100644 index 00000000..bc9cafbf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryAdapter.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.entity.Profile +import pl.szczodrzynski.edziennik.databinding.LoginSummaryItemBinding +import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.trigger +import kotlin.coroutines.CoroutineContext + +class LoginSummaryAdapter( + val activity: LoginActivity, + val onSelectionChanged: ((item: Item) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "LoginSummaryAdapter" + } + + private val app = activity.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = listOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return ViewHolder(LoginSummaryItemBinding.inflate(inflater, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + val b = holder.b + val profile = item.profile + val loginStore = activity.loginStores.firstOrNull { it.id == profile.loginStoreId } + ?: return + + val loginType = loginStore.type + val register = LoginInfo.list.firstOrNull { it.loginType == loginType } ?: return + val loginMode = loginStore.mode + val mode = register.loginModes.firstOrNull { it.loginMode == loginMode } ?: return + + b.profileName.text = profile.name + b.profileDetails.text = profile.subname + b.checkBox.isChecked = item.isSelected + b.modeIcon.setImageResource(mode.icon) + + if (profile.isParent) { + b.accountType.setText(R.string.account_type_parent) + } else { + b.accountType.setText(R.string.account_type_child) + } + + b.root.onClick { + b.checkBox.trigger() + } + b.checkBox.setOnCheckedChangeListener { _, isChecked -> + item.isSelected = isChecked + onSelectionChanged?.invoke(item) + } + } + + override fun getItemCount() = items.size + + class ViewHolder(val b: LoginSummaryItemBinding) : RecyclerView.ViewHolder(b.root) + + class Item(val profile: Profile, var isSelected: Boolean = true) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.kt index b16c0256..63525c45 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. + * Copyright (c) Kuba Szczodrzyński 2020-4-16. */ package pl.szczodrzynski.edziennik.ui.modules.login @@ -16,7 +16,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.databinding.FragmentLoginSummaryBinding +import pl.szczodrzynski.edziennik.databinding.LoginSummaryFragmentBinding import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import kotlin.coroutines.CoroutineContext @@ -27,27 +27,32 @@ class LoginSummaryFragment : Fragment(), CoroutineScope { private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginSummaryBinding + private lateinit var b: LoginSummaryFragmentBinding private val nav by lazy { activity.nav } private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main + // local/private variables go here + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginSummaryBinding.inflate(inflater) + b = LoginSummaryFragmentBinding.inflate(inflater) return b.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - b.profileListView.apply { - adapter = LoginSummaryProfileAdapter(activity, activity.profiles) { item -> - b.finishButton.isEnabled = activity.profiles.any { it.isSelected } - } + val adapter = LoginSummaryAdapter(activity) { _ -> + b.finishButton.isEnabled = activity.profiles.any { it.isSelected } + } + + adapter.items = activity.profiles + b.list.adapter = adapter + b.list.apply { isNestedScrollingEnabled = false setHasFixedSize(true) layoutManager = LinearLayoutManager(context) @@ -66,7 +71,7 @@ class LoginSummaryFragment : Fragment(), CoroutineScope { } b.anotherButton.onClick { - nav.navigate(R.id.loginChooserFragment, null, LoginActivity.navOptions) + nav.navigate(R.id.loginChooserFragment, null, activity.navOptions) } b.finishButton.onClick { @@ -86,7 +91,7 @@ class LoginSummaryFragment : Fragment(), CoroutineScope { val args = Bundle( "registrationAllowed" to b.registerMeSwitch.isChecked ) - nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions) + nav.navigate(R.id.loginSyncFragment, args, activity.navOptions) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryProfileAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryProfileAdapter.kt deleted file mode 100644 index 7a74dd76..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryProfileAdapter.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.* -import pl.szczodrzynski.edziennik.data.db.entity.Profile -import pl.szczodrzynski.edziennik.databinding.RowLoginProfileListItemBinding - -class LoginSummaryProfileAdapter( - val context: Context, - val items: List, - val onSelectionChanged: ((item: Item) -> Unit)? = null -) : RecyclerView.Adapter() { - - private val app by lazy { context.applicationContext as App } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(parent.context) - val view = RowLoginProfileListItemBinding.inflate(inflater, parent, false) - return ViewHolder(view) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = items[position] - val profile = item.profile - val b = holder.b - - b.textView.text = profile.name - b.checkBox.isChecked = item.isSelected - - val registerIcon = when (profile.loginStoreType) { - LOGIN_TYPE_MOBIDZIENNIK -> R.drawable.logo_mobidziennik - LOGIN_TYPE_LIBRUS -> R.drawable.logo_librus - LOGIN_TYPE_IDZIENNIK -> R.drawable.logo_idziennik - LOGIN_TYPE_VULCAN -> R.drawable.logo_vulcan - LOGIN_TYPE_EDUDZIENNIK -> R.drawable.logo_edudziennik - else -> null - } - if (registerIcon == null) - b.registerIcon.visibility = View.GONE - else { - b.registerIcon.visibility = View.VISIBLE - b.registerIcon.setImageResource(registerIcon) - } - - if (profile.isParent) { - b.accountType.setText(R.string.login_summary_account_parent) - } else { - b.accountType.setText(R.string.login_summary_account_child) - } - - val schoolYearName = "${profile.studentSchoolYearStart}/${profile.studentSchoolYearStart+1}" - b.textDetails.text = joinNotNullStrings( - " - ", - profile.studentClassName, - schoolYearName - ) - - b.root.onClick { - b.checkBox.trigger() - } - b.checkBox.setOnCheckedChangeListener { _, isChecked -> - item.isSelected = isChecked - onSelectionChanged?.invoke(item) - } - } - - override fun getItemCount() = items.size - - class ViewHolder(val b: RowLoginProfileListItemBinding) : RecyclerView.ViewHolder(b.root) - - class Item(val profile: Profile, var isSelected: Boolean = true) -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncErrorFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncErrorFragment.kt index 49977dee..e13130a5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncErrorFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncErrorFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. + * Copyright (c) Kuba Szczodrzyński 2020-4-14. */ package pl.szczodrzynski.edziennik.ui.modules.login @@ -14,7 +14,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncErrorBinding +import pl.szczodrzynski.edziennik.databinding.LoginSyncErrorFragmentBinding import pl.szczodrzynski.edziennik.onClick import kotlin.coroutines.CoroutineContext @@ -25,18 +25,20 @@ class LoginSyncErrorFragment : Fragment(), CoroutineScope { private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginSyncErrorBinding + private lateinit var b: LoginSyncErrorFragmentBinding private val nav by lazy { activity.nav } private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main + // local/private variables go here + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginSyncErrorBinding.inflate(inflater) + b = LoginSyncErrorFragmentBinding.inflate(inflater) return b.root } @@ -44,7 +46,7 @@ class LoginSyncErrorFragment : Fragment(), CoroutineScope { b.errorDetails.text = activity.lastError?.getStringReason(activity) activity.lastError = null b.nextButton.onClick { - nav.navigate(R.id.loginFinishFragment, arguments, LoginActivity.navOptions) + nav.navigate(R.id.loginFinishFragment, arguments, activity.navOptions) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt index 789bcb07..c0aab663 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt @@ -1,3 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-16. + */ + package pl.szczodrzynski.edziennik.ui.modules.login import android.os.Bundle @@ -19,9 +23,8 @@ import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskProgressEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskStartedEvent -import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_DISABLED -import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED -import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding +import pl.szczodrzynski.edziennik.data.db.entity.Profile +import pl.szczodrzynski.edziennik.databinding.LoginSyncFragmentBinding import kotlin.coroutines.CoroutineContext import kotlin.math.roundToInt @@ -32,7 +35,7 @@ class LoginSyncFragment : Fragment(), CoroutineScope { private lateinit var app: App private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginSyncBinding + private lateinit var b: LoginSyncFragmentBinding private val nav: NavController by lazy { Navigation.findNavController(activity, R.id.nav_host_fragment) } private val job: Job = Job() @@ -45,7 +48,7 @@ class LoginSyncFragment : Fragment(), CoroutineScope { activity = (getActivity() as LoginActivity?) ?: return null context ?: return null app = activity.application as App - b = FragmentLoginSyncBinding.inflate(inflater) + b = LoginSyncFragmentBinding.inflate(inflater) return b.root } @@ -56,9 +59,9 @@ class LoginSyncFragment : Fragment(), CoroutineScope { val registrationAllowed = arguments?.getBoolean("registrationAllowed") ?: false profiles.forEach { it.registration = if (registrationAllowed) - REGISTRATION_ENABLED + Profile.REGISTRATION_ENABLED else - REGISTRATION_DISABLED + Profile.REGISTRATION_DISABLED app.db.eventTypeDao().addDefaultTypes(activity, it.id) } @@ -84,13 +87,13 @@ class LoginSyncFragment : Fragment(), CoroutineScope { @Subscribe(threadMode = ThreadMode.MAIN) fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) { - nav.navigate(R.id.loginFinishFragment, finishArguments, LoginActivity.navOptions) + nav.navigate(R.id.loginFinishFragment, finishArguments, activity.navOptions) } @Subscribe(threadMode = ThreadMode.MAIN) fun onSyncProgressEvent(event: ApiTaskProgressEvent) { b.loginSyncProgressBar.progress = event.progress.roundToInt() - b.loginSyncProgressBar.isIndeterminate = event.progress < 0f + b.loginSyncProgressBar.isIndeterminate = event.progress <= 0f b.loginSyncSubtitle2.text = event.progressText } @@ -98,7 +101,7 @@ class LoginSyncFragment : Fragment(), CoroutineScope { fun onSyncErrorEvent(event: ApiTaskErrorEvent) { EventBus.getDefault().removeStickyEvent(event) activity.error(event.error) - nav.navigate(R.id.loginSyncErrorFragment, finishArguments, LoginActivity.navOptions) + nav.navigate(R.id.loginSyncErrorFragment, finishArguments, activity.navOptions) } override fun onStart() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginTemplateFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginTemplateFragment.kt deleted file mode 100644 index 4264416a..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginTemplateFragment.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN -import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_TEMPLATE -import pl.szczodrzynski.edziennik.databinding.FragmentLoginTemplateBinding -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginTemplateFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginTemplateFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginTemplateBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginTemplateBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN -> - b.loginPasswordLayout.error = getString(R.string.login_error_incorrect_login_or_password) - ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED -> - b.loginEmailLayout.error = getString(R.string.login_error_account_not_activated) - } - } - } - - b.helpButton.onClick { nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginEmailLayout.error = null - b.loginPasswordLayout.error = null - - val email = b.loginEmail.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val password = b.loginPassword.text?.toString() ?: "" - - if (email.isBlank()) { - b.loginEmailLayout.error = getString(R.string.login_error_no_email) - errors = true - } - if (password.isBlank()) { - b.loginPasswordLayout.error = getString(R.string.login_error_no_password) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginEmail.setText(email) - if (!"([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+".toRegex().matches(email)) { - b.loginEmailLayout.error = getString(R.string.login_error_incorrect_email) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_TEMPLATE, - "email" to email, - "password" to password - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanFragment.kt deleted file mode 100644 index 63ea9834..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanFragment.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2020-1-3. - */ - -package pl.szczodrzynski.edziennik.ui.modules.login - -import android.graphics.Color -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.WindowManager -import androidx.fragment.app.Fragment -import com.mikepenz.iconics.IconicsDrawable -import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import com.mikepenz.iconics.utils.colorInt -import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.data.api.* -import pl.szczodrzynski.edziennik.databinding.FragmentLoginVulcanBinding -import pl.szczodrzynski.edziennik.ui.dialogs.QrScannerDialog -import pl.szczodrzynski.edziennik.utils.Utils -import java.util.* -import kotlin.coroutines.CoroutineContext - -class LoginVulcanFragment : Fragment(), CoroutineScope { - companion object { - private const val TAG = "LoginVulcanFragment" - } - - private lateinit var app: App - private lateinit var activity: LoginActivity - private lateinit var b: FragmentLoginVulcanBinding - private val nav by lazy { activity.nav } - - private val job: Job = Job() - override val coroutineContext: CoroutineContext - get() = job + Dispatchers.Main - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - activity = (getActivity() as LoginActivity?) ?: return null - context ?: return null - app = activity.application as App - b = FragmentLoginVulcanBinding.inflate(inflater) - return b.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - activity.lastError?.let { error -> - activity.lastError = null - startCoroutineTimer(delayMillis = 100) { - when (error.errorCode) { - ERROR_LOGIN_VULCAN_INVALID_TOKEN -> - b.loginTokenLayout.error = getString(R.string.login_error_incorrect_token) - ERROR_LOGIN_VULCAN_EXPIRED_TOKEN -> - b.loginTokenLayout.error = getString(R.string.login_error_expired_token) - ERROR_LOGIN_VULCAN_INVALID_SYMBOL -> - b.loginSymbolLayout.error = getString(R.string.login_error_incorrect_symbol) - ERROR_LOGIN_VULCAN_INVALID_PIN -> - b.loginPinLayout.error = getString(R.string.login_error_incorrect_pin) - } - } - } - - b.loginQrScan.setImageDrawable(IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_qrcode_scan) - .colorInt(Color.BLACK) - .sizeDp(72)) - b.loginQrScan.onClick { - QrScannerDialog(activity, { code -> - try { - val data = Utils.VulcanQrEncryptionUtils.decode(code) - "CERT#https?://.+?/([A-z]+)/mobile-api#([A-z0-9]+)#ENDCERT".toRegex().find(data)?.let { - b.loginToken.setText(it[2]) - b.loginSymbol.setText(it[1]) - if (b.loginPin.requestFocus()) { - activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } - } - } - catch (_: Exception) {} - }) - } - - b.helpButton.onClick { nav.navigate(R.id.loginVulcanHelpFragment, null, LoginActivity.navOptions) } - b.backButton.onClick { nav.navigateUp() } - - b.loginButton.onClick { - var errors = false - - b.loginTokenLayout.error = null - b.loginSymbolLayout.error = null - b.loginPinLayout.error = null - - val token = b.loginToken.text?.toString()?.toUpperCase(Locale.ROOT) ?: "" - val symbol = b.loginSymbol.text?.toString()?.toLowerCase(Locale.ROOT) ?: "" - val pin = b.loginPin.text?.toString() ?: "" - - if (token.isBlank()) { - b.loginTokenLayout.error = getString(R.string.login_error_no_token) - errors = true - } - if (symbol.isBlank()) { - b.loginSymbolLayout.error = getString(R.string.login_error_no_symbol) - errors = true - } - if (pin.isBlank()) { - b.loginPinLayout.error = getString(R.string.login_error_no_pin) - errors = true - } - if (errors) return@onClick - - errors = false - - b.loginToken.setText(token) - b.loginSymbol.setText(symbol) - b.loginPin.setText(pin) - if (!"[A-Z0-9]{5,12}".toRegex().matches(token)) { - b.loginTokenLayout.error = getString(R.string.login_error_incorrect_token) - errors = true - } - if (!"[a-z0-9_-]+".toRegex().matches(symbol)) { - b.loginSymbolLayout.error = getString(R.string.login_error_incorrect_symbol) - errors = true - } - if (!"[a-z0-9_]+".toRegex().matches(pin)) { - b.loginPinLayout.error = getString(R.string.login_error_incorrect_pin) - errors = true - } - if (errors) return@onClick - - val args = Bundle( - "loginType" to LOGIN_TYPE_VULCAN, - "deviceToken" to token, - "deviceSymbol" to symbol, - "devicePin" to pin - ) - nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions) - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanHelpFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanHelpFragment.java deleted file mode 100644 index 0aa899ae..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginVulcanHelpFragment.java +++ /dev/null @@ -1,48 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import androidx.databinding.DataBindingUtil; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginVulcanHelpBinding; - -public class LoginVulcanHelpFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginVulcanHelpBinding b; - private static final String TAG = "LoginVulcanHelp"; - - public LoginVulcanHelpFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_vulcan_help, container, false); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - b.backButton.setOnClickListener((v) -> nav.navigateUp()); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/ModeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/ModeViewHolder.kt new file mode 100644 index 00000000..cdb0e7b8 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/ModeViewHolder.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-10. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.LoginChooserModeItemBinding +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.ui.modules.login.LoginChooserAdapter +import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo + +class ModeViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LoginChooserModeItemBinding = LoginChooserModeItemBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "ModeViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: LoginInfo.Mode, position: Int, adapter: LoginChooserAdapter) { + b.logo.setImageResource(item.icon) + b.name.setText(item.name) + if (item.hintText == null) { + b.description.isVisible = false + } + else { + b.description.isVisible = true + b.description.setText(item.hintText) + } + b.hint.isVisible = false + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/PlatformViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/PlatformViewHolder.kt new file mode 100644 index 00000000..ea8a063d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/PlatformViewHolder.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-10. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import coil.api.load +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.LoginPlatformItemBinding +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo +import pl.szczodrzynski.edziennik.ui.modules.login.LoginPlatformAdapter + +class PlatformViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LoginPlatformItemBinding = LoginPlatformItemBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "PlatformViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: LoginInfo.Platform, position: Int, adapter: LoginPlatformAdapter) { + b.logo.load(item.icon) + b.name.text = item.name + b.description.text = item.description + b.description.isVisible = item.description != null + b.screenshotButton.isVisible = false + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/RegisterViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/RegisterViewHolder.kt new file mode 100644 index 00000000..921041c8 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/viewholder/RegisterViewHolder.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-10. + */ + +package pl.szczodrzynski.edziennik.ui.modules.login.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.LoginChooserItemBinding +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.ui.modules.login.LoginChooserAdapter +import pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo + +class RegisterViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: LoginChooserItemBinding = LoginChooserItemBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "RegisterViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: LoginInfo.Register, position: Int, adapter: LoginChooserAdapter) { + b.logo.setImageResource(item.registerLogo) + b.name.setText(item.registerName) + b.description.isVisible = false + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index cc72c46c..3e3eac0e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -55,6 +55,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog; import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog; import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog; +import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Utils; import pl.szczodrzynski.edziennik.utils.models.Date; @@ -161,7 +162,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { profileCardTitleItem = new MaterialAboutProfileItem( app.getProfile().getName(), - getString(R.string.settings_profile_subtitle_format, app.getProfile().getSubname()), + app.getProfile().getSubname(), getProfileDrawable() ); profileCardTitleItem.setOnClickAction(() -> { @@ -218,6 +219,20 @@ public class SettingsNewFragment extends MaterialAboutFragment { }) );*/ + items.add( + new MaterialAboutActionItem( + getString(R.string.settings_add_student_text), + getString(R.string.settings_add_student_subtext), + new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon.cmd_account_plus_outline) + .size(IconicsSize.dp(iconSizeDp)) + .color(IconicsColor.colorInt(iconColor)) + ) + .setOnClickAction(() -> { + startActivity(new Intent(activity, LoginActivity.class)); + }) + ); + items.add( new MaterialAboutActionItem( getString(R.string.settings_profile_notifications_text), @@ -232,6 +247,20 @@ public class SettingsNewFragment extends MaterialAboutFragment { }) ); + items.add( + new MaterialAboutActionItem( + getString(R.string.settings_profile_remove_text), + getString(R.string.settings_profile_remove_subtext), + new IconicsDrawable(activity) + .icon(SzkolnyFont.Icon.szf_delete_empty_outline) + .size(IconicsSize.dp(iconSizeDp)) + .color(IconicsColor.colorInt(iconColor)) + ) + .setOnClickAction(() -> { + new ProfileRemoveDialog(activity, app.getProfile().getId(), app.getProfile().getName()); + }) + ); + items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true)))); } else { @@ -253,20 +282,6 @@ public class SettingsNewFragment extends MaterialAboutFragment { })) ); - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_profile_remove_text), - getString(R.string.settings_profile_remove_subtext), - new IconicsDrawable(activity) - .icon(SzkolnyFont.Icon.szf_delete_empty_outline) - .size(IconicsSize.dp(iconSizeDp)) - .color(IconicsColor.colorInt(iconColor)) - ) - .setOnClickAction(() -> { - new ProfileRemoveDialog(activity, app.getProfile().getId(), app.getProfile().getName()); - }) - ); - } return items; } diff --git a/app/src/main/res/drawable/logo_librus.png b/app/src/main/res/drawable/login_mode_librus_email.png similarity index 100% rename from app/src/main/res/drawable/logo_librus.png rename to app/src/main/res/drawable/login_mode_librus_email.png diff --git a/app/src/main/res/drawable/login_mode_librus_jst.png b/app/src/main/res/drawable/login_mode_librus_jst.png new file mode 100644 index 00000000..b0ea579c Binary files /dev/null and b/app/src/main/res/drawable/login_mode_librus_jst.png differ diff --git a/app/src/main/res/drawable/logo_synergia.png b/app/src/main/res/drawable/login_mode_librus_synergia.png similarity index 100% rename from app/src/main/res/drawable/logo_synergia.png rename to app/src/main/res/drawable/login_mode_librus_synergia.png diff --git a/app/src/main/res/drawable/login_mode_mobidziennik_web.png b/app/src/main/res/drawable/login_mode_mobidziennik_web.png new file mode 100644 index 00000000..5ab3d146 Binary files /dev/null and b/app/src/main/res/drawable/login_mode_mobidziennik_web.png differ diff --git a/app/src/main/res/drawable/logo_dzienniczek.png b/app/src/main/res/drawable/login_mode_vulcan_api.png similarity index 100% rename from app/src/main/res/drawable/logo_dzienniczek.png rename to app/src/main/res/drawable/login_mode_vulcan_api.png diff --git a/app/src/main/res/drawable/logo_vulcan.png b/app/src/main/res/drawable/login_mode_vulcan_web.png similarity index 100% rename from app/src/main/res/drawable/logo_vulcan.png rename to app/src/main/res/drawable/login_mode_vulcan_web.png diff --git a/app/src/main/res/drawable/logo_mobidziennik.png b/app/src/main/res/drawable/logo_mobidziennik.png deleted file mode 100644 index 0d4d853f..00000000 Binary files a/app/src/main/res/drawable/logo_mobidziennik.png and /dev/null differ diff --git a/app/src/main/res/layout/activity_grades_editor.xml b/app/src/main/res/layout/activity_grades_editor.xml deleted file mode 100644 index 7e3a6998..00000000 --- a/app/src/main/res/layout/activity_grades_editor.xml +++ /dev/null @@ -1,194 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -