Refactor Profiles, Login Stores and Login activity (hoping it works).

This commit is contained in:
Kuba Szczodrzyński 2020-01-04 22:08:56 +01:00
parent 95a150f7d8
commit 30c5b2d1c9
115 changed files with 2314 additions and 2944 deletions

View File

@ -73,7 +73,6 @@ import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
import pl.szczodrzynski.edziennik.sync.SyncWorker;
@ -139,11 +138,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
public AppConfig appConfig; // APPCONFIG: common for all profiles
//public AppProfile profile; // current profile
public JsonObject loginStore = null;
public SharedPreferences registerStore; // sharedPreferences for REGISTER
//public Register register; // REGISTER for current profile, read from registerStore
public ProfileFull profile;
public Profile profile;
public Config config;
private static Config mConfig;
public static Config getConfig() {
@ -616,15 +614,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
db.profileDao().add(profile);
});
}
public void profileSaveFullAsync(ProfileFull profile) {
AsyncTask.execute(() -> {
profileSaveFull(profile);
});
}
public void profileSaveFull(ProfileFull profileFull) {
db.profileDao().add(profileFull);
db.loginStoreDao().add(profileFull);
}
public void profileLoadById(int id) {
profileLoadById(id, false);
@ -638,7 +627,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
return;
}*/
if (profile == null || profile.getId() != id) {
profile = db.profileDao().getFullByIdNow(id);
profile = db.profileDao().getByIdNow(id);
/*if (profile == null) {
profileLoadById(id);
return;
@ -658,12 +647,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
}
}
public void profileLoad(ProfileFull profile) {
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
this.profile = profile;
profileId = profile.getId();
}
/*public void profileRemove(int id)
{
Profile profile = db.profileDao().getFullByIdNow(id);
@ -703,11 +686,13 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
}*/
public int profileFirstId() {
return db.profileDao().getFirstId();
Integer id = db.profileDao().getFirstId();
return id == null ? 1 : id;
}
public int profileLastId() {
return db.profileDao().getLastId();
Integer id = db.profileDao().getLastId();
return id == null ? 1 : id;
}

View File

@ -69,6 +69,7 @@ fun JsonObject?.getString(key: String): String? = get(key)?.let { if (it.isJsonN
fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt }
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject }
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray }
@ -77,10 +78,23 @@ fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?
fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue
fun JsonArray?.asJsonObjectList() = this?.map { it.asJsonObject }
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: Number) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: Char) = this.addProperty(key, value)
operator fun Profile.set(key: String, value: JsonElement) = this.studentData.add(key, value)
operator fun Profile.set(key: String, value: Boolean) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: String?) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Number) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Char) = this.studentData.addProperty(key, value)
fun JsonArray.asJsonObjectList() = this.map { it.asJsonObject }
fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
@ -233,10 +247,7 @@ fun colorFromCssName(name: String): Int {
}.toInt()
}
fun MutableList<Profile>.filterOutArchived(): MutableList<Profile> {
this.removeAll { it.archived }
return this
}
fun List<Profile>.filterOutArchived() = this.filter { !it.archived }
fun Activity.isStoragePermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= 23) {

View File

@ -41,6 +41,7 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.*
@ -321,6 +322,10 @@ class MainActivity : AppCompatActivity() {
removeAllItems()
toggleGroupEnabled = false
textInputEnabled = false
onCloseListener = {
if (!app.config.ui.bottomSheetOpened)
app.config.ui.bottomSheetOpened = true
}
}
drawer.apply {
@ -328,7 +333,6 @@ class MainActivity : AppCompatActivity() {
drawerProfileListEmptyListener = {
app.config.loginFinished = false
app.saveConfig("loginFinished")
profileListEmptyListener()
}
drawerItemSelectedListener = { id, position, drawerItem ->
@ -368,10 +372,8 @@ class MainActivity : AppCompatActivity() {
if (!profileListEmpty) {
handleIntent(intent?.extras)
}
app.db.profileDao().allFull.observe(this, Observer { profiles ->
// TODO fix weird -1 profiles ???
profiles.removeAll { it.id < 0 }
drawer.setProfileList(profiles)
app.db.profileDao().all.observe(this, Observer { profiles ->
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
if (profileListEmpty) {
profileListEmpty = false
handleIntent(intent?.extras)
@ -505,13 +507,6 @@ class MainActivity : AppCompatActivity() {
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
}
EventBus.getDefault().register(this)
}
override fun onDestroy() {
EventBus.getDefault().unregister(this)
super.onDestroy()
}
var profileListEmptyListener = {
@ -520,9 +515,6 @@ class MainActivity : AppCompatActivity() {
private var profileSettingClickListener = { id: Int, view: View? ->
when (id) {
DRAWER_PROFILE_ADD_NEW -> {
LoginActivity.privacyPolicyAccepted = true
// else it would try to navigateUp onBackPressed, which it can't do. There is no parent fragment
LoginActivity.firstCompleted = false
profileListEmptyListener()
}
DRAWER_PROFILE_SYNC_ALL -> {
@ -601,6 +593,7 @@ class MainActivity : AppCompatActivity() {
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
EventBus.getDefault().removeStickyEvent(event)
navView.toolbar.apply {
subtitleFormat = R.string.toolbar_subtitle
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
@ -754,10 +747,12 @@ class MainActivity : AppCompatActivity() {
val filter = IntentFilter()
filter.addAction(Intent.ACTION_MAIN)
registerReceiver(intentReceiver, filter)
EventBus.getDefault().register(this)
super.onResume()
}
override fun onPause() {
unregisterReceiver(intentReceiver)
EventBus.getDefault().unregister(this)
super.onPause()
}
@ -817,7 +812,6 @@ class MainActivity : AppCompatActivity() {
this.runOnUiThread {
if (app.profile == null) {
LoginActivity.firstCompleted = false
if (app.config.loginFinished) {
// this shouldn't run
profileListEmptyListener()
@ -1101,6 +1095,7 @@ class MainActivity : AppCompatActivity() {
}
}
fun error(error: ApiError) = errorSnackbar.addError(error).show()
fun snackbar(text: String, actionText: String? = null, onClick: (() -> Unit)? = null) = mainSnackbar.snackbar(text, actionText, onClick)
fun snackbarDismiss() = mainSnackbar.dismiss()
}

View File

@ -17,7 +17,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
@ -221,10 +220,8 @@ public class Notifier {
app.appConfig.notifications.add(notification);
}
public void postAll(ProfileFull profile) {
public void postAll() {
Collections.sort(app.appConfig.notifications, (o1, o2) -> (o2.addedDate - o1.addedDate > 0) ? 1 : (o2.addedDate - o1.addedDate < 0) ? -1 : 0);
if (profile != null && !profile.getSyncNotifications())
return;
if (app.appConfig.notifications.size() > 40) {
app.appConfig.notifications.subList(40, app.appConfig.notifications.size() - 1).clear();

View File

@ -49,6 +49,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mLoginFinished = mLoginFinished ?: values.get("loginFinished", false); return mLoginFinished ?: false }
set(value) { set("loginFinished", value); mLoginFinished = value }
private var mPrivacyPolicyAccepted: Boolean? = null
var privacyPolicyAccepted: Boolean
get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false }
set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value }
private var mDevModePassword: String? = null
var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }

View File

@ -18,7 +18,7 @@ import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.*
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min
import kotlin.math.roundToInt
@ -38,7 +38,7 @@ class ApiService : Service() {
private val app by lazy { applicationContext as App }
private val syncingProfiles = mutableListOf<ProfileFull>()
private val syncingProfiles = mutableListOf<Profile>()
private val finishingTaskQueue = mutableListOf(
SzkolnyTask.sync(syncingProfiles),

View File

@ -47,7 +47,6 @@ class DataNotifications(val data: Data) {
val today = Date.getToday()
val todayValue = today.value
profile.currentSemester = profile.dateToSemester(today)
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)

View File

@ -23,7 +23,7 @@ import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// vulcan
// mobireg
const val SYNERGIA_API_ENABLED = true
const val SYNERGIA_API_ENABLED = false

View File

@ -74,7 +74,7 @@ object Regexes {
"""id="ctl00_CzyRodzic" value="([01])" />""".toRegex()
}
val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
"""name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9/]+)<""".toRegex(DOT_MATCHES_ALL)
"""name="ctl00\\${'$'}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL)

View File

@ -32,6 +32,8 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
}
}
override fun generateUserCode() = "$schoolName:$loginEmail:${studentId?.crc32()}"
private var mLoginEmail: String? = null
var loginEmail: String?
get() { mLoginEmail = mLoginEmail ?: loginStore.getLoginData("email", null); return mLoginEmail }

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ACCOUNT_NAME_START
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_STUDENTS_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
@ -15,7 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getShortName
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.set
class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
@ -26,25 +27,35 @@ class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_EDUDZIENNIK
var firstProfileId = loginStoreId
EdudziennikLoginWeb(data) {
web.webGet(TAG, "") { text ->
val accountName = EDUDZIENNIK_ACCOUNT_NAME_START.find(text)?.get(1)?.fixName()
val accountNameLong = EDUDZIENNIK_ACCOUNT_NAME_START.find(text)?.get(1)?.fixName()
EDUDZIENNIK_STUDENTS_START.findAll(text).forEach {
val studentId = it[1]
val studentName = it[2].fixName()
val studentNameLong = it[2].fixName()
if (studentId.isBlank() || studentName.isBlank()) return@forEach
if (studentId.isBlank() || studentNameLong.isBlank()) return@forEach
val profile = Profile()
profile.studentNameLong = studentName
profile.studentNameShort = studentName.getShortName()
profile.accountNameLong = if (studentName == accountName) null else accountName
profile.studentSchoolYear = Utils.getCurrentSchoolYear()
profile.name = studentName
profile.subname = data.loginEmail
profile.empty = true
profile.putStudentData("studentId", studentId)
val studentNameShort = studentNameLong.getShortName()
val accountName = if (accountNameLong == studentNameLong) null else accountNameLong
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.loginEmail,
studentNameLong,
studentNameShort,
accountName
).apply {
studentData["studentId"] = studentId
}
profileList.add(profile)
}

View File

@ -41,6 +41,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
loginMethods += LOGIN_METHOD_IDZIENNIK_API
}
override fun generateUserCode() = "$webSchoolName:$webUsername:$registerId"
private var mLoginExpiryTime: Long? = null
var loginExpiryTime: Long
get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L }

View File

@ -25,9 +25,6 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
}
init {
data.profile?.luckyNumber = -1
data.profile?.luckyNumberDate = null
apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json ->
if (json !is JsonObject) {
onSuccess()
@ -37,9 +34,9 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
var nextSync = System.currentTimeMillis() + 14*DAY*1000
val settings = json.getJsonObject("ustawienia")?.apply {
profile?.dateSemester1Start = getString("poczatekSemestru1")?.let { Date.fromY_m_d(it) }
profile?.dateSemester2Start = getString("koniecSemestru1")?.let { Date.fromY_m_d(it).stepForward(0, 0, 1) }
profile?.dateYearEnd = getString("koniecSemestru2")?.let { Date.fromY_m_d(it) }
getString("poczatekSemestru1")?.let { profile?.dateSemester1Start = Date.fromY_m_d(it) }
getString("koniecSemestru1")?.let { profile?.dateSemester2Start = Date.fromY_m_d(it).stepForward(0, 0, 1) }
getString("koniecSemestru2")?.let { profile?.dateYearEnd = Date.fromY_m_d(it) }
}
json.getInt("szczesliwyNumerek")?.let { luckyNumber ->

View File

@ -57,7 +57,7 @@ class IdziennikWebGetMessage(
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
else Date.fromIso(readDateString)
recipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong
recipientObject.fullName = profile.accountName ?: profile.studentNameLong
data.messageRecipientList.add(recipientObject)
message.addRecipient(recipientObject)

View File

@ -51,12 +51,12 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
return@webApiGet
}
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEach { range ->
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEachIndexed { index, range ->
val lessonRange = LessonRange(
profileId,
range.getInt("LiczbaP") ?: return@forEach,
range.getString("Poczatek")?.let { Time.fromH_m(it) } ?: return@forEach,
range.getString("Koniec")?.let { Time.fromH_m(it) } ?: return@forEach
index + 1,
range.getString("Poczatek")?.let { Time.fromH_m(it) } ?: return@forEachIndexed,
range.getString("Koniec")?.let { Time.fromH_m(it) } ?: return@forEachIndexed
)
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
}

View File

@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_SETTINGS
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_IDZIENNIK
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
@ -16,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.swapFirstLastName
class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
@ -27,6 +29,10 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_IDZIENNIK
var firstProfileId = loginStoreId
IdziennikLoginWeb(data) {
web.webGet(TAG, IDZIENNIK_WEB_SETTINGS) { text ->
//val accounts = json.getJsonArray("accounts")
@ -34,12 +40,15 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
val isParent = Regexes.IDZIENNIK_LOGIN_FIRST_IS_PARENT.find(text)?.get(1) != "0"
val accountNameLong = if (isParent)
Regexes.IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME.find(text)?.get(1)?.swapFirstLastName()?.fixName()
else
null
else null
var schoolYearStart: Int? = null
var schoolYearEnd: Int? = null
var schoolYearName: String? = null
val schoolYear = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
schoolYearName = it[2]
val schoolYearId = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
schoolYearName = it[2]+"/"+it[3]
schoolYearStart = it[2].toIntOrNull()
schoolYearEnd = it[3].toIntOrNull()
it[1].toIntOrNull()
} ?: run {
data.error(ApiError(TAG, ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR)
@ -57,18 +66,26 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
val lastName = match[4]
val className = match[5] + " " + match[6]
val profile = Profile()
profile.studentNameLong = "$firstName $lastName".fixName()
profile.studentNameShort = "$firstName ${lastName[0]}.".fixName()
profile.accountNameLong = accountNameLong
profile.studentClassName = className
profile.studentSchoolYear = schoolYearName
profile.name = profile.studentNameLong
profile.subname = data.webUsername
profile.empty = true
profile.putStudentData("studentId", studentId)
profile.putStudentData("registerId", registerId)
profile.putStudentData("schoolYearId", schoolYear)
val studentNameLong = "$firstName $lastName".fixName()
val studentNameShort = "$firstName ${lastName[0]}.".fixName()
val accountName = if (accountNameLong == studentNameLong) null else accountNameLong
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.webUsername,
studentNameLong,
studentNameShort,
accountName
).apply {
schoolYearStart?.let { studentSchoolYearStart = it }
studentClassName = className
studentData["studentId"] = studentId
studentData["registerId"] = registerId
studentData["schoolYearId"] = schoolYearId
}
profileList.add(profile)
}

View File

@ -51,6 +51,8 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
}
}
override fun generateUserCode() = "$schoolName:$apiLogin"
fun getColor(id: Int?): Int {
return when (id) {
1 -> 0xFFF0E68C

View File

@ -21,7 +21,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
init { data.profile?.also { profile ->
apiGet(TAG, "SchoolNotices") { json ->
val announcements = json.getJsonArray("SchoolNotices").asJsonObjectList()
val announcements = json.getJsonArray("SchoolNotices")?.asJsonObjectList()
announcements?.forEach { announcement ->
val longId = announcement.getString("Id") ?: return@forEach

View File

@ -20,7 +20,7 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus,
init {
apiGet(TAG, "Attendances/Types") { json ->
val attendanceTypes = json.getJsonArray("Types").asJsonObjectList()
val attendanceTypes = json.getJsonArray("Types")?.asJsonObjectList()
attendanceTypes?.forEach { attendanceType ->
val id = attendanceType.getLong("Id") ?: return@forEach

View File

@ -27,7 +27,7 @@ class LibrusApiAttendances(override val data: DataLibrus,
}
apiGet(TAG, "Attendances") { json ->
val attendances = json.getJsonArray("Attendances").asJsonObjectList()
val attendances = json.getJsonArray("Attendances")?.asJsonObjectList()
attendances?.forEach { attendance ->
val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach)

View File

@ -38,6 +38,8 @@ class LibrusApiClasses(override val data: DataLibrus,
teacherId
)
data.profile?.studentClassName = name
data.teamList.put(id, teamObject)
data.unitId = studentClass.getJsonObject("Unit").getLong("Id") ?: 0L

View File

@ -19,7 +19,7 @@ class LibrusApiClassrooms(override val data: DataLibrus,
init {
apiGet(TAG, "Classrooms") { json ->
val classrooms = json.getJsonArray("Classrooms").asJsonObjectList()
val classrooms = json.getJsonArray("Classrooms")?.asJsonObjectList()
classrooms?.forEach { classroom ->
val id = classroom.getLong("Id") ?: return@forEach

View File

@ -18,7 +18,7 @@ class LibrusApiEventTypes(override val data: DataLibrus,
init {
apiGet(TAG, "HomeWorks/Categories") { json ->
val eventTypes = json.getJsonArray("Categories").asJsonObjectList()
val eventTypes = json.getJsonArray("Categories")?.asJsonObjectList()
eventTypes?.forEach { eventType ->
val id = eventType.getLong("Id") ?: return@forEach

View File

@ -28,7 +28,7 @@ class LibrusApiEvents(override val data: DataLibrus,
}
apiGet(TAG, "HomeWorks") { json ->
val events = json.getJsonArray("HomeWorks").asJsonObjectList()
val events = json.getJsonArray("HomeWorks")?.asJsonObjectList()
events?.forEach { event ->
val id = event.getLong("Id") ?: return@forEach

View File

@ -21,7 +21,7 @@ class LibrusApiGrades(override val data: DataLibrus,
init { data.profile?.also { profile ->
apiGet(TAG, "Grades") { json ->
val grades = json.getJsonArray("Grades").asJsonObjectList()
val grades = json.getJsonArray("Grades")?.asJsonObjectList()
grades?.forEach { grade ->
val id = grade.getLong("Id") ?: return@forEach

View File

@ -22,7 +22,7 @@ class LibrusApiHomework(override val data: DataLibrus,
init {
apiGet(TAG, "HomeWorkAssignments") { json ->
val homeworkList = json.getJsonArray("HomeWorkAssignments").asJsonObjectList()
val homeworkList = json.getJsonArray("HomeWorkAssignments")?.asJsonObjectList()
homeworkList?.forEach { homework ->
val id = homework.getLong("Id") ?: return@forEach

View File

@ -23,9 +23,6 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
}
init {
data.profile?.luckyNumber = -1
data.profile?.luckyNumberDate = null
var nextSync = System.currentTimeMillis() + 2*DAY*1000
apiGet(TAG, "LuckyNumbers") { json ->

View File

@ -24,7 +24,7 @@ class LibrusApiMe(override val data: DataLibrus,
data.isPremium = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true
val isParent = account?.getInt("GroupId") == 5
data.profile?.accountNameLong =
data.profile?.accountName =
if (isParent)
buildFullName(account?.getString("FirstName"), account?.getString("LastName"))
else null

View File

@ -18,7 +18,7 @@ class LibrusApiNoticeTypes(override val data: DataLibrus,
init {
apiGet(TAG, "Notes/Categories") { json ->
val noticeTypes = json.getJsonArray("Categories").asJsonObjectList()
val noticeTypes = json.getJsonArray("Categories")?.asJsonObjectList()
noticeTypes?.forEach { noticeType ->
val id = noticeType.getLong("Id") ?: return@forEach

View File

@ -26,7 +26,7 @@ class LibrusApiNotices(override val data: DataLibrus,
}
apiGet(TAG, "Notes") { json ->
val notes = json.getJsonArray("Notes").asJsonObjectList()
val notes = json.getJsonArray("Notes")?.asJsonObjectList()
notes?.forEach { note ->
val id = note.getLong("Id") ?: return@forEach

View File

@ -22,7 +22,7 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
init {
apiGet(TAG, "ParentTeacherConferences") { json ->
val ptMeetings = json.getJsonArray("ParentTeacherConferences").asJsonObjectList()
val ptMeetings = json.getJsonArray("ParentTeacherConferences")?.asJsonObjectList()
ptMeetings?.forEach { meeting ->
val id = meeting.getLong("Id") ?: return@forEach

View File

@ -18,7 +18,7 @@ class LibrusApiSubjects(override val data: DataLibrus,
init {
apiGet(TAG, "Subjects") { json ->
val subjects = json.getJsonArray("Subjects").asJsonObjectList()
val subjects = json.getJsonArray("Subjects")?.asJsonObjectList()
subjects?.forEach { subject ->
val id = subject.getLong("Id") ?: return@forEach

View File

@ -18,7 +18,7 @@ class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus,
init {
apiGet(TAG, "TeacherFreeDays/Types") { json ->
val teacherAbsenceTypes = json.getJsonArray("Types").asJsonObjectList()
val teacherAbsenceTypes = json.getJsonArray("Types")?.asJsonObjectList()
teacherAbsenceTypes?.forEach { teacherAbsenceType ->
val id = teacherAbsenceType.getLong("Id") ?: return@forEach

View File

@ -27,7 +27,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
}
apiGet(TAG, "TeacherFreeDays") { json ->
val teacherAbsences = json.getJsonArray("TeacherFreeDays").asJsonObjectList()
val teacherAbsences = json.getJsonArray("TeacherFreeDays")?.asJsonObjectList()
teacherAbsences?.forEach { teacherAbsence ->
val id = teacherAbsence.getLong("Id") ?: return@forEach

View File

@ -23,7 +23,7 @@ class LibrusApiUnits(override val data: DataLibrus,
}
apiGet(TAG, "Units") { json ->
val units = json.getJsonArray("Units").asJsonObjectList()
val units = json.getJsonArray("Units")?.asJsonObjectList()
units?.singleOrNull { it.getLong("Id") == data.unitId }?.also { unit ->
val startPoints = unit.getJsonObject("BehaviourGradesSettings")?.getJsonObject("StartPoints")

View File

@ -18,7 +18,7 @@ class LibrusApiUsers(override val data: DataLibrus,
init {
apiGet(TAG, "Users") { json ->
val users = json.getJsonArray("Users").asJsonObjectList()
val users = json.getJsonArray("Users")?.asJsonObjectList()
users?.forEach { user ->
val id = user.getLong("Id") ?: return@forEach

View File

@ -18,7 +18,7 @@ class LibrusApiVirtualClasses(override val data: DataLibrus,
init {
apiGet(TAG, "VirtualClasses") { json ->
val virtualClasses = json.getJsonArray("VirtualClasses").asJsonObjectList()
val virtualClasses = json.getJsonArray("VirtualClasses")?.asJsonObjectList()
virtualClasses?.forEach { virtualClass ->
val id = virtualClass.getLong("Id") ?: return@forEach

View File

@ -110,7 +110,7 @@ class LibrusMessagesGetMessage(
messageObject.id
)
messageRecipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong ?: ""
messageRecipientObject.fullName = profile.accountName ?: profile.studentNameLong ?: ""
messageRecipientList.add(messageRecipientObject)
}

View File

@ -22,6 +22,10 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_LIBRUS
var firstProfileId = loginStoreId
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) {
// email login: use Portal for account list
LibrusLoginPortal(data) {
@ -55,18 +59,24 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
val login = account.getString("login") ?: continue
val token = account.getString("accessToken") ?: continue
val tokenTime = (accountDataTime ?: 0) + DAY
val name = account.getString("studentName")?.fixName() ?: ""
val studentNameLong = account.getString("studentName").fixName()
val studentNameShort = studentNameLong.getShortName()
val profile = Profile()
profile.studentNameLong = name
profile.studentNameShort = name.getShortName()
profile.name = profile.studentNameLong
profile.subname = data.portalEmail
profile.empty = true
profile.putStudentData("accountId", id)
profile.putStudentData("accountLogin", login)
profile.putStudentData("accountToken", token)
profile.putStudentData("accountTokenTime", tokenTime)
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.portalEmail,
studentNameLong,
studentNameShort,
null
).apply {
studentData["accountId"] = id
studentData["accountLogin"] = login
studentData["accountToken"] = token
studentData["accountTokenTime"] = tokenTime
}
profileList.add(profile)
}
@ -80,32 +90,36 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
LibrusLoginApi(data) {
api.apiGet(TAG, "Me") { json ->
val profile = Profile()
val me = json.getJsonObject("Me")
val account = me?.getJsonObject("Account")
val user = me?.getJsonObject("User")
profile.putStudentData("isPremium", account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true)
val login = account.getString("Login")
val isParent = account?.getInt("GroupId") in 5..6
val isParent = account?.getInt("GroupId") == 5
profile.accountNameLong =
if (isParent)
buildFullName(account?.getString("FirstName"), account?.getString("LastName"))
else null
val studentNameLong = buildFullName(user?.getString("FirstName"), user?.getString("LastName"))
val studentNameShort = studentNameLong.getShortName()
val accountNameLong = if (isParent)
buildFullName(account?.getString("FirstName"), account?.getString("LastName"))
else null
profile.studentNameLong =
buildFullName(user?.getString("FirstName"), user?.getString("LastName"))
profile.studentNameShort = profile.studentNameLong?.getShortName()
profile.name = profile.studentNameLong
profile.subname = account.getString("Login")
profile.empty = true
profile.putStudentData("accountId", account.getInt("Id") ?: 0)
profile.putStudentData("accountLogin", profile.subname)
profile.putStudentData("accountToken", data.apiAccessToken)
profile.putStudentData("accountRefreshToken", data.apiRefreshToken)
profile.putStudentData("accountTokenTime", data.apiTokenExpiryTime)
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
login,
studentNameLong,
studentNameShort,
accountNameLong
).apply {
studentData["isPremium"] = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true
studentData["accountId"] = account.getInt("Id") ?: 0
studentData["accountLogin"] = login
studentData["accountToken"] = data.apiAccessToken
studentData["accountTokenTime"] = data.apiTokenExpiryTime
studentData["accountRefreshToken"] = data.apiRefreshToken
}
profileList.add(profile)
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))

View File

@ -7,9 +7,9 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik
import android.util.LongSparseArray
import androidx.core.util.isNotEmpty
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_MOBIDZIENNIK_WEB
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
@ -30,6 +30,8 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
}
}
override fun generateUserCode() = "$loginServerName:$loginUsername:$studentId"
val teachersMap = LongSparseArray<String>()
val subjectsMap = LongSparseArray<String>()

View File

@ -53,6 +53,7 @@ class MobidziennikApiTeams(val data: DataMobidziennik, tableTeams: List<String>?
if (team.type == 1) {
data.profile?.studentNumber = studentNumber
data.teamClass = team
data.profile?.studentClassName = team.name
}
data.teamList.put(teamId, team)
}

View File

@ -8,31 +8,31 @@ import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String) {
init {
data.profile?.luckyNumber = -1
data.profile?.luckyNumberDate = null
Regexes.MOBIDZIENNIK_LUCKY_NUMBER.find(text)?.let {
try {
val luckyNumber = it.groupValues[1].toInt()
Regexes.MOBIDZIENNIK_LUCKY_NUMBER.find(text)?.get(1)?.toIntOrNull()?.let {
val luckyNumberObject = LuckyNumber(
data.profileId,
Date.getToday(),
it
)
val luckyNumberObject = LuckyNumber(
data.profileId,
Date.getToday(),
luckyNumber
)
data.luckyNumberList.add(luckyNumberObject)
data.metadataList.add(
Metadata(
data.profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
data.profile?.empty ?: false,
data.profile?.empty ?: false,
System.currentTimeMillis()
))
data.luckyNumberList.add(luckyNumberObject)
data.metadataList.add(
Metadata(
data.profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
data.profile?.empty ?: false,
data.profile?.empty ?: false,
System.currentTimeMillis()
))
} catch (_: Exception){}
}
}
}

View File

@ -68,7 +68,7 @@ class MobidziennikWebGetMessage(
message.id
)
recipient.fullName = profile?.accountNameLong ?: profile?.studentNameLong ?: ""
recipient.fullName = profile?.accountName ?: profile?.studentNameLong ?: ""
messageRecipientList.add(recipient)
} else {

View File

@ -1,13 +1,15 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.utils.models.Date
class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) {
companion object {
@ -18,6 +20,10 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_MOBIDZIENNIK
var firstProfileId = loginStoreId
MobidziennikLoginWeb(data) {
web.webGet(TAG, "/api/zrzutbazy") { text ->
val tables = text.split("T@B#LA")
@ -32,6 +38,21 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un
}
}
var dateSemester1Start: Date? = null
var dateSemester2Start: Date? = null
var dateYearEnd: Date? = null
for (row in tables[3].split("\n")) {
if (row.isEmpty())
continue
val cols = row.split("|")
when (cols[1]) {
"semestr1_poczatek" -> dateSemester1Start = Date.fromYmd(cols[3])
"semestr2_poczatek" -> dateSemester2Start = Date.fromYmd(cols[3])
"koniec_roku_szkolnego" -> dateYearEnd = Date.fromYmd(cols[3])
}
}
tables[8].split("\n").forEach { student ->
if (student.isEmpty())
return@forEach
@ -39,15 +60,28 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un
if (student1.size == 2)
return@forEach
val profile = Profile()
profile.studentNameLong = "${student1[2]} ${student1[4]}".fixName()
profile.studentNameShort = "${student1[2]} ${student1[4][0]}.".fixName()
profile.accountNameLong = if (accountNameLong == profile.studentNameLong) null else accountNameLong
profile.studentSchoolYear = Utils.getCurrentSchoolYear()
profile.name = profile.studentNameLong
profile.subname = data.loginUsername
profile.empty = true
profile.putStudentData("studentId", student1[0].toInt())
val studentNameLong = "${student1[2]} ${student1[4]}".fixName()
val studentNameShort = "${student1[2]} ${student1[4][0]}.".fixName()
val accountName = if (accountNameLong == studentNameLong) null else accountNameLong
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.loginUsername,
studentNameLong,
studentNameShort,
accountName
).apply {
studentData["studentId"] = student1[0].toInt()
}
dateSemester1Start?.let {
profile.dateSemester1Start = it
profile.studentSchoolYearStart = it.year
}
dateSemester2Start?.let { profile.dateSemester2Start = it }
dateYearEnd?.let { profile.dateYearEnd = it }
profileList.add(profile)
}

View File

@ -6,10 +6,10 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.template
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_TEMPLATE_API
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_TEMPLATE_WEB
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
@ -40,6 +40,8 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
loginMethods += LOGIN_METHOD_TEMPLATE_API
}
override fun generateUserCode() = "TEMPLATE:DO_NOT_USE"
/* __ __ _
\ \ / / | |
\ \ /\ / /__| |__

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
@ -13,8 +14,8 @@ import pl.szczodrzynski.edziennik.isNotNullNorEmpty
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
fun isApiLoginValid() = /*apiCertificateExpiryTime-30 > currentTimeUnix()
&&*/ apiCertificateKey.isNotNullNorEmpty()
fun isApiLoginValid() = currentSemesterEndDate-30 > currentTimeUnix()
&& apiCertificateKey.isNotNullNorEmpty()
&& apiCertificatePrivate.isNotNullNorEmpty()
&& symbol.isNotNullNorEmpty()
@ -25,6 +26,8 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
}
}
override fun generateUserCode() = "$schoolName:$studentId"
/**
* A UONET+ client symbol.
*
@ -107,6 +110,16 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() { mStudentSemesterNumber = mStudentSemesterNumber ?: profile?.getStudentData("studentSemesterNumber", 0); return mStudentSemesterNumber ?: 0 }
set(value) { profile?.putStudentData("studentSemesterNumber", value) ?: return; mStudentSemesterNumber = value }
/**
* Date of the end of the current semester ([studentSemesterNumber]).
*
* After this date, an API refresh of student list is required.
*/
private var mCurrentSemesterEndDate: Long? = null
var currentSemesterEndDate: Long
get() { mCurrentSemesterEndDate = mCurrentSemesterEndDate ?: profile?.getStudentData("currentSemesterEndDate", 0L); return mCurrentSemesterEndDate ?: 0L }
set(value) { profile?.putStudentData("currentSemesterEndDate", value) ?: return; mCurrentSemesterEndDate = value }
/* _____ _____ ____
/\ | __ \_ _| |___ \
/ \ | |__) || | __ ____) |
@ -139,6 +152,12 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() { mApiCertificateKey = mApiCertificateKey ?: loginStore.getLoginData("certificateKey", null); return mApiCertificateKey }
set(value) { loginStore.putLoginData("certificateKey", value); mApiCertificateKey = value }
/**
* This is not meant for normal usage.
*
* It provides a backward compatibility (<4.0) in order
* to migrate and use private keys instead of PFX.
*/
private var mApiCertificatePfx: String? = null
var apiCertificatePfx: String?
get() { mApiCertificatePfx = mApiCertificatePfx ?: loginStore.getLoginData("certificatePfx", null); return mApiCertificatePfx }
@ -149,11 +168,6 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() { mApiCertificatePrivate = mApiCertificatePrivate ?: loginStore.getLoginData("certificatePrivate", null); return mApiCertificatePrivate }
set(value) { loginStore.putLoginData("certificatePrivate", value); mApiCertificatePrivate = value }
private var mApiCertificateExpiryTime: Int? = null
var apiCertificateExpiryTime: Int
get() { mApiCertificateExpiryTime = mApiCertificateExpiryTime ?: loginStore.getLoginData("certificateExpiryTime", 0); return mApiCertificateExpiryTime ?: 0 }
set(value) { loginStore.putLoginData("certificateExpiryTime", value); mApiCertificateExpiryTime = value }
val apiUrl: String?
get() {
val url = when (apiToken?.substring(0, 3)) {

View File

@ -39,7 +39,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
"LoginId" to data.studentLoginId,
"IdUczen" to data.studentId
)) { json, _ ->
json.getJsonArray("Data").asJsonObjectList()?.forEach { message ->
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { message ->
val id = message.getLong("WiadomoscId") ?: return@forEach
val subject = message.getString("Tytul") ?: ""
val body = message.getString("Tresc") ?: ""

View File

@ -36,7 +36,7 @@ class VulcanApiSendMessage(
}
}
val params = mapOf(
"NadawcaWiadomosci" to (profile?.accountNameLong ?: profile?.studentNameLong ?: ""),
"NadawcaWiadomosci" to (profile?.accountName ?: profile?.studentNameLong ?: ""),
"Tytul" to subject,
"Tresc" to text,
"Adresaci" to recipientsArray,

View File

@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_NO_STUDENTS_IN_ACCOUNT
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_STUDENT_LIST
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
@ -25,11 +26,15 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_VULCAN
var firstProfileId = loginStoreId
VulcanLoginApi(data) {
api.apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, response ->
val students = json.getJsonArray("Data")
if (students == null || students.size() < 1) {
if (students == null || students.isEmpty()) {
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
.withResponse(response)
.withApiResponse(json))
@ -53,49 +58,56 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
val userLogin = student.getString("UzytkownikLogin") ?: ""
val currentSemesterStartDate = student.getLong("OkresDataOd") ?: return@forEach
val currentSemesterEndDate = (student.getLong("OkresDataDo")
?: return@forEach) + 86400
val currentSemesterEndDate = (student.getLong("OkresDataDo") ?: return@forEach) + 86400
val studentSemesterNumber = student.getInt("OkresNumer") ?: return@forEach
val newProfile = Profile()
newProfile.empty = true
val isParent = student.getString("UzytkownikRola") == "opiekun"
val userName = if (isParent)
val accountName = if (isParent)
student.getString("UzytkownikNazwa")?.swapFirstLastName()?.fixName()
else
null
newProfile.accountNameLong = userName
newProfile.studentClassName = studentClassName
val today = Date.getToday()
newProfile.studentSchoolYear = "${today.year}/${today.year+1}"
newProfile.putStudentData("studentId", studentId)
newProfile.putStudentData("studentLoginId", studentLoginId)
newProfile.putStudentData("studentClassId", studentClassId)
newProfile.putStudentData("studentSemesterId", studentSemesterId)
newProfile.putStudentData("schoolSymbol", schoolSymbol)
newProfile.putStudentData("schoolName", schoolName)
newProfile.putStudentData("currentSemesterEndDate", currentSemesterEndDate)
newProfile.putStudentData("studentSemesterNumber", studentSemesterNumber)
else null
var dateSemester1Start: Date? = null
var dateSemester2Start: Date? = null
var dateYearEnd: Date? = null
when (studentSemesterNumber) {
1 -> {
newProfile.dateSemester1Start = Date.fromMillis(currentSemesterStartDate * 1000)
newProfile.dateSemester2Start = Date.fromMillis(currentSemesterEndDate * 1000)
dateSemester1Start = Date.fromMillis(currentSemesterStartDate * 1000)
dateSemester2Start = Date.fromMillis(currentSemesterEndDate * 1000)
}
2 -> {
newProfile.dateSemester2Start = Date.fromMillis(currentSemesterStartDate * 1000)
newProfile.dateYearEnd = Date.fromMillis(currentSemesterEndDate * 1000)
dateSemester2Start = Date.fromMillis(currentSemesterStartDate * 1000)
dateYearEnd = Date.fromMillis(currentSemesterEndDate * 1000)
}
}
newProfile.studentNameLong = studentNameLong
newProfile.studentNameShort = studentNameShort
newProfile.name = studentNameLong
newProfile.subname = userLogin
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
userLogin,
studentNameLong,
studentNameShort,
accountName
).apply {
this.studentClassName = studentClassName
studentData["studentId"] = studentId
studentData["studentLoginId"] = studentLoginId
studentData["studentClassId"] = studentClassId
studentData["studentSemesterId"] = studentSemesterId
studentData["studentSemesterNumber"] = studentSemesterNumber
studentData["schoolSymbol"] = schoolSymbol
studentData["schoolName"] = schoolName
studentData["currentSemesterEndDate"] = currentSemesterEndDate
}
dateSemester1Start?.let {
profile.dateSemester1Start = it
profile.studentSchoolYearStart = it.year
}
dateSemester2Start?.let { profile.dateSemester2Start = it }
dateYearEnd?.let { profile.dateYearEnd = it }
profileList.add(newProfile)
profileList.add(profile)
}
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-3.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_NO_STUDENTS_IN_ACCOUNT
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_STUDENT_LIST
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.models.Date
class VulcanApiUpdateSemester(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
companion object {
const val TAG = "VulcanApiUpdateSemester"
}
init { data.profile?.also { profile ->
apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, response ->
val students = json.getJsonArray("Data")
if (students == null || students.isEmpty()) {
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
.withResponse(response)
.withApiResponse(json))
return@apiGet
}
students.asJsonObjectList().firstOrNull {
it.getInt("Id") == data.studentId
}?.let { student ->
val studentClassId = student.getInt("IdOddzial") ?: return@let
val studentClassName = student.getString("OkresPoziom").toString() + (student.getString("OddzialSymbol") ?: return@let)
val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") ?: return@let
val currentSemesterStartDate = student.getLong("OkresDataOd") ?: return@let
val currentSemesterEndDate = (student.getLong("OkresDataDo") ?: return@let) + 86400
val studentSemesterNumber = student.getInt("OkresNumer") ?: return@let
var dateSemester1Start: Date? = null
var dateSemester2Start: Date? = null
var dateYearEnd: Date? = null
when (studentSemesterNumber) {
1 -> {
dateSemester1Start = Date.fromMillis(currentSemesterStartDate * 1000)
dateSemester2Start = Date.fromMillis(currentSemesterEndDate * 1000)
}
2 -> {
dateSemester2Start = Date.fromMillis(currentSemesterStartDate * 1000)
dateYearEnd = Date.fromMillis(currentSemesterEndDate * 1000)
}
}
data.studentClassId = studentClassId
data.studentSemesterId = studentSemesterId
data.studentSemesterNumber = studentSemesterNumber
data.currentSemesterEndDate = currentSemesterEndDate
profile.studentClassName = studentClassName
dateSemester1Start?.let {
profile.dateSemester1Start = it
profile.studentSchoolYearStart = it.year
}
dateSemester2Start?.let { profile.dateSemester2Start = it }
dateYearEnd?.let { profile.dateYearEnd = it }
}
onSuccess()
}
} ?: onSuccess()}
}

View File

@ -10,10 +10,10 @@ import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.JsonCallbackHandler
import io.github.wulkanowy.signer.android.getPrivateKeyFromCert
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
@ -32,6 +32,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
onSuccess()
}
else {
// < v4.0 - PFX to Private Key migration
if (data.apiCertificatePfx.isNotNullNorEmpty()) {
try {
data.apiCertificatePrivate = getPrivateKeyFromCert(
@ -46,6 +47,16 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
return@run
}
}
if (data.apiCertificateKey.isNotNullNorEmpty()
&& data.apiCertificatePrivate.isNotNullNorEmpty()
&& data.symbol.isNotNullNorEmpty()) {
// (see data.isApiLoginValid())
// the semester end date is over
VulcanApiUpdateSemester(data, onSuccess)
return@run
}
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
loginWithToken()
}
@ -107,12 +118,10 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
}
data.apiCertificateKey = cert.getString("CertyfikatKlucz")
data.apiCertificatePfx = cert.getString("CertyfikatPfx")
data.apiCertificateExpiryTime = 1598832000
data.apiToken = data.apiToken?.substring(0, 3)
data.apiCertificatePrivate = getPrivateKeyFromCert(
if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
data.apiCertificatePfx ?: ""
cert.getString("CertyfikatPfx") ?: ""
)
data.loginStore.removeLoginData("certificatePfx")
data.loginStore.removeLoginData("devicePin")

View File

@ -46,7 +46,7 @@ import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLException
open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) {
abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) {
companion object {
private const val TAG = "Data"
}
@ -221,6 +221,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
return // return on first login
profile.empty = false
profile.userCode = generateUserCode()
db.profileDao().add(profile)
db.loginStoreDao().add(loginStore)
@ -230,31 +231,18 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
name = profile.name
subname = profile.subname
syncEnabled = profile.syncEnabled
loggedIn = profile.loggedIn
empty = profile.empty
archived = profile.archived
studentNameLong = profile.studentNameLong
studentNameShort = profile.studentNameShort
studentNumber = profile.studentNumber
studentData = profile.studentData
accountNameLong = profile.accountNameLong
yearAverageMode = profile.yearAverageMode
currentSemester = profile.currentSemester
attendancePercentage = profile.attendancePercentage
accountName = profile.accountName
dateSemester1Start = profile.dateSemester1Start
dateSemester2Start = profile.dateSemester2Start
dateYearEnd = profile.dateYearEnd
luckyNumberEnabled = profile.luckyNumberEnabled
luckyNumber = profile.luckyNumber
luckyNumberDate = profile.luckyNumberDate
lastFullSync = profile.lastFullSync
lastReceiversSync = profile.lastReceiversSync
}
}
if (loginStore.id == app.profile?.loginStoreId) {
app.loginStore = loginStore.data
app.profile.loginStoreData = loginStore.data
}
// always present and not empty, during every sync
db.endpointTimerDao().addAll(endpointTimers)
@ -372,6 +360,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
}
}
abstract fun generateUserCode(): String
fun cancel() {
d("Data", "Cancelled")
cancelled = true

View File

@ -8,13 +8,12 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
class Szkolny(val app: App, val callback: EdziennikCallback) {
private val api = SzkolnyApi(app)
fun sync(profileList: List<ProfileFull>) {
fun sync(profileList: List<Profile>) {
val profiles = profileList.filter { it.registration == Profile.REGISTRATION_ENABLED }
if (profiles.isNotEmpty()) {
val events = api.getEvents(profiles)

View File

@ -20,7 +20,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import retrofit2.Retrofit
@ -55,7 +55,7 @@ class SzkolnyApi(val app: App) {
api = retrofit.create()
}
fun getEvents(profiles: List<ProfileFull>): List<EventFull> {
fun getEvents(profiles: List<Profile>): List<EventFull> {
val teams = app.db.teamDao().allNow
val notifications = app.db.notificationDao().getNotPostedNow()
@ -71,10 +71,10 @@ class SzkolnyApi(val app: App) {
appVersionCode = BuildConfig.VERSION_CODE,
syncInterval = app.config.sync.interval
),
userCodes = profiles.map { it.usernameId },
userCodes = profiles.map { it.userCode },
users = profiles.map { profile ->
ServerSyncRequest.User(
profile.usernameId,
profile.userCode,
profile.studentNameLong ?: "",
profile.studentNameShort ?: "",
profile.loginStoreType,
@ -97,7 +97,7 @@ class SzkolnyApi(val app: App) {
seen = profile?.empty ?: false
notified = profile?.empty ?: false
if (profile?.usernameId == event.sharedBy) sharedBy = "self"
if (profile?.userCode == event.sharedBy) sharedBy = "self"
})
}
}

View File

@ -45,11 +45,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
} else {
// get the requested profile and login store
val profile = app.db.profileDao().getFullByIdNow(profileId)
val profile = app.db.profileDao().getByIdNow(profileId) ?: return
this.profile = profile
if (profile == null) {
return
}
val loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) ?: return
this.loginStore = loginStore
// save the profile ID and name as the current task's

View File

@ -11,11 +11,11 @@ import android.os.Build.VERSION_CODES.O
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.ApiService
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
abstract class IApiTask(open val profileId: Int) {
var taskId: Int = 0
var profile: ProfileFull? = null
var profile: Profile? = null
var taskName: String? = null
/**

View File

@ -8,14 +8,13 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.szkolny.Szkolny
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
class SzkolnyTask(val request: Any) : IApiTask(-1) {
companion object {
private const val TAG = "SzkolnyTask"
fun sync(profiles: List<ProfileFull>) = SzkolnyTask(SyncRequest(profiles))
fun sync(profiles: List<Profile>) = SzkolnyTask(SyncRequest(profiles))
/*fun shareEvent(event: EventFull) = SzkolnyTask(ShareEventRequest(event))
fun unshareEvent(event: EventFull) = SzkolnyTask(UnshareEventRequest(event))*/
}
@ -40,7 +39,7 @@ class SzkolnyTask(val request: Any) : IApiTask(-1) {
}
}
data class SyncRequest(val profiles: List<ProfileFull>)
data class SyncRequest(val profiles: List<Profile>)
/*data class ShareEventRequest(val event: EventFull)
data class UnshareEventRequest(val event: EventFull)*/
}

View File

@ -1,6 +1,7 @@
package pl.szczodrzynski.edziennik.data.db;
import android.content.Context;
import android.database.Cursor;
import androidx.annotation.NonNull;
import androidx.room.Database;
@ -10,6 +11,7 @@ import androidx.room.TypeConverters;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import pl.szczodrzynski.edziennik.ExtensionsKt;
import pl.szczodrzynski.edziennik.config.db.ConfigDao;
import pl.szczodrzynski.edziennik.config.db.ConfigEntry;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate;
@ -108,7 +110,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
AttendanceType.class,
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
ConfigEntry.class,
Metadata.class}, version = 71)
Metadata.class}, version = 72)
@TypeConverters({
ConverterTime.class,
ConverterDate.class,
@ -849,6 +851,114 @@ public abstract class AppDb extends RoomDatabase {
database.execSQL("INSERT INTO config (profileId, `key`, value) VALUES (-1, \"runSync\", \"true\");");
}
};
private static final Migration MIGRATION_71_72 = new Migration(71, 72) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE loginStores RENAME to _loginStores;");
database.execSQL("CREATE TABLE loginStores(" +
"loginStoreId INTEGER NOT NULL," +
"loginStoreType INTEGER NOT NULL," +
"loginStoreMode INTEGER NOT NULL," +
"loginStoreData TEXT NOT NULL," +
"PRIMARY KEY(loginStoreId));");
database.execSQL("INSERT INTO loginStores " +
"(loginStoreId, loginStoreType, loginStoreMode, loginStoreData) " +
"SELECT loginStoreId, loginStoreType, loginStoreMode, loginStoreData " +
"FROM _loginStores;");
database.execSQL("DROP TABLE _loginStores;");
database.execSQL("ALTER TABLE profiles RENAME TO _profiles_old;");
database.execSQL("CREATE TABLE profiles (\n" +
"profileId INTEGER NOT NULL, name TEXT NOT NULL, subname TEXT, image TEXT DEFAULT NULL, \n" +
"studentNameLong TEXT NOT NULL, studentNameShort TEXT NOT NULL, accountName TEXT, \n" +
"studentData TEXT NOT NULL, empty INTEGER NOT NULL DEFAULT 1, archived INTEGER NOT NULL DEFAULT 0, \n" +
"syncEnabled INTEGER NOT NULL DEFAULT 1, enableSharedEvents INTEGER NOT NULL DEFAULT 1, registration INTEGER NOT NULL DEFAULT 0, \n" +
"userCode TEXT NOT NULL DEFAULT \"\", studentNumber INTEGER NOT NULL DEFAULT -1, studentClassName TEXT DEFAULT NULL, \n" +
"studentSchoolYearStart INTEGER NOT NULL, dateSemester1Start TEXT NOT NULL, dateSemester2Start TEXT NOT NULL, \n" +
"dateYearEnd TEXT NOT NULL, disabledNotifications TEXT DEFAULT NULL, lastReceiversSync INTEGER NOT NULL DEFAULT 0, \n" +
"loginStoreId INTEGER NOT NULL, loginStoreType INTEGER NOT NULL, PRIMARY KEY(profileId));");
database.execSQL("INSERT INTO profiles (profileId, name, subname, image, studentNameLong, studentNameShort, accountName, \n" +
"userCode, studentData, empty, archived, syncEnabled, enableSharedEvents, registration, studentNumber, studentSchoolYearStart, \n" +
"dateSemester1Start, dateSemester2Start, dateYearEnd, lastReceiversSync, loginStoreId, loginStoreType \n" +
") SELECT profileId, name, subname, image, studentNameLong, studentNameShort, accountNameLong, \"\" AS userCode, studentData, \n" +
"empty, archived, syncEnabled, enableSharedEvents, registration, studentNumber, SUBSTR(dateSemester1Start, 0, 5) AS studentSchoolYearStart, \n" +
"dateSemester1Start, dateSemester2Start, dateYearEnd, lastReceiversSync, _profiles_old.loginStoreId, loginStoreType FROM _profiles_old \n" +
"JOIN loginStores ON loginStores.loginStoreId = _profiles_old.loginStoreId \n" +
"WHERE profileId >= 0;");
database.execSQL("DROP TABLE _profiles_old;");
// MIGRACJA userCode - mobidziennik
database.execSQL("DROP TABLE IF EXISTS _userCodes;");
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, serverName TEXT, username TEXT, studentId TEXT);");
database.execSQL("DELETE FROM _userCodes;");
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 1;");
database.execSQL("UPDATE _userCodes SET serverName = SUBSTR(loginData, instr(loginData, '\"serverName\":\"')+14);");
database.execSQL("UPDATE _userCodes SET serverName = SUBSTR(serverName, 0, instr(serverName, '\",')+instr(serverName, '\"}')-(instr(serverName, '\"}')*min(instr(serverName, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET username = SUBSTR(loginData, instr(loginData, '\"username\":\"')+12);");
database.execSQL("UPDATE _userCodes SET username = SUBSTR(username, 0, instr(username, '\",')+instr(username, '\"}')-(instr(username, '\"}')*min(instr(username, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":')+12);");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, ',')+instr(studentId, '}')-(instr(studentId, '}')*min(instr(studentId, ','), 1)));");
database.execSQL("UPDATE _userCodes SET userCode = serverName||\":\"||username||\":\"||studentId;");
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);");
// MIGRACJA userCode - librus
database.execSQL("DROP TABLE IF EXISTS _userCodes;");
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, accountLogin TEXT);");
database.execSQL("DELETE FROM _userCodes;");
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 2;");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET accountLogin = SUBSTR(studentData, instr(studentData, '\"accountLogin\":\"')+16);");
database.execSQL("UPDATE _userCodes SET accountLogin = SUBSTR(accountLogin, 0, instr(accountLogin, '\",')+instr(accountLogin, '\"}')-(instr(accountLogin, '\"}')*min(instr(accountLogin, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||accountLogin;");
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);");
// MIGRACJA userCode - iuczniowie
database.execSQL("DROP TABLE IF EXISTS _userCodes;");
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, username TEXT, registerId TEXT);");
database.execSQL("DELETE FROM _userCodes;");
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 3;");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(loginData, instr(loginData, '\"schoolName\":\"')+14);");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET username = SUBSTR(loginData, instr(loginData, '\"username\":\"')+12);");
database.execSQL("UPDATE _userCodes SET username = SUBSTR(username, 0, instr(username, '\",')+instr(username, '\"}')-(instr(username, '\"}')*min(instr(username, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET registerId = SUBSTR(studentData, instr(studentData, '\"registerId\":')+13);");
database.execSQL("UPDATE _userCodes SET registerId = SUBSTR(registerId, 0, instr(registerId, ',')+instr(registerId, '}')-(instr(registerId, '}')*min(instr(registerId, ','), 1)));");
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||username||\":\"||registerId;");
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);");
// MIGRACJA userCode - vulcan
database.execSQL("DROP TABLE IF EXISTS _userCodes;");
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, studentId TEXT);");
database.execSQL("DELETE FROM _userCodes;");
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 4;");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":')+12);");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, ',')+instr(studentId, '}')-(instr(studentId, '}')*min(instr(studentId, ','), 1)));");
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||studentId;");
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);");
// MIGRACJA userCode - edudziennik
database.execSQL("DROP TABLE IF EXISTS _userCodes;");
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, email TEXT, studentId TEXT);");
database.execSQL("DELETE FROM _userCodes;");
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 5;");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);");
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET email = SUBSTR(loginData, instr(loginData, '\"email\":\"')+9);");
database.execSQL("UPDATE _userCodes SET email = SUBSTR(email, 0, instr(email, '\",')+instr(email, '\"}')-(instr(email, '\"}')*min(instr(email, '\",'), 1)));");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":\"')+13);");
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, '\",')+instr(studentId, '\"}')-(instr(studentId, '\"}')*min(instr(studentId, '\",'), 1)));");
// CRC32 Student IDs
try (Cursor cursor = database.query("SELECT profileId, studentId FROM _userCodes;")) {
while (cursor.moveToNext()) {
int profileId = cursor.getInt(0);
long crc = ExtensionsKt.crc32(cursor.getString(1));
database.execSQL("UPDATE _userCodes SET studentId = "+crc+" WHERE profileId = "+profileId);
}
}
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||email||\":\"||studentId;");
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);");
database.execSQL("DROP TABLE _userCodes;");
}
};
public static AppDb getDatabase(final Context context) {
@ -917,7 +1027,8 @@ public abstract class AppDb extends RoomDatabase {
MIGRATION_67_68,
MIGRATION_68_69,
MIGRATION_69_70,
MIGRATION_70_71
MIGRATION_70_71,
MIGRATION_71_72
)
.allowMainThreadQueries()
//.fallbackToDestructiveMigration()

View File

@ -1,197 +0,0 @@
package pl.szczodrzynski.edziennik.data.db.modules.login;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
@Entity(tableName = "loginStores",
primaryKeys = {"loginStoreId"})
public class LoginStore {
@ColumnInfo(name = "loginStoreId")
public int id = -1;
@ColumnInfo(name = "loginStoreType")
public int type = -1;
public final static int LOGIN_TYPE_MOBIDZIENNIK = 1;
public final static int LOGIN_TYPE_LIBRUS = 2;
public final static int LOGIN_TYPE_VULCAN = 4;
public final static int LOGIN_TYPE_IUCZNIOWIE = 3;
public final static int LOGIN_TYPE_EDUDZIENNIK = 5;
public final static int LOGIN_TYPE_DEMO = 20;
@ColumnInfo(name = "loginStoreData")
public JsonObject data;
@ColumnInfo(name = "loginStoreMode")
public int mode = 0;
public static final int LOGIN_MODE_LIBRUS_EMAIL = 0;
public static final int LOGIN_MODE_LIBRUS_SYNERGIA = 1;
public static final int LOGIN_MODE_LIBRUS_JST = 2;
public LoginStore(int id, int type, JsonObject data) {
this.id = id;
this.type = type;
this.data = data;
}
public static LoginStore fromProfileFull(ProfileFull profileFull) {
return new LoginStore(profileFull.getLoginStoreId(), profileFull.getLoginStoreType(), profileFull.getLoginStoreData());
}
public void copyFrom(Bundle args) {
for (String key: args.keySet()) {
Object o = args.get(key);
if (o instanceof String) {
putLoginData(key, (String) o);
}
else if (o instanceof Integer) {
putLoginData(key, (Integer) o);
}
else if (o instanceof Long) {
putLoginData(key, (Long) o);
}
else if (o instanceof Float) {
putLoginData(key, (Float) o);
}
else if (o instanceof Boolean) {
putLoginData(key, (Boolean) o);
}
}
}
public boolean hasLoginData(String key) {
if (data == null)
return false;
return data.has(key);
}
@Nullable
public String getLoginData(String key, @Nullable String defaultValue) {
if (data == null)
return defaultValue;
JsonElement element = data.get(key);
if (element != null && !(element instanceof JsonNull)) {
return element.getAsString();
}
return defaultValue;
}
@Nullable
public int getLoginData(String key, int defaultValue) {
if (data == null)
return defaultValue;
JsonElement element = data.get(key);
if (element != null && !(element instanceof JsonNull)) {
return element.getAsInt();
}
return defaultValue;
}
@Nullable
public long getLoginData(String key, long defaultValue) {
if (data == null)
return defaultValue;
JsonElement element = data.get(key);
if (element != null && !(element instanceof JsonNull)) {
return element.getAsLong();
}
return defaultValue;
}
@Nullable
public float getLoginData(String key, float defaultValue) {
if (data == null)
return defaultValue;
JsonElement element = data.get(key);
if (element != null && !(element instanceof JsonNull)) {
return element.getAsFloat();
}
return defaultValue;
}
public boolean getLoginData(String key, boolean defaultValue) {
if (data == null)
return defaultValue;
JsonElement element = data.get(key);
if (element != null && !(element instanceof JsonNull)) {
return element.getAsBoolean();
}
return defaultValue;
}
public void putLoginData(String key, String value) {
forceLoginStore();
data.addProperty(key, value);
}
public void putLoginData(String key, int value) {
forceLoginStore();
data.addProperty(key, value);
}
public void putLoginData(String key, long value) {
forceLoginStore();
data.addProperty(key, value);
}
public void putLoginData(String key, float value) {
forceLoginStore();
data.addProperty(key, value);
}
public void putLoginData(String key, boolean value) {
forceLoginStore();
data.addProperty(key, value);
}
public void removeLoginData(String key) {
if (data == null)
return;
data.remove(key);
}
public void clearLoginStore() {
data = new JsonObject();
}
private void forceLoginStore() {
if (data == null) {
clearLoginStore();
}
}
public String type() {
switch (type) {
case LOGIN_TYPE_MOBIDZIENNIK:
return "LOGIN_TYPE_MOBIDZIENNIK";
case LOGIN_TYPE_LIBRUS:
return "LOGIN_TYPE_LIBRUS";
case LOGIN_TYPE_IUCZNIOWIE:
return "LOGIN_TYPE_IDZIENNIK";
case LOGIN_TYPE_VULCAN:
return "LOGIN_TYPE_VULCAN";
case LOGIN_TYPE_DEMO:
return "LOGIN_TYPE_DEMO";
default:
return "unknown";
}
}
public String mode() {
switch (mode) {
case LOGIN_MODE_LIBRUS_EMAIL:
return "LOGIN_MODE_LIBRUS_EMAIL";
case LOGIN_MODE_LIBRUS_SYNERGIA:
return "LOGIN_MODE_LIBRUS_SYNERGIA";
case LOGIN_MODE_LIBRUS_JST:
return "LOGIN_MODE_LIBRUS_JST";
default:
return "unknown";
}
}
@Override
public String toString() {
return "LoginStore{" +
"id=" + id +
", type=" + type() +
", mode=" + mode() +
", data=" + data +
'}';
}
}

View File

@ -0,0 +1,88 @@
package pl.szczodrzynski.edziennik.data.db.modules.login
import android.os.Bundle
import androidx.room.ColumnInfo
import androidx.room.Entity
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.*
@Entity(tableName = "loginStores", primaryKeys = ["loginStoreId"])
class LoginStore(
@ColumnInfo(name = "loginStoreId")
val id: Int,
@ColumnInfo(name = "loginStoreType")
val type: Int,
@ColumnInfo(name = "loginStoreMode")
val mode: Int,
@ColumnInfo(name = "loginStoreData")
val data: JsonObject = JsonObject()
) {
companion object {
const val LOGIN_TYPE_MOBIDZIENNIK = 1
const val LOGIN_TYPE_LIBRUS = 2
const val LOGIN_TYPE_VULCAN = 4
const val LOGIN_TYPE_IDZIENNIK = 3
const val LOGIN_TYPE_EDUDZIENNIK = 5
const val LOGIN_TYPE_DEMO = 20
const val LOGIN_MODE_LIBRUS_EMAIL = 0
const val LOGIN_MODE_LIBRUS_SYNERGIA = 1
const val LOGIN_MODE_LIBRUS_JST = 2
}
fun hasLoginData(key: String) = data.has(key)
fun getLoginData(key: String, defaultValue: Boolean) = data.getBoolean(key) ?: defaultValue
fun getLoginData(key: String, defaultValue: String?) = data.getString(key) ?: defaultValue
fun getLoginData(key: String, defaultValue: Int) = data.getInt(key) ?: defaultValue
fun getLoginData(key: String, defaultValue: Long) = data.getLong(key) ?: defaultValue
fun getLoginData(key: String, defaultValue: Float) = data.getFloat(key) ?: defaultValue
fun getLoginData(key: String, defaultValue: Char) = data.getChar(key) ?: defaultValue
fun putLoginData(key: String, value: Boolean) { data[key] = value }
fun putLoginData(key: String, value: String?) { data[key] = value }
fun putLoginData(key: String, value: Number) { data[key] = value }
fun putLoginData(key: String, value: Char) { data[key] = value }
fun removeLoginData(key: String) { data.remove(key) }
fun copyFrom(args: Bundle) {
for (key in args.keySet()) {
when (val o = args[key]) {
is String -> putLoginData(key, o)
is Int -> putLoginData(key, o)
is Long -> putLoginData(key, o)
is Float -> putLoginData(key, o)
is Boolean -> putLoginData(key, o)
}
}
}
fun type(): String {
return when (type) {
LOGIN_TYPE_MOBIDZIENNIK -> "LOGIN_TYPE_MOBIDZIENNIK"
LOGIN_TYPE_LIBRUS -> "LOGIN_TYPE_LIBRUS"
LOGIN_TYPE_IDZIENNIK -> "LOGIN_TYPE_IDZIENNIK"
LOGIN_TYPE_VULCAN -> "LOGIN_TYPE_VULCAN"
LOGIN_TYPE_DEMO -> "LOGIN_TYPE_DEMO"
else -> "unknown"
}
}
fun mode(): String {
return when (mode) {
LOGIN_MODE_LIBRUS_EMAIL -> "LOGIN_MODE_LIBRUS_EMAIL"
LOGIN_MODE_LIBRUS_SYNERGIA -> "LOGIN_MODE_LIBRUS_SYNERGIA"
LOGIN_MODE_LIBRUS_JST -> "LOGIN_MODE_LIBRUS_JST"
else -> "unknown"
}
}
override fun toString(): String {
return "LoginStore{" +
"id=" + id +
", type=" + type() +
", mode=" + mode() +
", data=" + data +
'}'
}
}

View File

@ -8,8 +8,6 @@ import androidx.room.Query;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
@Dao
public abstract class LoginStoreDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
@ -27,10 +25,6 @@ public abstract class LoginStoreDao {
@Query("SELECT * FROM loginStores WHERE loginStoreId = :loginStoreId")
public abstract LoginStore getByIdNow(int loginStoreId);
public void add(ProfileFull profileFull) {
add(new LoginStore(profileFull.getLoginStoreId(), profileFull.getLoginStoreType(), profileFull.getLoginStoreData()));
}
@Query("UPDATE loginStores SET loginStoreId = :targetId WHERE loginStoreId = :sourceId")
public abstract void changeId(int sourceId, int targetId);

View File

@ -1,10 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-3.
*/
package pl.szczodrzynski.edziennik.data.db.modules.profiles
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.widget.ImageView
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.room.ColumnInfo
@ -12,7 +15,9 @@ import androidx.room.Entity
import androidx.room.Ignore
import com.google.gson.JsonObject
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.colorFromName
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.ImageHolder
import pl.szczodrzynski.navlib.R
@ -20,143 +25,91 @@ import pl.szczodrzynski.navlib.drawer.IDrawerProfile
import pl.szczodrzynski.navlib.getDrawableFromRes
@Entity(tableName = "profiles", primaryKeys = ["profileId"])
open class Profile : IDrawerProfile {
open class Profile(
@ColumnInfo(name = "profileId")
override val id: Int,
val loginStoreId: Int,
val loginStoreType: Int,
override var name: String,
override var subname: String?,
/**
* The name of the student.
* This doesn't change, no matter if it's a parent or student account.
*/
var studentNameLong: String,
var studentNameShort: String,
/**
* A full name of the account owner.
* If null, then it's a student account.
* If not null, then it's a parent account with this name.
*/
var accountName: String?,
val studentData: JsonObject = JsonObject()
) : IDrawerProfile {
companion object {
const val REGISTRATION_UNSPECIFIED = 0
const val REGISTRATION_DISABLED = 1
const val REGISTRATION_ENABLED = 2
const val COLOR_MODE_DEFAULT = 0
const val COLOR_MODE_WEIGHTED = 1
const val AGENDA_DEFAULT = 0
const val AGENDA_CALENDAR = 1
const val YEAR_1_AVG_2_AVG = 0
const val YEAR_1_SEM_2_AVG = 1
const val YEAR_1_AVG_2_SEM = 2
const val YEAR_1_SEM_2_SEM = 3
const val YEAR_ALL_GRADES = 4
}
@ColumnInfo(name = "profileId")
override var id = -1
override var name: String? = ""
override var subname: String? = null
override var image: String? = null
/*public String name = "";
public String subname = null;
public String image = null;*/
var syncEnabled = true
var syncNotifications = true
var enableSharedEvents = true
var countInSeconds = false
var loggedIn = false
var empty = true
var archived = false
/**
* The name of the student.
* This doesn't change, no matter if it's a parent or student account.
*/
var studentNameLong: String? = null
var studentNameShort: String? = null
var syncEnabled = true
var enableSharedEvents = true
var registration = REGISTRATION_UNSPECIFIED
var userCode = ""
/**
* The student's number in the class register.
*/
var studentNumber = -1
var studentData: JsonObject? = null
/**
* A full name of the account owner.
* If null, then it's a student account.
* If not null, then it's a parent account with this name.
*/
var accountNameLong: String? = null
var studentClassName: String? = null
var studentSchoolYear: String? = null
var studentSchoolYearStart = Date.getToday().let { if (it.month < 9) it.year - 1 else it.year }
var registration = REGISTRATION_UNSPECIFIED
var gradeColorMode = COLOR_MODE_WEIGHTED
var agendaViewType = AGENDA_DEFAULT
var yearAverageMode = YEAR_ALL_GRADES
var currentSemester = 1
var attendancePercentage: Float = 0.0f
var dateSemester1Start: Date? = null
var dateSemester2Start: Date? = null
var dateYearEnd: Date? = null
var luckyNumberEnabled = true
var luckyNumber = -1
var luckyNumberDate: Date? = null
//public Map<Integer, Pair<String, Integer>> eventTypes;
var loginStoreId = id
var changedEndpoints: List<String>? = null
var dateSemester1Start = Date(studentSchoolYearStart, 9, 1)
var dateSemester2Start = Date(studentSchoolYearStart + 1, 2, 1)
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
@delegate:Ignore
val currentSemester by lazy { dateToSemester(Date.getToday()) }
var disabledNotifications: List<Long>? = null
var lastFullSync: Long = 0
var lastReceiversSync: Long = 0
fun shouldFullSync(context: Context): Boolean {
val connManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
// if time since last full sync is > 7 days and wifi is connected
// or
// if time since last full sync is > 14 days, regardless of wifi state
return System.currentTimeMillis() - lastFullSync > 7 * 24 * 60 * 60 * 1000 && mWifi.isConnected || System.currentTimeMillis() - lastFullSync > 14 * 24 * 60 * 60 * 1000
}
fun hasStudentData(key: String) = studentData.has(key)
fun getStudentData(key: String, defaultValue: Boolean) = studentData.getBoolean(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: String?) = studentData.getString(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Int) = studentData.getInt(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Long) = studentData.getLong(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Float) = studentData.getFloat(key) ?: defaultValue
fun getStudentData(key: String, defaultValue: Char) = studentData.getChar(key) ?: defaultValue
fun putStudentData(key: String, value: Boolean) { studentData[key] = value }
fun putStudentData(key: String, value: String?) { studentData[key] = value }
fun putStudentData(key: String, value: Number) { studentData[key] = value }
fun putStudentData(key: String, value: Char) { studentData[key] = value }
fun removeStudentData(key: String) { studentData.remove(key) }
@Ignore
constructor(id: Int, name: String, subname: String, loginStoreId: Int) : this() {
this.id = id
this.name = name
this.subname = subname
this.loginStoreId = loginStoreId
}
constructor() {
//eventTypes = new HashMap<>();
val today = Date.getToday()
val schoolYearStart = if (today.month < 9) today.year - 1 else today.year
dateSemester1Start = Date(schoolYearStart, 9, 1)
dateSemester2Start = Date(schoolYearStart + 1, 2, 1)
dateYearEnd = Date(schoolYearStart + 1, 6, 30)
}
fun getSemesterStart(semester: Int): Date {
if (dateSemester1Start == null || dateSemester2Start == null || dateYearEnd == null) {
val today = Date.getToday()
val schoolYearStart = if (today.month < 9) today.year - 1 else today.year
dateSemester1Start = Date(schoolYearStart, 9, 1)
dateSemester2Start = Date(schoolYearStart + 1, 2, 1)
dateYearEnd = Date(schoolYearStart + 1, 6, 30)
}
return if (semester == 1)
dateSemester1Start!!
else
dateSemester2Start!!
}
fun getSemesterEnd(semester: Int): Date {
if (dateSemester1Start == null || dateSemester2Start == null || dateYearEnd == null) {
val today = Date.getToday()
val schoolYearStart = if (today.month < 9) today.year - 1 else today.year
dateSemester1Start = Date(schoolYearStart, 9, 1)
dateSemester2Start = Date(schoolYearStart + 1, 2, 1)
dateYearEnd = Date(schoolYearStart + 1, 6, 30)
}
return if (semester == 1)
dateSemester2Start!!.clone().stepForward(0, 0, -1)
else
dateYearEnd!!
}
fun dateToSemester(date: Date?): Int {
if (date == null)
return 1
return if (date.value >= getSemesterStart(2).value) 2 else 1
}
@Ignore
constructor(context: Context) {
//RegisterEvent.checkPredefinedEventTypes(context, eventTypes);
}
val isParent
get() = accountName != null
override fun getImageDrawable(context: Context): Drawable {
@ -179,13 +132,6 @@ open class Profile : IDrawerProfile {
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
}
/*if (profileImage == null) {
profileImage = BitmapFactory.decodeResource(getResources(), pl.szczodrzynski.edziennik.R.drawable.profile);
}
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(getResources(), profileImage);
roundDrawable.setCircular(true);
return roundDrawable;*/
}
override fun getImageHolder(context: Context): ImageHolder {
return if (!image.isNullOrEmpty()) {
@ -203,130 +149,43 @@ open class Profile : IDrawerProfile {
getImageHolder(imageView.context).applyTo(imageView)
}
fun hasStudentData(key: String): Boolean {
if (studentData == null)
return false
return studentData?.has(key) ?: false
}
fun getStudentData(key: String, defaultValue: String?): String? {
if (studentData == null)
return defaultValue
val element = studentData!!.get(key)
return if (element != null) {
element.asString
} else defaultValue
}
fun getStudentData(key: String, defaultValue: Int): Int {
if (studentData == null)
return defaultValue
val element = studentData!!.get(key)
return element?.asInt ?: defaultValue
}
fun getStudentData(key: String, defaultValue: Long): Long {
if (studentData == null)
return defaultValue
val element = studentData!!.get(key)
return element?.asLong ?: defaultValue
}
fun getStudentData(key: String, defaultValue: Float): Float {
if (studentData == null)
return defaultValue
val element = studentData!!.get(key)
return element?.asFloat ?: defaultValue
}
fun getStudentData(key: String, defaultValue: Boolean): Boolean {
if (studentData == null)
return defaultValue
val element = studentData!!.get(key)
return element?.asBoolean ?: defaultValue
}
fun putStudentData(key: String, value: String?) {
if (studentData == null)
studentData = JsonObject()
studentData!!.addProperty(key, value)
}
fun putStudentData(key: String, value: Int) {
if (studentData == null)
studentData = JsonObject()
studentData!!.addProperty(key, value)
}
fun putStudentData(key: String, value: Long) {
if (studentData == null)
studentData = JsonObject()
studentData!!.addProperty(key, value)
}
fun putStudentData(key: String, value: Float) {
if (studentData == null)
studentData = JsonObject()
studentData!!.addProperty(key, value)
}
fun putStudentData(key: String, value: Boolean) {
if (studentData == null)
studentData = JsonObject()
studentData!!.addProperty(key, value)
}
fun removeStudentData(key: String) {
if (studentData == null)
studentData = JsonObject()
studentData!!.remove(key)
}
fun clearStudentStore() {
studentData = JsonObject()
}
override fun toString(): String {
return "Profile{" +
"id=" + id +
", name='" + name + '\''.toString() +
", subname='" + subname + '\''.toString() +
", image='" + image + '\''.toString() +
", syncEnabled=" + syncEnabled +
", syncNotifications=" + syncNotifications +
", enableSharedEvents=" + enableSharedEvents +
", empty=" + empty +
", studentNameLong='" + studentNameLong + '\''.toString() +
", studentNameShort='" + studentNameShort + '\''.toString() +
", studentNumber=" + studentNumber +
", studentData=" + studentData.toString() +
", registration=" + registration +
", gradeColorMode=" + gradeColorMode +
", agendaViewType=" + agendaViewType +
", currentSemester=" + currentSemester +
", attendancePercentage=" + attendancePercentage +
", dateSemester1Start=" + dateSemester1Start +
", dateSemester2Start=" + dateSemester2Start +
", dateYearEnd=" + dateYearEnd +
", luckyNumberEnabled=" + luckyNumberEnabled +
", luckyNumber=" + luckyNumber +
", luckyNumberDate=" + luckyNumberDate +
", loginStoreId=" + loginStoreId +
'}'.toString()
}
companion object {
const val REGISTRATION_UNSPECIFIED = 0
const val REGISTRATION_DISABLED = 1
const val REGISTRATION_ENABLED = 2
const val COLOR_MODE_DEFAULT = 0
const val COLOR_MODE_WEIGHTED = 1
const val AGENDA_DEFAULT = 0
const val AGENDA_CALENDAR = 1
const val YEAR_1_AVG_2_AVG = 0
const val YEAR_1_SEM_2_AVG = 1
const val YEAR_1_AVG_2_SEM = 2
const val YEAR_1_SEM_2_SEM = 3
const val YEAR_ALL_GRADES = 4
}
val supportedFragments: List<Int>
get() = when (loginStoreType) {
LoginStore.LOGIN_TYPE_MOBIDZIENNIK,
LoginStore.LOGIN_TYPE_DEMO,
LoginStore.LOGIN_TYPE_VULCAN -> listOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_BEHAVIOUR,
MainActivity.DRAWER_ITEM_ATTENDANCE
)
LoginStore.LOGIN_TYPE_LIBRUS,
LoginStore.LOGIN_TYPE_IDZIENNIK -> listOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_BEHAVIOUR,
MainActivity.DRAWER_ITEM_ATTENDANCE,
MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
)
LOGIN_TYPE_EDUDZIENNIK -> listOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_BEHAVIOUR,
MainActivity.DRAWER_ITEM_ATTENDANCE,
MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
)
else -> listOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES
)
}
}

View File

@ -1,74 +0,0 @@
package pl.szczodrzynski.edziennik.data.db.modules.profiles;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED;
@Dao
public interface ProfileDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void add(Profile profile);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void addAll(List<Profile> profileList);
@Query("DELETE FROM profiles WHERE profileId = :profileId")
void remove(int profileId);
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
LiveData<ProfileFull> getById(int profileId);
@Nullable
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
ProfileFull getFullByIdNow(int profileId);
@Query("SELECT* FROM profiles WHERE profileId = :profileId")
Profile getByIdNow(int profileId);
@Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
LiveData<List<Profile>> getAll();
@Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
List<Profile> getAllNow();
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId >= 0 ORDER BY profileId")
LiveData<List<ProfileFull>> getAllFull();
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId >= 0 ORDER BY profileId")
List<ProfileFull> getAllFullNow();
@Query("SELECT profileId FROM profiles WHERE loginStoreId = :loginStoreId ORDER BY profileId")
List<Integer> getIdsByLoginStoreIdNow(int loginStoreId);
@Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
List<Profile> getProfilesForSyncNow();
@Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
List<Integer> getIdsForSyncNow();
@Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId")
List<Integer> getIdsNow();
@Query("SELECT profiles.*, loginStores.* FROM teams JOIN profiles USING(profileId) LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE teamCode = :teamCode AND registration = "+ REGISTRATION_ENABLED +" AND enableSharedEvents = 1")
List<ProfileFull> getByTeamCodeNowWithRegistration(String teamCode);
@Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId ASC LIMIT 1")
int getFirstId();
@Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId DESC LIMIT 1")
int getLastId();
@Query("UPDATE profiles SET loginStoreId = :targetId WHERE loginStoreId = :sourceId")
void changeStoreId(int sourceId, int targetId);
@Query("UPDATE profiles SET currentSemester = :semester WHERE profileId = :profileId")
void changeSemester(int profileId, int semester);
}

View File

@ -0,0 +1,55 @@
package pl.szczodrzynski.edziennik.data.db.modules.profiles
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface ProfileDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun add(profile: Profile)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun addAll(profileList: List<Profile>)
@Query("DELETE FROM profiles WHERE profileId = :profileId")
fun remove(profileId: Int)
@Query("SELECT profiles.* FROM profiles WHERE profileId = :profileId")
fun getById(profileId: Int): LiveData<Profile?>
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
fun getByIdNow(profileId: Int): Profile?
@get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val all: LiveData<List<Profile>>
@get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val allNow: List<Profile>
@get:Query("SELECT COUNT(profileId) FROM profiles WHERE profileId >= 0")
val count: Int
@Query("SELECT profileId FROM profiles WHERE loginStoreId = :loginStoreId ORDER BY profileId")
fun getIdsByLoginStoreIdNow(loginStoreId: Int): List<Int>
@get:Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
val profilesForSyncNow: List<Profile>
@get:Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId")
val idsForSyncNow: List<Int>
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val idsNow: List<Int>
@Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED + " AND enableSharedEvents = 1")
fun getByTeamCodeNowWithRegistration(teamCode: String?): List<Profile>
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId ASC LIMIT 1")
val firstId: Int?
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId DESC LIMIT 1")
val lastId: Int?
}

View File

@ -1,246 +0,0 @@
package pl.szczodrzynski.edziennik.data.db.modules.profiles
import android.content.Context
import androidx.room.ColumnInfo
import androidx.room.Ignore
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.*
import java.util.*
class ProfileFull : Profile {
@ColumnInfo(name = "loginStoreType")
var loginStoreType: Int = 0
@ColumnInfo(name = "loginStoreData")
var loginStoreData: JsonObject? = null
val usernameId: String
get() {
if (loginStoreData == null) {
return "NO_LOGIN_STORE"
}
if (studentData == null) {
return "NO_STUDENT_STORE"
}
return when (loginStoreType) {
LOGIN_TYPE_MOBIDZIENNIK -> getLoginData("serverName", "MOBI_UN") + ":" + getLoginData("username", "MOBI_UN") + ":" + getStudentData("studentId", -1)
LOGIN_TYPE_LIBRUS -> getStudentData("schoolName", "LIBRUS_UN") + ":" + getStudentData("accountLogin", "LIBRUS_LOGIN_UN")
LOGIN_TYPE_IUCZNIOWIE -> getLoginData("schoolName", "IUCZNIOWIE_UN") + ":" + getLoginData("username", "IUCZNIOWIE_UN") + ":" + getStudentData("registerId", -1)
LOGIN_TYPE_VULCAN -> getStudentData("schoolName", "VULCAN_UN") + ":" + getStudentData("studentId", -1)
LOGIN_TYPE_EDUDZIENNIK -> getStudentData("schoolName", "EDU_UN") + ":" + getLoginData("email", "EDU_UN") + ":" + getStudentData("studentId", null)?.crc32()
LOGIN_TYPE_DEMO -> getLoginData("serverName", "DEMO_UN") + ":" + getLoginData("username", "DEMO_UN") + ":" + getStudentData("studentId", -1)
else -> "TYPE_UNKNOWN"
}
}
// example (minimal) list of fragments
// there will never be less available options
val supportedFragments: List<Int>
get() {
val fragmentIds: MutableList<Int>
when (loginStoreType) {
LOGIN_TYPE_MOBIDZIENNIK, LOGIN_TYPE_DEMO, LOGIN_TYPE_VULCAN -> {
fragmentIds = ArrayList()
fragmentIds.add(DRAWER_ITEM_TIMETABLE)
fragmentIds.add(DRAWER_ITEM_AGENDA)
fragmentIds.add(DRAWER_ITEM_GRADES)
fragmentIds.add(DRAWER_ITEM_MESSAGES)
fragmentIds.add(DRAWER_ITEM_HOMEWORK)
fragmentIds.add(DRAWER_ITEM_BEHAVIOUR)
fragmentIds.add(DRAWER_ITEM_ATTENDANCE)
return fragmentIds
}
LOGIN_TYPE_LIBRUS, LOGIN_TYPE_IUCZNIOWIE -> {
fragmentIds = ArrayList()
fragmentIds.add(DRAWER_ITEM_TIMETABLE)
fragmentIds.add(DRAWER_ITEM_AGENDA)
fragmentIds.add(DRAWER_ITEM_GRADES)
fragmentIds.add(DRAWER_ITEM_MESSAGES)
fragmentIds.add(DRAWER_ITEM_HOMEWORK)
fragmentIds.add(DRAWER_ITEM_BEHAVIOUR)
fragmentIds.add(DRAWER_ITEM_ATTENDANCE)
fragmentIds.add(DRAWER_ITEM_ANNOUNCEMENTS)
return fragmentIds
}
LOGIN_TYPE_EDUDZIENNIK -> {
fragmentIds = ArrayList()
fragmentIds.add(DRAWER_ITEM_TIMETABLE)
fragmentIds.add(DRAWER_ITEM_AGENDA)
fragmentIds.add(DRAWER_ITEM_GRADES)
fragmentIds.add(DRAWER_ITEM_HOMEWORK)
fragmentIds.add(DRAWER_ITEM_BEHAVIOUR)
fragmentIds.add(DRAWER_ITEM_ATTENDANCE)
fragmentIds.add(DRAWER_ITEM_ANNOUNCEMENTS)
return fragmentIds
}
}
fragmentIds = ArrayList()
fragmentIds.add(DRAWER_ITEM_TIMETABLE)
fragmentIds.add(DRAWER_ITEM_AGENDA)
fragmentIds.add(DRAWER_ITEM_GRADES)
return fragmentIds
}
constructor() : super()
constructor(profile: Profile, loginStore: LoginStore) {
/*Profile::class.memberProperties.forEach { profileProperty ->
if (profileProperty.visibility == KVisibility.PUBLIC) {
ProfileFull::class.memberProperties.singleOrNull { it.name == profileProperty.name }?.let { fullProperty ->
if (fullProperty is KMutableProperty<*>) {
fullProperty.setter.call(this, profileProperty.get(profile))
}
}
}
}*/
Profile::class.java.declaredFields.forEach { profileProperty ->
profileProperty.isAccessible = true
profileProperty.set(this, profileProperty.get(profile))
}
this.loginStoreType = loginStore.type
this.loginStoreId = loginStore.id
this.loginStoreData = loginStore.data
/*for (Field field: LoginStore.class.getFields()) {
try {
ProfileFull.class.getField(field.getName()).set(this, field.get(loginStore));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}*/
}
constructor(context: Context) : super(context)
@Ignore
constructor(id: Int, name: String, subname: String, loginStoreId: Int) : super(id, name, subname, loginStoreId)
fun canChangeLoginPassword(): Boolean {
return loginStoreType == LOGIN_TYPE_MOBIDZIENNIK || loginStoreType == LOGIN_TYPE_LIBRUS || loginStoreType == LOGIN_TYPE_IUCZNIOWIE
}
fun changeLoginPassword(password: String) {
if (loginStoreData == null) {
return
}
if (studentData == null) {
return
}
when (loginStoreType) {
LOGIN_TYPE_MOBIDZIENNIK, LOGIN_TYPE_LIBRUS, LOGIN_TYPE_IUCZNIOWIE -> putLoginData("password", password)
}
}
fun getLoginData(key: String, defaultValue: String): String {
if (loginStoreData == null)
return defaultValue
val element = loginStoreData!!.get(key)
return if (element != null) {
element.asString
} else defaultValue
}
fun getLoginData(key: String, defaultValue: Int): Int {
if (loginStoreData == null)
return defaultValue
val element = loginStoreData!!.get(key)
return element?.asInt ?: defaultValue
}
fun getLoginData(key: String, defaultValue: Long): Long {
if (loginStoreData == null)
return defaultValue
val element = loginStoreData!!.get(key)
return element?.asLong ?: defaultValue
}
fun getLoginData(key: String, defaultValue: Float): Float {
if (loginStoreData == null)
return defaultValue
val element = loginStoreData!!.get(key)
return element?.asFloat ?: defaultValue
}
fun getLoginData(key: String, defaultValue: Boolean): Boolean {
if (loginStoreData == null)
return defaultValue
val element = loginStoreData!!.get(key)
return element?.asBoolean ?: defaultValue
}
fun putLoginData(key: String, value: String) {
forceLoginStore()
loginStoreData!!.addProperty(key, value)
}
fun putLoginData(key: String, value: Int) {
forceLoginStore()
loginStoreData!!.addProperty(key, value)
}
fun putLoginData(key: String, value: Long) {
forceLoginStore()
loginStoreData!!.addProperty(key, value)
}
fun putLoginData(key: String, value: Float) {
forceLoginStore()
loginStoreData!!.addProperty(key, value)
}
fun putLoginData(key: String, value: Boolean) {
forceLoginStore()
loginStoreData!!.addProperty(key, value)
}
fun removeLoginData(key: String) {
if (loginStoreData == null)
return
loginStoreData!!.remove(key)
}
fun clearLoginStore() {
loginStoreData = JsonObject()
}
private fun forceLoginStore() {
if (loginStoreData == null) {
clearLoginStore()
}
}
fun loginStoreType(): String {
return when (loginStoreType) {
LOGIN_TYPE_MOBIDZIENNIK -> "LOGIN_TYPE_MOBIDZIENNIK"
LOGIN_TYPE_LIBRUS -> "LOGIN_TYPE_LIBRUS"
LOGIN_TYPE_IUCZNIOWIE -> "LOGIN_TYPE_IDZIENNIK"
LOGIN_TYPE_VULCAN -> "LOGIN_TYPE_VULCAN"
LOGIN_TYPE_DEMO -> "LOGIN_TYPE_DEMO"
else -> "LOGIN_TYPE_UNKNOWN"
}
}
override fun toString(): String {
return "ProfileFull{" +
"parent=" + super.toString() +
"loginStoreId=" + loginStoreId +
", loginStoreType=" + loginStoreType() +
", loginStoreData=" + loginStoreData +
'}'.toString()
}
}

View File

@ -17,7 +17,6 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED;
@ -33,8 +32,8 @@ public class ServerRequest {
this(app, url, source, app.profile);
}
public ServerRequest(App app, String url, String source, ProfileFull profileFull) {
this(app, url, source, profileFull, profileFull == null ? -1 : profileFull.getLoginStoreType(), profileFull == null ? "" : profileFull.getUsernameId());
public ServerRequest(App app, String url, String source, Profile profileFull) {
this(app, url, source, profileFull, profileFull == null ? -1 : profileFull.getLoginStoreType(), profileFull == null ? "" : profileFull.getUserCode());
}
public ServerRequest(App app, String url, String source, Profile profile, int loginStoreType, String usernameId) {

View File

@ -22,7 +22,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType;
import pl.szczodrzynski.edziennik.data.db.modules.feedback.FeedbackMessage;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment;
@ -88,11 +88,11 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
if (studentIdStr != null) {
int studentId = strToInt(studentIdStr);
AsyncTask.execute(() -> {
List<ProfileFull> profileList = app.db.profileDao().getAllFullNow();
List<Profile> profileList = app.db.profileDao().getAllNow();
ProfileFull profile = null;
Profile profile = null;
for (ProfileFull profileFull: profileList) {
for (Profile profileFull: profileList) {
if (profileFull.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK
&& studentId == profileFull.getStudentData("studentId", -1)) {
profile = profileFull;
@ -185,7 +185,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
.withType(TYPE_SERVER_MESSAGE)
.withFragmentRedirect(MainActivity.DRAWER_ITEM_NOTIFICATIONS)
);
app.notifier.postAll(null);
app.notifier.postAll();
app.saveConfig("notifications");
break;
case "feedback_message_from_dev":
@ -212,7 +212,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
.withType(TYPE_FEEDBACK_MESSAGE)
.withFragmentRedirect(MainActivity.TARGET_FEEDBACK)
);
app.notifier.postAll(null);
app.notifier.postAll();
app.saveConfig("notifications");
}
});
@ -234,7 +234,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
.withType(TYPE_FEEDBACK_MESSAGE)
.withFragmentRedirect(MainActivity.TARGET_FEEDBACK)
);
app.notifier.postAll(null);
app.notifier.postAll();
app.saveConfig("notifications");
break;
case "ping":
@ -254,8 +254,8 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
while (teamCode != null || teamUnshareCode != null) {
d(TAG, "Got an event for teamCode " + teamCode + " and teamUnshareCode " + teamUnshareCode);
// get the target Profile by the corresponding teamCode
List<ProfileFull> profiles = app.db.profileDao().getByTeamCodeNowWithRegistration(teamCode == null ? teamUnshareCode : teamCode);
for (ProfileFull profile : profiles) {
List<Profile> profiles = app.db.profileDao().getByTeamCodeNowWithRegistration(teamCode == null ? teamUnshareCode : teamCode);
for (Profile profile : profiles) {
d(TAG, "Matched profile " + profile.getName());
if (teamCode != null) {
// SHARING
@ -276,7 +276,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
// TODO? i guess
Event oldEvent = app.db.eventDao().getByIdNow(profile.getId(), event.id);
if (event.sharedBy != null && event.sharedBy.equals(profile.getUsernameId())) {
if (event.sharedBy != null && event.sharedBy.equals(profile.getUserCode())) {
d(TAG, "Shared by self! Changing name");
event.sharedBy = "self";
event.sharedByName = profile.getStudentNameLong();
@ -318,7 +318,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
teamUnshareCode = null;
}
}
app.notifier.postAll(null);
app.notifier.postAll();
app.saveConfig();
});
break;

View File

@ -21,7 +21,6 @@ import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
@ -609,7 +608,7 @@ class EventManualDialog(
)
launch {
val profile = app.db.profileDao().getFullByIdNow(profileId)
val profile = app.db.profileDao().getByIdNow(profileId)
if (!share && !editingShared) {
Toast.makeText(activity, "Save without sharing", Toast.LENGTH_SHORT).show()
@ -643,7 +642,7 @@ class EventManualDialog(
Toast.makeText(activity, "Share/update own event", Toast.LENGTH_SHORT).show()
eventObject.apply {
sharedBy = profile?.usernameId
sharedBy = profile?.userCode
sharedByName = profile?.studentNameLong
}

View File

@ -74,7 +74,7 @@ public class GradeDetailsDialog {
b.setGrade(grade);
int gradeColor;
if (app.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
if (App.getConfig().getFor(profileId).getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {

View File

@ -312,10 +312,7 @@ public class AttendanceFragment extends Fragment {
float attendancePercentage;
// in Mobidziennik there are no TYPE_PRESENT records so we cannot calculate the percentage
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && false) {
attendancePercentage = app.profile.getAttendancePercentage();
}
else if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN) {
if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN) {
float allCount = presentCount + absentCount + belatedCount; // do not count releases
float present = allCount - absentCount;
attendancePercentage = present / allCount * 100.0f;

View File

@ -138,7 +138,7 @@ class CrashActivity : AppCompatActivity(), CoroutineScope {
content = content.replace("\n".toRegex(), "<br>")
contentPlain += "\n" + Build.MANUFACTURER + "\n" + Build.BRAND + "\n" + Build.MODEL + "\n" + Build.DEVICE + "\n"
if (app.profile != null && app.profile.registration == Profile.REGISTRATION_ENABLED) {
contentPlain += "U: " + app.profile.usernameId + "\nS: " + app.profile.studentNameLong + "\n"
contentPlain += "U: " + app.profile.userCode + "\nS: " + app.profile.studentNameLong + "\n"
}
contentPlain += BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE
return if (plain) contentPlain else content

View File

@ -18,7 +18,7 @@ import com.mikepenz.iconics.utils.colorRes
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeFull
import pl.szczodrzynski.edziennik.utils.Utils.bs

View File

@ -71,7 +71,7 @@ public class GradesFragment extends Fragment {
private boolean sortModeChanged = false;
private String getRegisterCardAverageModeSubText() {
switch (app.profile.getYearAverageMode()) {
switch (App.getConfig().forProfile().getGrades().getYearAverageMode()) {
default:
case YEAR_1_AVG_2_AVG:
return getString(R.string.settings_register_avg_mode_0_short);
@ -184,9 +184,8 @@ public class GradesFragment extends Fragment {
.title(getString(R.string.settings_register_avg_mode_dialog_title))
.content(getString(R.string.settings_register_avg_mode_dialog_text))
.items(modeNames)
.itemsCallbackSingleChoice(modeIds.indexOf(app.profile.getYearAverageMode()), (dialog, itemView, which, text) -> {
app.profile.setYearAverageMode(modeIds.get(which));
app.profileSaveAsync();
.itemsCallbackSingleChoice(modeIds.indexOf(App.getConfig().forProfile().getGrades().getYearAverageMode()), (dialog, itemView, which, text) -> {
App.getConfig().forProfile().getGrades().setYearAverageMode(modeIds.get(which));
activity.reloadTarget();
return true;
})
@ -253,11 +252,16 @@ public class GradesFragment extends Fragment {
for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) {
model = new ItemGradesSubjectModel(app.profile, new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new ArrayList<>(), new ArrayList<>());//ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
model = new ItemGradesSubjectModel(app.profile,
new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName),
new ArrayList<>(),
new ArrayList<>());
subjectList.add(model);
if (model.subject != null && model.subject.id == finalExpandSubjectId) {
model.expandView = true;
}
model.colorMode = App.getConfig().forProfile().getGrades().getColorMode();
model.yearAverageMode = App.getConfig().forProfile().getGrades().getYearAverageMode();
}
if (!grade.seen && grade.semester == 1) {
model.semester1Unread++;
@ -376,7 +380,7 @@ public class GradesFragment extends Fragment {
else if (!model.isBehaviourSubject && model.isNormalSubject) {
// applies for normal grades & normal+descriptive grades
// calculate the normal grade average based on the user's setting
switch (app.profile.getYearAverageMode()) {
switch (App.getConfig().forProfile().getGrades().getYearAverageMode()) {
case YEAR_1_AVG_2_AVG:
model.yearAverage = (model.semester1Average + model.semester2Average) / 2;
break;

View File

@ -56,7 +56,7 @@ public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.Vi
}));
int gradeColor;
if (app.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
if (App.getConfig().forProfile().getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {

View File

@ -491,7 +491,7 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
continue;
int gradeColor;
if (model.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
if (model.colorMode == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
} else {
gradeColor = Colors.gradeToColor(grade);
@ -581,13 +581,13 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
}
arguments.putInt("semester", 1);
//d(TAG, "Model is " + model);
switch (model.profile.getYearAverageMode()) {
switch (model.yearAverageMode) {
case YEAR_1_SEM_2_AVG:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester2Final == null && model.profile.getYearAverageMode() == YEAR_1_AVG_2_SEM ? -1 : model.profile.getYearAverageMode());
arguments.putInt("averageMode", model.semester2Final == null && model.yearAverageMode == YEAR_1_AVG_2_SEM ? -1 : model.yearAverageMode);
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester2);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester2);
@ -607,13 +607,13 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
}
arguments.putInt("semester", 2);
//d(TAG, "Model is " + model);
switch (model.profile.getYearAverageMode()) {
switch (model.yearAverageMode) {
case YEAR_1_AVG_2_SEM:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester1Final == null && model.profile.getYearAverageMode() == YEAR_1_SEM_2_AVG ? -1 : model.profile.getYearAverageMode());
arguments.putInt("averageMode", model.semester1Final == null && model.yearAverageMode == YEAR_1_SEM_2_AVG ? -1 : model.yearAverageMode);
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester1);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester1);

View File

@ -51,7 +51,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject;
import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding;
import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentHomeOldBinding;
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
@ -240,24 +239,6 @@ public class HomeFragmentOld extends Fragment {
}
}
if (app.profile.getLuckyNumberEnabled()
&& app.profile.getLuckyNumber() != -1
&& app.profile.getLuckyNumberDate() != null && app.profile.getLuckyNumberDate().getValue() == Date.getToday().getValue()) {
CardLuckyNumberBinding b;
b = DataBindingUtil.inflate(layoutInflater, R.layout.card_lucky_number, insertPoint, false);
insertPoint.addView(b.getRoot());
b.cardLuckyNumberTitle.setText(getString(R.string.card_lucky_number_title_format, app.profile.getLuckyNumber()));
if (app.profile.getStudentNumber() == -1) {
b.cardLuckyNumberText.setText(R.string.card_lucky_number_not_set);
}
else {
b.cardLuckyNumberText.setText(getString(R.string.card_lucky_number_text_format, app.profile.getStudentNumber()));
}
b.cardLuckyNumber.setOnClickListener(v1 -> setNumberDialog());
}
timetableCard = new HomeTimetableCardOld(app, activity, this, layoutInflater, insertPoint);
timetableCard.run();
@ -498,7 +479,7 @@ public class HomeFragmentOld extends Fragment {
if (ellipsized)
continue;
int gradeColor;
if (app.profile.getGradeColorMode() == Profile.COLOR_MODE_DEFAULT) {
if (App.getConfig().forProfile().getGrades().getColorMode() == Profile.COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {

View File

@ -121,7 +121,7 @@ class HomeGradesCard(
16 /*ellipsize width*/)) / 1.5f
subject.grades1.onEach { grade ->
val gradeColor = when (app.profile.gradeColorMode) {
val gradeColor = when (App.getConfig().forProfile().grades.colorMode) {
Profile.COLOR_MODE_DEFAULT -> grade.color
else -> Colors.gradeToColor(grade)
}

View File

@ -1,161 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.navigation.NavDestination;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigation;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.ActivityLoginBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding b;
private App app;
private static final String TAG = "LoginActivity";
public static final int RESULT_OK = 1;
public static NavOptions navOptions;
static ApiError error = null;
ErrorSnackbar errorSnackbar = new ErrorSnackbar(this);
static List<LoginProfileObject> profileObjects;
public static boolean firstCompleted = false; // if a profile is already added during *this* login. This means that LoginChooser has to navigateUp onBackPressed. Else, finish the activity.
public static boolean privacyPolicyAccepted = false;
@Override
public void onBackPressed() {
NavDestination destination = Navigation.findNavController(this, R.id.nav_host_fragment).getCurrentDestination();
if (destination != null && destination.getId() == R.id.loginSyncErrorFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginProgressFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginSyncFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginChooserFragment && !firstCompleted) {
setResult(RESULT_CANCELED);
finish();
return;
}
if (destination != null && destination.getId() == R.id.loginSummaryFragment) {
new MaterialDialog.Builder(this)
.title(R.string.are_you_sure)
.content(R.string.login_cancel_confirmation)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive((dialog, which) -> {
setResult(RESULT_CANCELED);
finish();
})
.show();
return;
}
Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme_Light);
firstCompleted = false;
profileObjects = new ArrayList<>();
error = null;
navOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(R.anim.slide_in_left)
.setPopExitAnim(R.anim.slide_out_right)
.build();
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_login, null, false);
setContentView(b.getRoot());
errorSnackbar.setCoordinator(b.coordinator, null);
app = (App) getApplication();
if (!app.config.getLoginFinished()) {
app.config.getUi().setMiniMenuVisible(getResources().getConfiguration().smallestScreenWidthDp > 480);
}
/*b.getRoot().addOnLayoutChangeListener(((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
Animator circularReveal = null;
float finalRadius = (float) (Math.max(b.revealView.getWidth(), b.revealView.getHeight()) * 1.1);
circularReveal = ViewAnimationUtils.createCircularReveal(b.revealView, b.revealView.getWidth()/2, b.revealView.getHeight()/2, 0, finalRadius);
circularReveal.setDuration(400);
circularReveal.setInterpolator(new AccelerateInterpolator());
// make the view visible and start the animation
b.revealView.setVisibility(View.VISIBLE);
circularReveal.addListener(new Animator.AnimatorListener() {
@Override public void onAnimationEnd(Animator animation) {
Anim.fadeIn(b.title1, 500, new Animation.AnimationListener() {
@Override public void onAnimationEnd(Animation animation) {
b.title2.postDelayed(() -> {
Anim.fadeIn(b.title2, 500, new Animation.AnimationListener() {
@Override public void onAnimationEnd(Animation animation) {
b.revealView.postDelayed(() -> {
Anim.fadeOut(b.title1, 500, null);
Animation anim2 = null;
anim2 = new Anim.ResizeAnimation(b.revealView, 1.0f, 1.0f, 1.0f, 0.15f);
anim2.setDuration(800);
anim2.setInterpolator(new AccelerateDecelerateInterpolator());
b.revealView.startAnimation(anim2);
}, 700);
}
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
});
}, 1500);
}
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
});
}
@Override public void onAnimationCancel(Animator animation) { }
@Override public void onAnimationStart(Animator animation) { }
@Override public void onAnimationRepeat(Animator animation) { }
});
circularReveal.start();
}));*/
/**/
/*TextInputEditText e = findViewById(R.id.buttontest);
e.setOnClickListener((v -> {
Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
PopupMenu popup = new PopupMenu(this, e);
//popup.getMenu().add(0, 15, 0, HomeFragment.plural(c, R.plurals.time_till_seconds, 15));
//popup.getMenu().add(0, 15 * 60, 0, HomeFragment.plural(c, R.plurals.time_till_minutes, 15));
popup.getMenu().add(0, 30 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_minutes, 30));
popup.getMenu().add(0, 60 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 1));
popup.getMenu().add(0, 120 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 2));
popup.getMenu().add(0, 180 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 3));
popup.getMenu().add(0, 240 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 4));
popup.setOnMenuItemClickListener(item -> {
e.setText(item.getTitle());
return false;
});
popup.show();
}));*/
}
}

View File

@ -0,0 +1,96 @@
package pl.szczodrzynski.edziennik.ui.modules.login
import android.app.Activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavOptions
import androidx.navigation.Navigation
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.databinding.ActivityLoginBinding
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
}
private val app: App by lazy { applicationContext as App }
private lateinit var b: ActivityLoginBinding
val nav by lazy { Navigation.findNavController(this, R.id.nav_host_fragment) }
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
var lastError: ApiError? = null
val profiles = mutableListOf<LoginSummaryProfileAdapter.Item>()
val loginStores = mutableListOf<LoginStore>()
override fun onBackPressed() {
val destination = nav.currentDestination
if (destination != null && destination.id == R.id.loginSyncErrorFragment) {
return
}
if (destination != null && destination.id == R.id.loginProgressFragment) {
return
}
if (destination != null && destination.id == R.id.loginSyncFragment) {
return
}
if (destination != null && destination.id == R.id.loginChooserFragment && !app.config.loginFinished) {
setResult(Activity.RESULT_CANCELED)
finish()
return
}
if (destination != null && destination.id == R.id.loginSummaryFragment) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.are_you_sure)
.setMessage(R.string.login_cancel_confirmation)
.setPositiveButton(R.string.yes) { _, _ ->
setResult(Activity.RESULT_CANCELED)
finish()
}
.setNegativeButton(R.string.no, null)
.show()
return
}
nav.navigateUp()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(R.style.AppTheme_Light)
navOptions = NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(R.anim.slide_in_left)
.setPopExitAnim(R.anim.slide_out_right)
.build()
b = ActivityLoginBinding.inflate(layoutInflater)
setContentView(b.root)
errorSnackbar.setCoordinator(b.coordinator, null)
launch {
app.config.loginFinished = app.db.profileDao().count > 0
if (!app.config.loginFinished) {
app.config.ui.miniMenuVisible = resources.configuration.smallestScreenWidthDp > 480
}
}
}
fun error(error: ApiError) { errorSnackbar.addError(error).show(); lastError = error }
}

View File

@ -1,85 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding;
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity;
import static android.app.Activity.RESULT_CANCELED;
public class LoginChooserFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginChooserBinding b;
private static final String TAG = "LoginTemplate";
public static boolean fakeLogin = false;
public LoginChooserFragment() { }
@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_chooser, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.loginMobidziennikLogo.setOnClickListener((v) -> nav.navigate(R.id.loginMobidziennikFragment, null, LoginActivity.navOptions));
b.loginLibrusLogo.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusFragment, null, LoginActivity.navOptions));
b.loginLibrusJstLogo.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusJstFragment, null, LoginActivity.navOptions));
b.loginVulcanLogo.setOnClickListener((v) -> nav.navigate(R.id.loginVulcanFragment, null, LoginActivity.navOptions));
b.loginIuczniowieLogo.setOnClickListener((v) -> nav.navigate(R.id.loginIuczniowieFragment, null, LoginActivity.navOptions));
b.loginEdudziennikLogo.setOnClickListener((v) -> nav.navigate(R.id.loginEdudziennikFragment, null, LoginActivity.navOptions));
if (LoginActivity.firstCompleted) {
// we are navigated here from LoginSummary
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> nav.navigateUp()));
}
else if (app.config.getLoginFinished()) {
// we are navigated here from AppDrawer
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> {
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
}));
}
else {
// there is no profiles
b.cancelButton.setVisibility(View.GONE);
}
b.fakeLogin.setVisibility(App.devMode ? View.VISIBLE : View.GONE);
b.fakeLogin.setChecked(fakeLogin);
b.fakeLogin.setOnCheckedChangeListener((v, isChecked) -> fakeLogin = isChecked);
b.helpButton.setOnClickListener((v -> {
startActivity(new Intent(getActivity(), FeedbackActivity.class));
}));
}
}

View File

@ -0,0 +1,72 @@
package pl.szczodrzynski.edziennik.ui.modules.login
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import androidx.fragment.app.Fragment
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity
class LoginChooserFragment : Fragment() {
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 val nav by lazy { activity.nav }
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)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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.onClick { nav.navigateUp() }
}
app.config.loginFinished -> {
// we are navigated here from AppDrawer
b.cancelButton.visibility = View.VISIBLE
b.cancelButton.onClick {
activity.setResult(Activity.RESULT_CANCELED)
activity.finish()
}
}
else -> {
// there is no profiles
b.cancelButton.visibility = View.GONE
}
}
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)) }
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-23
* Copyright (c) Kuba Szczodrzyński 2020-1-3.
*/
package pl.szczodrzynski.edziennik.ui.modules.login
@ -9,89 +9,85 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.Navigation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.databinding.FragmentLoginEdudziennikBinding
import pl.szczodrzynski.edziennik.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import java.util.*
import kotlin.coroutines.CoroutineContext
class LoginEdudziennikFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginEdudziennikFragment"
}
private val app by lazy { activity?.application as App? }
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginEdudziennikBinding
private val nav by lazy { activity.nav }
private var job = Job()
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var b: FragmentLoginEdudziennikBinding
private lateinit var nav: NavController
private lateinit var errorSnackbar: ErrorSnackbar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity?.also { activity ->
nav = Navigation.findNavController(activity, R.id.nav_host_fragment)
errorSnackbar = (activity as LoginActivity).errorSnackbar
}
b = FragmentLoginEdudziennikBinding.inflate(inflater, container, false)
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?) { launch {
startCoroutineTimer(delayMillis = 100) {
val error = LoginActivity.error
if (error != null) {
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)
}
errorSnackbar.addError(error)
LoginActivity.error = null
}
}
b.backButton.setOnClickListener { nav.navigateUp() }
b.loginButton.setOnClickListener { login() }
}}
b.backButton.onClick { nav.navigateUp() }
private fun login() {
var errors = false
b.loginButton.onClick {
var errors = false
b.loginEmailLayout.error = null
b.loginPasswordLayout.error = null
b.loginEmailLayout.error = null
b.loginPasswordLayout.error = null
val emailEditable = b.loginEmail.text
val passwordEditable = b.loginPassword.text
val email = b.loginEmail.text?.toString()?.toLowerCase(Locale.ROOT) ?: ""
val password = b.loginPassword.text?.toString() ?: ""
if (emailEditable.isNullOrBlank()) {
b.loginEmailLayout.error = getString(R.string.login_error_no_email)
errors = true
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)
}
if (passwordEditable.isNullOrBlank()) {
b.loginPasswordLayout.error = getString(R.string.login_error_no_password)
errors = true
}
if (errors)
return
nav.navigate(R.id.loginProgressFragment, Bundle().apply {
putInt("loginType", LOGIN_TYPE_EDUDZIENNIK)
putString("email", emailEditable.toString())
putString("password", passwordEditable.toString())
}, LoginActivity.navOptions)
}
}

View File

@ -1,67 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginFinishBinding;
public class LoginFinishFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginFinishBinding b;
private static final String TAG = "LoginFinishFragment";
static boolean firstRun = true;
static int firstProfileId = -1;
public LoginFinishFragment() { }
@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_finish, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
if (!firstRun) {
b.loginFinishSubtitle.setText(R.string.login_finish_subtitle_not_first_run);
}
b.finishButton.setOnClickListener((v -> {
Intent intent = null;
if (firstProfileId != -1) {
intent = new Intent();
intent.putExtra("profileId", firstProfileId);
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOME);
}
getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
}));
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-4.
*/
package pl.szczodrzynski.edziennik.ui.modules.login
import android.app.Activity
import android.content.Intent
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.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentLoginFinishBinding
import pl.szczodrzynski.edziennik.onClick
import kotlin.coroutines.CoroutineContext
class LoginFinishFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginFinishFragment"
}
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginFinishBinding
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 = FragmentLoginFinishBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val firstRun = arguments?.getBoolean("firstRun", true) ?: true
if (!firstRun) {
b.loginFinishSubtitle.setText(R.string.login_finish_subtitle_not_first_run)
}
b.finishButton.onClick {
val firstProfileId = arguments?.getInt("firstProfileId", -1) ?: -1
if (firstProfileId != -1) {
val intent = Intent()
intent.putExtra("profileId", firstProfileId)
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOME)
activity.setResult(Activity.RESULT_OK, intent)
}
activity.finish()
}
}
}

View File

@ -1,128 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
public class LoginIuczniowieFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginIuczniowieBinding b;
private static final String TAG = "LoginIuczniowie";
private ErrorSnackbar errorSnackbar;
public LoginIuczniowieFragment() { }
@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);
errorSnackbar = ((LoginActivity) getActivity()).errorSnackbar;
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_iuczniowie, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
ApiError error = LoginActivity.error;
if (error != null) {
switch (error.getErrorCode()) {
case ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME:
b.loginSchoolNameLayout.setError(getString(R.string.login_error_incorrect_school_name));
break;
case ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
}
errorSnackbar.addError(error).show();
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginIuczniowieHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginSchoolNameLayout.setError(null);
b.loginUsernameLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable schoolNameEditable = b.loginSchoolName.getText();
Editable usernameEditable = b.loginUsername.getText();
Editable passwordEditable = b.loginPassword.getText();
if (schoolNameEditable == null || schoolNameEditable.length() == 0) {
b.loginSchoolNameLayout.setError(getString(R.string.login_error_no_school_name));
errors = true;
}
if (usernameEditable == null || usernameEditable.length() == 0) {
b.loginUsernameLayout.setError(getString(R.string.login_error_no_username));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String schoolName = schoolNameEditable.toString().toLowerCase();
String username = usernameEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginSchoolName.setText(schoolName);
b.loginUsername.setText(username);
if (!schoolName.matches("[a-z0-9_\\-]+")) {
b.loginSchoolNameLayout.setError(getString(R.string.login_error_incorrect_school_name));
errors = true;
}
if (!username.matches("[a-z0-9_\\-]+")) {
b.loginUsernameLayout.setError(getString(R.string.login_error_incorrect_username));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_IUCZNIOWIE);
args.putString("schoolName", schoolName);
args.putString("username", username);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,109 @@
/*
* 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)
}
}
}

View File

@ -1,115 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS;
public class LoginLibrusFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginLibrusBinding b;
private static final String TAG = "LoginLibrus";
private ErrorSnackbar errorSnackbar;
public LoginLibrusFragment() { }
@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);
errorSnackbar = ((LoginActivity) getActivity()).errorSnackbar;
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_librus, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
ApiError error = LoginActivity.error;
if (error != null) {
switch (error.getErrorCode()) {
case ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
case ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED:
b.loginEmailLayout.setError(getString(R.string.login_error_account_not_activated));
break;
}
errorSnackbar.addError(error).show();
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginEmailLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable emailEditable = b.loginEmail.getText();
Editable passwordEditable = b.loginPassword.getText();
if (emailEditable == null || emailEditable.length() == 0) {
b.loginEmailLayout.setError(getString(R.string.login_error_no_email));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String email = emailEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginEmail.setText(email);
if (!email.matches("([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+")) {
b.loginEmailLayout.setError(getString(R.string.login_error_incorrect_email));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_LIBRUS);
args.putString("email", email);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,97 @@
/*
* 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)
}
}
}

View File

@ -1,121 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-13.
*/
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusJstBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_MODE_LIBRUS_JST;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS;
public class LoginLibrusJstFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginLibrusJstBinding b;
private static final String TAG = "LoginLibrus";
private ErrorSnackbar errorSnackbar;
public LoginLibrusJstFragment() { }
@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);
errorSnackbar = ((LoginActivity) getActivity()).errorSnackbar;
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_librus_jst, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
ApiError error = LoginActivity.error;
if (error != null) {
switch (error.getErrorCode()) {
case ERROR_LOGIN_LIBRUS_API_INVALID_LOGIN:
b.loginCodeLayout.setError(getString(R.string.login_error_incorrect_code_or_pin));
break;
}
errorSnackbar.addError(error).show();
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginCodeLayout.setError(null);
b.loginPinLayout.setError(null);
Editable codeEditable = b.loginCode.getText();
Editable pinEditable = b.loginPin.getText();
if (codeEditable == null || codeEditable.length() == 0) {
b.loginCodeLayout.setError(getString(R.string.login_error_no_code));
errors = true;
}
if (pinEditable == null || pinEditable.length() == 0) {
b.loginPinLayout.setError(getString(R.string.login_error_no_pin));
errors = true;
}
if (errors)
return;
errors = false;
String code = codeEditable.toString().toUpperCase();
String pin = pinEditable.toString();
b.loginCode.setText(code);
if (!code.matches("[A-Z0-9_]+")) {
b.loginCodeLayout.setError(getString(R.string.login_error_incorrect_code));
errors = true;
}
if (!pin.matches("[a-z0-9_]+")) {
b.loginPinLayout.setError(getString(R.string.login_error_incorrect_pin));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_LIBRUS);
args.putInt("loginMode", LOGIN_MODE_LIBRUS_JST);
args.putString("accountCode", code);
args.putString("accountPin", pin);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,100 @@
/*
* 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_API_INVALID_LOGIN
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 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 ->
b.loginCodeLayout.error = getString(R.string.login_error_incorrect_code_or_pin)
}
}
}
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)
}
}
}

View File

@ -1,136 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikBinding;
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class LoginMobidziennikFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginMobidziennikBinding b;
private static final String TAG = "LoginMobidziennik";
private ErrorSnackbar errorSnackbar;
public LoginMobidziennikFragment() { }
@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);
errorSnackbar = ((LoginActivity) getActivity()).errorSnackbar;
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_mobidziennik, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
ApiError error = LoginActivity.error;
if (error != null) {
switch (error.getErrorCode()) {
case ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
case ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD:
b.loginPasswordLayout.setError(getString(R.string.login_error_old_password));
break;
case ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED:
b.loginUsernameLayout.setError(getString(R.string.sync_error_archived));
break;
case ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS:
b.loginServerAddressLayout.setError(getString(R.string.login_error_incorrect_address));
break;
}
errorSnackbar.addError(error).show();
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginMobidziennikHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginServerAddressLayout.setError(null);
b.loginUsernameLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable serverNameEditable = b.loginServerAddress.getText();
Editable usernameEditable = b.loginUsername.getText();
Editable passwordEditable = b.loginPassword.getText();
if (serverNameEditable == null || serverNameEditable.length() == 0) {
b.loginServerAddressLayout.setError(getString(R.string.login_error_no_address));
errors = true;
}
if (usernameEditable == null || usernameEditable.length() == 0) {
b.loginUsernameLayout.setError(getString(R.string.login_error_no_login));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String serverName = serverNameEditable.toString().toLowerCase().replaceAll("(?:http://|www.|mobidziennik\\.pl|wizja\\.net|\\.)", "");
String username = usernameEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginServerAddress.setText(serverName);
b.loginUsername.setText(username);
if (!serverName.matches("^[a-z0-9_\\-]+$")) {
b.loginServerAddressLayout.setError(getString(R.string.login_error_incorrect_address));
errors = true;
}
if (!username.matches("^[a-z0-9_\\-@+.]+$")) {
b.loginUsernameLayout.setError(getString(R.string.login_error_incorrect_login));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_MOBIDZIENNIK);
args.putString("serverName", serverName);
args.putString("username", username);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,114 @@
/*
* 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)
}
}
}

View File

@ -1,112 +0,0 @@
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.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import com.google.gson.JsonObject;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent;
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent;
import pl.szczodrzynski.edziennik.data.api.models.ApiError;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding;
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.LOGIN_NO_ARGUMENTS;
public class LoginProgressFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginProgressBinding b;
private static final String TAG = "LoginProgress";
public LoginProgressFragment() { }
@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_progress, container, false);
return b.getRoot();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onFirstLoginFinishedEvent(FirstLoginFinishedEvent event) {
LoginActivity.profileObjects.add(new LoginProfileObject(
event.getLoginStore(),
event.getProfileList()));
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onSyncErrorEvent(ApiTaskErrorEvent event) {
LoginActivity.error = event.getError();
if (getActivity() == null)
return;
nav.navigateUp();
}
// TODO add progress bar in login
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
Bundle args = getArguments();
LoginActivity.error = null;
if (args == null) {
LoginActivity.error = new ApiError(TAG, LOGIN_NO_ARGUMENTS);
nav.navigateUp();
return;
}
int loginType = args.getInt("loginType", -1);
int loginMode = args.getInt("loginMode", 0);
LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject());
loginStore.mode = loginMode;
loginStore.copyFrom(args);
if (App.devMode && LoginChooserFragment.fakeLogin) {
loginStore.putLoginData("fakeLogin", true);
}
EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext());
}
@Override
public void onStart() {
EventBus.getDefault().register(this);
super.onStart();
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.LOGIN_NO_ARGUMENTS
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding
import kotlin.coroutines.CoroutineContext
class LoginProgressFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginProgressFragment"
}
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginProgressBinding
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 = FragmentLoginProgressBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val args = arguments ?: run {
activity.error(ApiError(TAG, LOGIN_NO_ARGUMENTS))
nav.navigateUp()
return
}
launch {
val firstProfileId = (app.db.profileDao().lastId ?: 0) + 1
val loginType = args.getInt("loginType", -1)
val loginMode = args.getInt("loginMode", 0)
val loginStore = LoginStore(
id = firstProfileId,
type = loginType,
mode = loginMode
)
loginStore.copyFrom(args)
if (App.devMode && LoginChooserFragment.fakeLogin) {
loginStore.putLoginData("fakeLogin", true)
}
EdziennikTask.firstLogin(loginStore).enqueue(activity)
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onFirstLoginFinishedEvent(event: FirstLoginFinishedEvent) {
if (event.profileList.isEmpty()) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.login_account_no_students)
.setMessage(R.string.login_account_no_students_text)
.setPositiveButton(R.string.ok, null)
.setOnDismissListener { nav.navigateUp() }
.show()
return
}
activity.loginStores += event.loginStore
activity.profiles += event.profileList.map { LoginSummaryProfileAdapter.Item(it) }
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions)
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
EventBus.getDefault().removeStickyEvent(event)
activity.error(event.error)
nav.navigateUp()
}
override fun onStart() {
EventBus.getDefault().register(this)
super.onStart()
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
}

View File

@ -1,268 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.ExtensionsKt;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSummaryBinding;
import pl.szczodrzynski.edziennik.databinding.RowLoginProfileListItemBinding;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_MODE_LIBRUS_EMAIL;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_MODE_VULCAN_API;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_MODE_VULCAN_WEB;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_TYPE_EDUDZIENNIK;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_TYPE_IDZIENNIK;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_TYPE_LIBRUS;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_TYPE_MOBIDZIENNIK;
import static pl.szczodrzynski.edziennik.data.api.LoginMethodsKt.LOGIN_TYPE_VULCAN;
public class LoginSummaryFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginSummaryBinding b;
private static final String TAG = "LoginSummary";
public LoginSummaryFragment() { }
@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_summary, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
LoginActivity.firstCompleted = true;
List<ItemProfileModel> profileList = new ArrayList<>();
int index = 0;
for (LoginProfileObject profileObject: LoginActivity.profileObjects) {
int subIndex = 0;
for (Profile profile: profileObject.profileList) {
List<String> subnameList = new ArrayList<>();
if (profile.getStudentClassName() != null)
subnameList.add(profile.getStudentClassName());
if (profile.getStudentSchoolYear() != null)
subnameList.add(profile.getStudentSchoolYear());
ItemProfileModel profileModel = new ItemProfileModel(
index,
subIndex,
profile.getName(),
ExtensionsKt.join(subnameList, " - "),
profileObject.loginStore.type,
profileObject.loginStore.mode,
profile.getAccountNameLong() != null,
profileObject.selectedList.get(subIndex)
);
profileList.add(profileModel);
subIndex++;
}
index++;
}
b.profileListView.setLayoutManager(new LinearLayoutManager(getContext()));
b.profileListView.setAdapter(new ProfileListAdapter(profileList));
b.registerMeSwitch.setOnCheckedChangeListener(((buttonView, isChecked) -> {
if (!isChecked) {
new MaterialDialog.Builder(getActivity())
.title(R.string.login_summary_unregister_title)
.content(R.string.login_summary_unregister_text)
.positiveText(R.string.yes)
.negativeText(R.string.cancel)
.onNegative(((dialog, which) -> {
b.registerMeSwitch.setChecked(true);
}))
.show();
}
}));
b.anotherButton.setOnClickListener((v -> nav.navigate(R.id.loginChooserFragment, null, LoginActivity.navOptions)));
b.finishButton.setOnClickListener(v -> {
if (LoginActivity.privacyPolicyAccepted) {
Bundle args = new Bundle();
args.putBoolean("registrationAllowed", b.registerMeSwitch.isChecked());
nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions);
return;
}
boolean profileSelected = true;
for (LoginProfileObject profileObject: LoginActivity.profileObjects) {
if (profileObject.selectedList.size() == 0 && profileSelected)
profileSelected = false;
}
if (!profileSelected) {
new MaterialDialog.Builder(getActivity())
.title(R.string.login_summary_no_profiles_title)
.content(R.string.login_summary_no_profiles_text)
.positiveText(R.string.ok)
.show();
return;
}
new MaterialDialog.Builder(getActivity())
.title(R.string.privacy_policy)
.content(Html.fromHtml("Korzystając z aplikacji potwierdzasz <a href=\"http://szkolny.eu/privacy-policy\">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia."))
.positiveText(R.string.i_agree)
.neutralText(R.string.i_disagree)
.onPositive(((dialog, which) -> {
Bundle args = new Bundle();
args.putBoolean("registrationAllowed", b.registerMeSwitch.isChecked());
nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions);
}))
.show();
});
}
class ItemProfileModel {
int listIndex;
int listSubIndex;
String name;
String subname;
int loginType;
int loginMode;
boolean isParent;
boolean selected;
public ItemProfileModel(int listIndex, int listSubIndex, String name, String subname, int loginType, int loginMode, boolean isParent, boolean selected) {
this.listIndex = listIndex;
this.listSubIndex = listSubIndex;
this.name = name;
this.subname = subname;
this.loginType = loginType;
this.loginMode = loginMode;
this.isParent = isParent;
this.selected = selected;
}
}
public class ProfileListAdapter extends RecyclerView.Adapter<ProfileListAdapter.ViewHolder> {
private List<ItemProfileModel> profileList;
public ProfileListAdapter(List<ItemProfileModel> profileList) {
this.profileList = profileList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RowLoginProfileListItemBinding b = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.row_login_profile_list_item, parent, false);
return new ViewHolder(b);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
RowLoginProfileListItemBinding b = holder.b;
ItemProfileModel m = profileList.get(position);
b.textView.setText(m.name);
b.checkBox.setChecked(m.selected);
b.checkBox.jumpDrawablesToCurrentState();
View.OnClickListener onClickListener = v -> {
if (v instanceof CheckBox) {
m.selected = ((CheckBox) v).isChecked();
} else {
m.selected = !m.selected;
b.checkBox.setChecked(m.selected);
b.checkBox.jumpDrawablesToCurrentState();
}
LoginActivity.profileObjects.get(m.listIndex).selectedList.set(m.listSubIndex, m.selected);
};
b.checkBox.setOnClickListener(onClickListener);
b.getRoot().setOnClickListener(onClickListener);
int imageRes = 0;
if (m.loginType == LOGIN_TYPE_MOBIDZIENNIK) {
imageRes = R.drawable.logo_mobidziennik;
}
else if (m.loginType == LOGIN_TYPE_IDZIENNIK) {
imageRes = R.drawable.logo_idziennik;
}
else if (m.loginType == LOGIN_TYPE_LIBRUS) {
if (m.loginMode == LOGIN_MODE_LIBRUS_EMAIL) {
imageRes = R.drawable.logo_librus;
}
else {
imageRes = R.drawable.logo_synergia;
}
}
else if (m.loginType == LOGIN_TYPE_VULCAN) {
if (m.loginMode == LOGIN_MODE_VULCAN_WEB) {
imageRes = R.drawable.logo_vulcan;
}
else if (m.loginMode == LOGIN_MODE_VULCAN_API) {
imageRes = R.drawable.logo_dzienniczek;
}
}
else if (m.loginType == LOGIN_TYPE_EDUDZIENNIK) {
imageRes = R.drawable.logo_edudziennik;
}
if (imageRes != 0) {
b.registerIcon.setImageResource(imageRes);
}
if (m.isParent) {
b.accountType.setText(R.string.login_summary_account_parent);
}
else {
b.accountType.setText(R.string.login_summary_account_child);
}
if (m.subname.trim().isEmpty()) {
b.textDetails.setText(null);
b.textDetails.setVisibility(View.GONE);
}
else {
b.textDetails.setText(m.subname);
b.textDetails.setVisibility(View.VISIBLE);
}
//b.root.setOnClickListener(onClickListener);
//holder.bind(b.textView, onClickListener);
}
@Override
public int getItemCount() {
return profileList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RowLoginProfileListItemBinding b;
public ViewHolder(@NonNull RowLoginProfileListItemBinding b) {
super(b.getRoot());
this.b = b;
}
}
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-3.
*/
package pl.szczodrzynski.edziennik.ui.modules.login
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
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.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class LoginSummaryFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginSummaryFragment"
}
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginSummaryBinding
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 = FragmentLoginSummaryBinding.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 }
}
isNestedScrollingEnabled = false
setHasFixedSize(true)
layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context))
}
b.registerMeSwitch.onChange { _, isChecked ->
if (isChecked)
return@onChange
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.login_summary_unregister_title)
.setMessage(R.string.login_summary_unregister_text)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel) { _, _ -> b.registerMeSwitch.isChecked = true }
.show()
}
b.anotherButton.onClick {
nav.navigate(R.id.loginChooserFragment, null, LoginActivity.navOptions)
}
b.finishButton.onClick {
if (!app.config.privacyPolicyAccepted) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.privacy_policy)
.setMessage(Html.fromHtml("Korzystając z aplikacji potwierdzasz <a href=\"http://szkolny.eu/privacy-policy\">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia."))
.setPositiveButton(R.string.i_agree) { _, _ ->
app.config.privacyPolicyAccepted = true
b.finishButton.performClick()
}
.setNegativeButton(R.string.i_disagree, null)
.show()
return@onClick
}
val args = Bundle(
"registrationAllowed" to b.registerMeSwitch.isChecked
)
nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions)
}
}
}

View File

@ -0,0 +1,84 @@
/*
* 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.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.databinding.RowLoginProfileListItemBinding
import pl.szczodrzynski.edziennik.joinNotNullStrings
import pl.szczodrzynski.edziennik.onClick
class LoginSummaryProfileAdapter(
val context: Context,
val items: List<Item>,
val onSelectionChanged: ((item: Item) -> Unit)? = null
) : RecyclerView.Adapter<LoginSummaryProfileAdapter.ViewHolder>() {
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.performClick()
}
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)
}

View File

@ -1,58 +0,0 @@
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.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncErrorBinding;
public class LoginSyncErrorFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginSyncErrorBinding b;
private static final String TAG = "LoginSyncError";
public LoginSyncErrorFragment() { }
@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_sync_error, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.errorDetails.setText(LoginActivity.error == null ? "" : LoginActivity.error.getStringReason(getActivity()));
b.reportButton.setOnClickListener((v -> {
// TODO error report activity open here app.apiEdziennik.guiReportError(getActivity(), LoginActivity.error, null);
}));
b.nextButton.setOnClickListener((v -> {
nav.navigate(R.id.loginFinishFragment, null, LoginActivity.navOptions);
}));
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncErrorBinding
import pl.szczodrzynski.edziennik.onClick
import kotlin.coroutines.CoroutineContext
class LoginSyncErrorFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginSyncErrorFragment"
}
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginSyncErrorBinding
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 = FragmentLoginSyncErrorBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
b.errorDetails.text = activity.lastError?.getStringReason(activity)
activity.lastError = null
b.nextButton.onClick {
nav.navigate(R.id.loginFinishFragment, arguments, LoginActivity.navOptions)
}
}
}

View File

@ -1,20 +1,19 @@
package pl.szczodrzynski.edziennik.ui.modules.login
import android.os.AsyncTask
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.Navigation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskProgressEvent
@ -22,38 +21,87 @@ import pl.szczodrzynski.edziennik.data.api.events.ApiTaskStartedEvent
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.*
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_DISABLED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_UNSPECIFIED
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding
import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
class LoginSyncFragment : Fragment() {
class LoginSyncFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "LoginSyncFragment"
}
private lateinit var app: App
private lateinit var activity: LoginActivity
private lateinit var b: FragmentLoginSyncBinding
private val nav: NavController by lazy { Navigation.findNavController(activity, R.id.nav_host_fragment) }
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var finishArguments: Bundle
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as LoginActivity?) ?: return null
if (context == null)
return null
context ?: return null
app = activity.application as App
b = FragmentLoginSyncBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val profiles = activity.profiles.filter { it.isSelected }.map { it.profile }
val loginStores = activity.loginStores.filter { store -> profiles.any { it.loginStoreId == store.id } }
val registrationAllowed = arguments?.getBoolean("registrationAllowed") ?: false
profiles.forEach {
it.registration = if (registrationAllowed)
REGISTRATION_ENABLED
else
REGISTRATION_DISABLED
val typeList = listOf(
EventType(it.id, TYPE_HOMEWORK.toLong(), getString(R.string.event_type_homework), COLOR_HOMEWORK),
EventType(it.id, TYPE_DEFAULT.toLong(), getString(R.string.event_other), COLOR_DEFAULT),
EventType(it.id, TYPE_EXAM.toLong(), getString(R.string.event_exam), COLOR_EXAM),
EventType(it.id, TYPE_SHORT_QUIZ.toLong(), getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ),
EventType(it.id, TYPE_ESSAY.toLong(), getString(R.string.event_essay), COLOR_SHORT_QUIZ),
EventType(it.id, TYPE_PROJECT.toLong(), getString(R.string.event_project), COLOR_PROJECT),
EventType(it.id, TYPE_PT_MEETING.toLong(), getString(R.string.event_pt_meeting), COLOR_PT_MEETING),
EventType(it.id, TYPE_EXCURSION.toLong(), getString(R.string.event_excursion), COLOR_EXCURSION),
EventType(it.id, TYPE_READING.toLong(), getString(R.string.event_reading), COLOR_READING),
EventType(it.id, TYPE_CLASS_EVENT.toLong(), getString(R.string.event_class_event), COLOR_CLASS_EVENT),
EventType(it.id, TYPE_INFORMATION.toLong(), getString(R.string.event_information), COLOR_INFORMATION)
)
app.db.eventTypeDao().addAll(typeList)
}
app.db.profileDao().addAll(profiles)
app.db.loginStoreDao().addAll(loginStores)
finishArguments = Bundle(
"firstProfileId" to profiles.firstOrNull()?.id,
"firstRun" to !app.config.loginFinished
)
app.config.loginFinished = true
val profileIds = profiles.map { it.id }
EdziennikTask.syncProfileList(profileIds).enqueue(activity)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncStartedEvent(event: ApiTaskStartedEvent) {
b.loginSyncSubtitle1.text = Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, event.profile?.name ?: ""))
b.loginSyncSubtitle1.text = listOf(
getString(R.string.login_sync_subtitle_1_format),
event.profile?.name?.asBoldSpannable()
).concat(" ")
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) {
nav.navigate(R.id.loginFinishFragment, null, LoginActivity.navOptions)
nav.navigate(R.id.loginFinishFragment, finishArguments, LoginActivity.navOptions)
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -63,93 +111,11 @@ class LoginSyncFragment : Fragment() {
b.loginSyncSubtitle2.text = event.progressText
}
@Subscribe(threadMode = ThreadMode.MAIN)
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
LoginActivity.error = event.error
nav.navigate(R.id.loginSyncErrorFragment, null, LoginActivity.navOptions)
}
private fun begin() {
AsyncTask.execute {
var profileId = app.profileLastId() + 1
val firstProfileId = profileId
var loginStoreId = profileId
// profileId contains the first ID free to use
val profileIds = mutableListOf<Int>()
for (profileObject in LoginActivity.profileObjects) {
for ((subIndex, profile) in profileObject.profileList.withIndex()) {
if (profileObject.selectedList[subIndex]) {
saveProfile(
profile,
profileObject.loginStore,
profileId,
loginStoreId
)
profileIds += profileId
profileId++
}
}
loginStoreId = profileId
}
/*for (profile in app.db.profileDao().allNow) {
d(TAG, profile.toString())
}
for (loginStore in app.db.loginStoreDao().allNow) {
d(TAG, loginStore.toString())
}*/
if (app.config.loginFinished) {
LoginFinishFragment.firstRun = false
} else {
LoginFinishFragment.firstRun = true
app.config.loginFinished = true
}
LoginFinishFragment.firstProfileId = firstProfileId
EdziennikTask.syncProfileList(profileIds).enqueue(activity)
}
}
private fun saveProfile(profile: Profile, loginStore: LoginStore, profileId: Int, loginStoreId: Int) {
profile.registration = REGISTRATION_UNSPECIFIED
if (arguments != null) {
if (arguments!!.getBoolean("registrationAllowed", false)) {
profile.registration = REGISTRATION_ENABLED
} else {
profile.registration = REGISTRATION_DISABLED
}
}
profile.id = profileId
profile.loginStoreId = loginStoreId
loginStore.id = loginStoreId
val typeList = listOf(
EventType(profileId, TYPE_HOMEWORK.toLong(), getString(R.string.event_type_homework), COLOR_HOMEWORK),
EventType(profileId, TYPE_DEFAULT.toLong(), getString(R.string.event_other), COLOR_DEFAULT),
EventType(profileId, TYPE_EXAM.toLong(), getString(R.string.event_exam), COLOR_EXAM),
EventType(profileId, TYPE_SHORT_QUIZ.toLong(), getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ),
EventType(profileId, TYPE_ESSAY.toLong(), getString(R.string.event_essay), COLOR_SHORT_QUIZ),
EventType(profileId, TYPE_PROJECT.toLong(), getString(R.string.event_project), COLOR_PROJECT),
EventType(profileId, TYPE_PT_MEETING.toLong(), getString(R.string.event_pt_meeting), COLOR_PT_MEETING),
EventType(profileId, TYPE_EXCURSION.toLong(), getString(R.string.event_excursion), COLOR_EXCURSION),
EventType(profileId, TYPE_READING.toLong(), getString(R.string.event_reading), COLOR_READING),
EventType(profileId, TYPE_CLASS_EVENT.toLong(), getString(R.string.event_class_event), COLOR_CLASS_EVENT),
EventType(profileId, TYPE_INFORMATION.toLong(), getString(R.string.event_information), COLOR_INFORMATION)
)
app.db.eventTypeDao().addAll(typeList)
app.db.profileDao().add(profile)
app.db.loginStoreDao().add(loginStore)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!isAdded)
return
LoginActivity.error = null
begin()
EventBus.getDefault().removeStickyEvent(event)
activity.error(event.error)
nav.navigate(R.id.loginSyncErrorFragment, finishArguments, LoginActivity.navOptions)
}
override fun onStart() {

Some files were not shown because too many files have changed in this diff Show More