[APIv2] Add API Service. Update other APIv2 components. Update Profile DAO

This commit is contained in:
Kuba Szczodrzyński 2019-09-29 17:19:38 +02:00
parent 5edd4d5922
commit 0bf2026a64
51 changed files with 843 additions and 265 deletions

View File

@ -218,6 +218,8 @@
android:name=".sync.SyncService"
android:icon="@mipmap/ic_launcher"
android:label="@string/sync_service" />
<service android:name=".api.v2.ApiService" />
</application>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@ -19,7 +19,6 @@ import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;
import com.chuckerteam.chucker.api.ChuckerCollector;
import com.chuckerteam.chucker.api.ChuckerInterceptor;
@ -81,7 +80,6 @@ import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.models.AppConfig;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
import pl.szczodrzynski.edziennik.receivers.JobsCreator;
import pl.szczodrzynski.edziennik.sync.SyncJob;
import pl.szczodrzynski.edziennik.utils.PermissionChecker;
@ -617,7 +615,7 @@ public class App extends androidx.multidex.MultiDexApplication {
}
public ProfileFull profileGetOrNull(int id) {
return db.profileDao().getByIdNow(id);
return db.profileDao().getFullByIdNow(id);
}
public void profileLoadById(int id) {
@ -632,7 +630,7 @@ public class App extends androidx.multidex.MultiDexApplication {
return;
}*/
if (profile == null || profile.getId() != id) {
profile = db.profileDao().getByIdNow(id);
profile = db.profileDao().getFullByIdNow(id);
/*if (profile == null) {
profileLoadById(id);
return;
@ -659,7 +657,7 @@ public class App extends androidx.multidex.MultiDexApplication {
/*public void profileRemove(int id)
{
Profile profile = db.profileDao().getByIdNow(id);
Profile profile = db.profileDao().getFullByIdNow(id);
if (profile.id == profile.loginStoreId) {
// this profile is the owner of the login store

View File

@ -10,10 +10,13 @@ import androidx.core.app.ActivityCompat
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import im.wangchao.mhttp.Response
import pl.szczodrzynski.edziennik.datamodels.Profile
import pl.szczodrzynski.edziennik.datamodels.Teacher
import pl.szczodrzynski.navlib.crc16
import pl.szczodrzynski.navlib.getColorFromRes
import java.text.SimpleDateFormat
import java.util.*
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
@ -117,4 +120,11 @@ fun Activity.isStoragePermissionGranted(): Boolean {
} else {
true
}
}
fun Response?.getUnixDate(): Long {
val rfcDate = this?.headers()?.get("date") ?: return currentTimeUnix()
val pattern = "EEE, dd MMM yyyy HH:mm:ss Z"
val format = SimpleDateFormat(pattern, Locale.ENGLISH)
return format.parse(rfcDate).time / 1000
}

View File

@ -41,7 +41,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.*
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.*
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.datamodels.LoginStore

View File

@ -244,7 +244,7 @@ public class WidgetTimetable extends AppWidgetProvider {
filterOutArchived(profileList);
}
else {
Profile profile = app.db.profileDao().getByIdNow(widgetConfig.profileId);
Profile profile = app.db.profileDao().getFullByIdNow(widgetConfig.profileId);
if (profile != null) {
profileList.add(profile);
}

View File

@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.WidgetTimetable;
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.datamodels.AnnouncementFull;
import pl.szczodrzynski.edziennik.datamodels.Attendance;
@ -81,16 +81,16 @@ import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_ARCHIVED;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_NOT_FOUND;
import static pl.szczodrzynski.edziennik.api.AppError.stringErrorCode;
import static pl.szczodrzynski.edziennik.api.AppError.stringErrorType;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_AGENDA;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ALL;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ANNOUNCEMENTS;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ATTENDANCES;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_GRADES;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_HOMEWORKS;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_INBOX;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_OUTBOX;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_NOTICES;
import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_TIMETABLE;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_AGENDA;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ALL;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ANNOUNCEMENTS;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ATTENDANCES;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_GRADES;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_HOMEWORKS;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_MESSAGES_INBOX;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_MESSAGES_OUTBOX;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_NOTICES;
import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_TIMETABLE;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_FINAL;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_PROPOSED;
@ -115,7 +115,7 @@ public class Edziennik {
private static boolean registerEmpty;
public static int oldLuckyNumber;
public static EdziennikInterface getApi(App app, int loginType) {
public static OldEdziennikInterface getApi(App app, int loginType) {
switch (loginType) {
default:
case LOGIN_TYPE_MOBIDZIENNIK:
@ -625,7 +625,7 @@ public class Edziennik {
* Used in services, login form and {@code guiSync}
* <p>
* May be ran on worker thread.
* {@link EdziennikInterface}.sync is ran always on worker thread.
* {@link OldEdziennikInterface}.sync is ran always on worker thread.
* Every callback is ran on the UI thread.
*
* @param app
@ -676,7 +676,7 @@ public class Edziennik {
}
};
AsyncTask.execute(() -> {
ProfileFull profile = app.db.profileDao().getByIdNow(profileId);
ProfileFull profile = app.db.profileDao().getFullByIdNow(profileId);
if (profile != null) {
if (profile.getArchived()) {
@ -1130,7 +1130,7 @@ public class Edziennik {
.show();
}
public void removeProfile(int profileId) {
Profile profileObject = app.db.profileDao().getByIdNow(profileId);
Profile profileObject = app.db.profileDao().getFullByIdNow(profileId);
if (profileObject == null)
return;
app.db.announcementDao().clear(profileId);

View File

@ -35,7 +35,7 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback;
import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback;
@ -91,7 +91,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.crc32;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
import static pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue;
public class Iuczniowie implements EdziennikInterface {
public class Iuczniowie implements OldEdziennikInterface {
public Iuczniowie(App app) {
this.app = app;
}

View File

@ -46,7 +46,7 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback;
import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback;
@ -131,7 +131,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.d;
import static pl.szczodrzynski.edziennik.utils.Utils.getGradeValue;
import static pl.szczodrzynski.edziennik.utils.Utils.strToInt;
public class Librus implements EdziennikInterface {
public class Librus implements OldEdziennikInterface {
public Librus(App app) {
this.app = app;
}
@ -2537,7 +2537,7 @@ public class Librus implements EdziennikInterface {
&& (el = obj.get("Id")) != null) {
type = el.getAsInt();
}
/*EventType typeObject = app.db.eventTypeDao().getByIdNow(profileId, type);
/*EventType typeObject = app.db.eventTypeDao().getFullByIdNow(profileId, type);
if (typeObject == null) {
getCustomTypes = true;
}*/

View File

@ -41,7 +41,7 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback;
import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback;
@ -99,7 +99,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.d;
import static pl.szczodrzynski.edziennik.utils.Utils.monthFromName;
import static pl.szczodrzynski.edziennik.utils.Utils.strToInt;
public class Mobidziennik implements EdziennikInterface {
public class Mobidziennik implements OldEdziennikInterface {
public Mobidziennik(App app) {
this.app = app;
}

View File

@ -36,7 +36,7 @@ import okio.Buffer;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface;
import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback;
import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback;
import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback;
@ -93,7 +93,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.getGradeValue;
import static pl.szczodrzynski.edziennik.utils.Utils.getVulcanGradeColor;
import static pl.szczodrzynski.edziennik.utils.Utils.intToStr;
public class Vulcan implements EdziennikInterface {
public class Vulcan implements OldEdziennikInterface {
public Vulcan(App app) {
this.app = app;
}

View File

@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo;
import pl.szczodrzynski.edziennik.models.Endpoint;
public interface EdziennikInterface {
public interface OldEdziennikInterface {
/**
* Sync all Edziennik data.

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
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.api.v2.events.requests.MessageGetRequest
import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent
import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest
import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest
import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.librus.Librus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
import pl.szczodrzynski.edziennik.datamodels.LoginStore
import pl.szczodrzynski.edziennik.datamodels.Profile
import kotlin.math.min
class ApiService : Service() {
companion object {
const val TAG = "ApiService"
const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.GET_DATA"
}
private val app by lazy { applicationContext as App }
private val taskQueue = mutableListOf<ApiTask>()
private val errorList = mutableListOf<ApiError>()
private var taskRunning = false
private var taskRunningId = -1
private var taskMaximumId = 0
private var taskProfileId = -1
private var taskProfileName: String? = null
private var taskProgress = 0
private var taskProgressRes: Int? = null
private val taskCallback = object : EdziennikCallback {
override fun onCompleted() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onError(apiError: ApiError) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onProgress(step: Int) {
taskProgress += step
taskProgress = min(100, taskProgress)
EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes))
}
override fun onStartProgress(stringRes: Int) {
}
}
private fun sync() {
if (taskRunning)
return
if (taskQueue.size <= 0)
return // TODO stopSelf() or sth
val task = taskQueue.removeAt(0)
taskRunning = true
taskRunningId = task.taskId
// get the requested profile and login store
val profile: Profile? = app.db.profileDao().getByIdNow(task.profileId)
if (profile == null || !profile.syncEnabled) {
return
}
val loginStore: LoginStore? = app.db.loginStoreDao().getByIdNow(profile.loginStoreId)
if (loginStore == null) {
return
}
// save the profile ID and name as the current task's
taskProfileId = profile.id
taskProfileName = profile.name
taskProgress = 0
taskProgressRes = null
val edziennikInterface = when (loginStore.type) {
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
else -> null
}
if (edziennikInterface == null) {
return
}
when (task) {
is SyncProfileRequest -> edziennikInterface.sync(task.featureIds ?: Features.getAllIds())
is SyncViewRequest -> edziennikInterface.sync(Features.getIdsByView(task.targetId))
is MessageGetRequest -> edziennikInterface.getMessage(task.messageId)
}
}
@Subscribe(threadMode = ThreadMode.ASYNC)
fun onSyncRequest(syncRequest: SyncRequest) {
app.db.profileDao().idsForSyncNow.forEach { id ->
taskQueue += SyncProfileRequest(id, null)
}
sync()
}
@Subscribe(threadMode = ThreadMode.ASYNC)
fun onSyncProfileRequest(syncProfileRequest: SyncProfileRequest) {
Log.d(TAG, syncProfileRequest.toString())
taskQueue += syncProfileRequest
sync()
}
@Subscribe(threadMode = ThreadMode.ASYNC)
fun onMessageGetRequest(messageGetRequest: MessageGetRequest) {
Log.d(TAG, messageGetRequest.toString())
taskQueue += messageGetRequest
sync()
}
private val notification by lazy {
NotificationCompat.Builder(this, NOTIFICATION_API_CHANNEL_ID)
.setContentTitle("API")
.setContentText("API is running")
.setSmallIcon(R.drawable.ic_notification)
.build()
}
override fun onCreate() {
EventBus.getDefault().register(this)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(1, notification)
return START_NOT_STICKY
}
override fun onDestroy() {
EventBus.getDefault().unregister(this)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}

View File

@ -4,18 +4,6 @@
package pl.szczodrzynski.edziennik.api.v2
internal const val FEATURE_ANY = -1
const val FEATURE_ALL = 0
const val FEATURE_TIMETABLE = 1
const val FEATURE_AGENDA = 2
const val FEATURE_GRADES = 3
const val FEATURE_HOMEWORKS = 4
const val FEATURE_NOTICES = 5
const val FEATURE_ATTENDANCES = 6
const val FEATURE_MESSAGES_INBOX = 7
const val FEATURE_MESSAGES_OUTBOX = 8
const val FEATURE_ANNOUNCEMENTS = 9
const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp"
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
@ -42,7 +30,7 @@ const val LIBRUS_JST_DEMO_CODE = "68656A21"
const val LIBRUS_JST_DEMO_PIN = "1290"
/** https://synergia.librus.pl/loguj/token/TOKEN/przenies */
const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies/"
const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies"
const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/"
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="

View File

@ -4,20 +4,130 @@
package pl.szczodrzynski.edziennik.api.v2
import android.util.Log
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiGrades
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergiaGrades
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
const val ENDPOINT_LIBRUS_API_ME = 0
const val ENDPOINT_LIBRUS_API_GRADES = 0
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 0
const val ENDPOINT_LIBRUS_API_ME = 101
const val ENDPOINT_LIBRUS_API_SCHOOLS = 102
const val ENDPOINT_LIBRUS_API_CLASSES = 103
const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 104
const val ENDPOINT_LIBRUS_API_UNITS = 105
const val ENDPOINT_LIBRUS_API_USERS = 106
const val ENDPOINT_LIBRUS_API_SUBJECTS = 107
const val ENDPOINT_LIBRUS_API_CLASSROOMS = 108
const val ENDPOINT_LIBRUS_API_TIMETABLES = 109
const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 110
const val ENDPOINT_LIBRUS_API_NORMAL_GC = 111
const val ENDPOINT_LIBRUS_API_POINT_GC = 112
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 113
const val ENDPOINT_LIBRUS_API_TEXT_GC = 114
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 115
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 116
const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 117
const val ENDPOINT_LIBRUS_API_POINT_GRADES = 118
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 119
const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 120
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 121
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 122
const val ENDPOINT_LIBRUS_API_EVENTS = 123
const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 124
const val ENDPOINT_LIBRUS_API_HOMEWORK = 125
const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 126
const val ENDPOINT_LIBRUS_API_NOTICES = 127
const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 128
const val ENDPOINT_LIBRUS_API_ATTENDANCE = 129
const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 130
const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 131
const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 132
const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 133
const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 134
const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 201
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 202
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 301
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 302
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 303
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 304
const val ENDPOINT_LIBRUS_MESSAGES_GET = 304
val endpoints = listOf(
Endpoint(LOGIN_TYPE_LIBRUS, ENDPOINT_LIBRUS_API_ME, null, LibrusApiMe::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API},
Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusSynergiaGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_SYNERGIA },
Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusApiGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API }
// LIBRUS: API
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
ENDPOINT_LIBRUS_API_TIMETABLES,
ENDPOINT_LIBRUS_API_SUBSTITUTIONS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf(
ENDPOINT_LIBRUS_API_EVENTS,
ENDPOINT_LIBRUS_API_EVENT_TYPES,
ENDPOINT_LIBRUS_API_PT_MEETINGS,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS,
ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS,
ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC,
ENDPOINT_LIBRUS_API_POINT_GC,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC,
ENDPOINT_LIBRUS_API_TEXT_GC,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC,
ENDPOINT_LIBRUS_API_NORMAL_GRADES,
ENDPOINT_LIBRUS_API_POINT_GRADES,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES,
ENDPOINT_LIBRUS_API_TEXT_GRADES,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_API_HOMEWORK
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf(
ENDPOINT_LIBRUS_API_NOTICES
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf(
ENDPOINT_LIBRUS_API_ATTENDANCE,
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf(
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_API_ME
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_LIBRUS_API_SCHOOLS,
ENDPOINT_LIBRUS_API_UNITS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf(
ENDPOINT_LIBRUS_API_CLASSES
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf(
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_LIBRUS_API_LUCKY_NUMBER
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf(
ENDPOINT_LIBRUS_API_USERS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf(
ENDPOINT_LIBRUS_API_SUBJECTS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf(
ENDPOINT_LIBRUS_API_CLASSROOMS
), LOGIN_METHOD_LIBRUS_API),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO
), LOGIN_METHOD_LIBRUS_SYNERGIA),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO
), LOGIN_METHOD_LIBRUS_SYNERGIA),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_SYNERGIA_GRADES
), LOGIN_METHOD_LIBRUS_SYNERGIA),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED), LOGIN_METHOD_LIBRUS_MESSAGES),
Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), LOGIN_METHOD_LIBRUS_MESSAGES)
)
/*

View File

@ -59,11 +59,6 @@ const val ERROR_LOGIN_LIBRUS_API_OTHER = 131
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING = 132
const val ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED = 133
const val ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR = 134
const val ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR = 135
const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 136
const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410 = 137
const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND = 138
const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER = 139
const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING = 139
const val ERROR_LIBRUS_API_TOKEN_EXPIRED = 140
const val ERROR_LIBRUS_API_INSUFFICIENT_SCOPES = 141
@ -83,8 +78,22 @@ const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154
const val ERROR_LIBRUS_MESSAGES_ACCESS_DENIED = 155
const val ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED = 156
const val ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID = 157
const val ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED = 158
const val ERROR_LIBRUS_PORTAL_API_DISABLED = 159
const val ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 160
const val ERROR_LIBRUS_PORTAL_OTHER = 161
const val ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND = 162
const val ERROR_LOGIN_LIBRUS_PORTAL_OTHER = 163
const val ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED = 164
const val ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED = 165
const val ERROR_LOGIN_LIBRUS_PORTAL_NO_CLIENT_ID = 166
const val ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE = 167
const val ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH = 168
const val ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT = 169
const val ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT = 170
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID = 171
const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901
const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902
const val EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903
const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903
const val EXCEPTION_LIBRUS_API_REQUEST = 904

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2
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_ATTENDANCES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORKS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_NOTICES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.datamodels.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.datamodels.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.messages.MessagesFragment
import pl.szczodrzynski.edziennik.messages.MessagesListFragment
const val FEATURE_ALL = 0
const val FEATURE_TIMETABLE = 1
const val FEATURE_AGENDA = 2
const val FEATURE_GRADES = 3
const val FEATURE_HOMEWORK = 4
const val FEATURE_NOTICES = 5
const val FEATURE_ATTENDANCES = 6
const val FEATURE_MESSAGES_INBOX = 7
const val FEATURE_MESSAGES_OUTBOX = 8
const val FEATURE_ANNOUNCEMENTS = 9
const val FEATURE_STUDENT_INFO = 101
const val FEATURE_STUDENT_NUMBER = 109
const val FEATURE_SCHOOL_INFO = 102
const val FEATURE_CLASS_INFO = 103
const val FEATURE_TEAM_INFO = 104
const val FEATURE_LUCKY_NUMBER = 105
const val FEATURE_TEACHERS = 106
const val FEATURE_SUBJECTS = 107
const val FEATURE_CLASSROOMS = 108
const val FEATURE_MESSAGE_GET = 201
object Features {
private fun getAllNecessary(): List<Int> = listOf(
FEATURE_STUDENT_INFO,
FEATURE_STUDENT_NUMBER,
FEATURE_SCHOOL_INFO,
FEATURE_CLASS_INFO,
FEATURE_TEAM_INFO,
FEATURE_LUCKY_NUMBER,
FEATURE_TEACHERS,
FEATURE_SUBJECTS,
FEATURE_CLASSROOMS)
private fun getAllFeatures(): List<Int> = listOf(
FEATURE_TIMETABLE,
FEATURE_AGENDA,
FEATURE_GRADES,
FEATURE_HOMEWORK,
FEATURE_NOTICES,
FEATURE_ATTENDANCES,
FEATURE_MESSAGES_INBOX,
FEATURE_MESSAGES_OUTBOX,
FEATURE_ANNOUNCEMENTS)
fun getAllIds(): List<Int> = getAllFeatures() + getAllNecessary()
fun getIdsByView(targetId: Int): List<Int> {
return when (targetId) {
DRAWER_ITEM_HOME -> getAllFeatures()
DRAWER_ITEM_TIMETABLE -> listOf(FEATURE_TIMETABLE)
DRAWER_ITEM_AGENDA -> listOf(FEATURE_AGENDA)
DRAWER_ITEM_GRADES -> listOf(FEATURE_GRADES)
DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
TYPE_RECEIVED -> listOf(FEATURE_MESSAGES_INBOX)
TYPE_SENT -> listOf(FEATURE_MESSAGES_OUTBOX)
else -> listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX)
}
DRAWER_ITEM_HOMEWORKS -> listOf(FEATURE_HOMEWORK)
DRAWER_ITEM_NOTICES -> listOf(FEATURE_NOTICES)
DRAWER_ITEM_ATTENDANCES -> listOf(FEATURE_ATTENDANCES)
DRAWER_ITEM_ANNOUNCEMENTS -> listOf(FEATURE_ANNOUNCEMENTS)
else -> getAllFeatures()
} + getAllNecessary()
}
}

View File

@ -10,6 +10,8 @@ import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
val SYNERGIA_API_ENABLED = "true".toBoolean()
const val LOGIN_TYPE_MOBIDZIENNIK = 1
const val LOGIN_TYPE_LIBRUS = 2
const val LOGIN_TYPE_IUCZNIOWIE = 3
@ -37,14 +39,29 @@ const val LOGIN_METHOD_VULCAN_WEB = 100
const val LOGIN_METHOD_VULCAN_API = 200
val librusLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, null, LoginLibrusPortal::class.java) { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, null, LoginLibrusApi::class.java) { _, loginStore ->
if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, listOf(FEATURE_GRADES, FEATURE_HOMEWORKS, FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusSynergia::class.java) { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusMessages::class.java) { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
}
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, LoginLibrusPortal::class.java)
.withIsPossible { _, loginStore ->
loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL
}
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, LoginLibrusApi::class.java)
.withIsPossible { _, loginStore ->
loginStore.mode != LOGIN_MODE_LIBRUS_SYNERGIA || SYNERGIA_API_ENABLED
}
.withRequiredLoginMethod { _, loginStore ->
if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LoginLibrusSynergia::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LoginLibrusMessages::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
}
)

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
class SyncErrorEvent(val error: ApiError)

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class SyncFinishedEvent

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class SyncProfileFinishedEvent(val profileId: Int)

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class SyncProgressEvent(val profileId: Int, val profileName: String?, val progress: Int, val progressRes: Int?)

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class SyncStartedEvent(val profileId: Int)

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
data class MessageGetRequest(override val profileId: Int, val messageId: Int) : ApiTask(profileId) {
override fun toString(): String {
return "MessageGetRequest(profileId=$profileId, messageId=$messageId)"
}
}

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
data class SyncProfileRequest(override val profileId: Int, val featureIds: List<Int>?) : ApiTask(profileId) {
override fun toString(): String {
return "SyncProfileRequest(profileId=$profileId, featureIds=$featureIds)"
}
}

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
class SyncRequest()

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
import pl.szczodrzynski.edziennik.api.v2.models.ApiTask
class SyncViewRequest(override val profileId: Int, val targetId: Int) : ApiTask(profileId) {
override fun toString(): String {
return "SyncViewRequest(profileId=$profileId, targetId=$targetId)"
}
}

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2.interfaces
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
/**
* A callback passed only to an e-register class.
* All [Endpoint]s and [LoginMethod]s receive this callback,
* but may only use [EndpointCallback]'s methods.
*/
interface EdziennikCallback : EndpointCallback {
fun onCompleted()
}

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2.interfaces
interface EdziennikInterface {
fun sync(featureIds: List<Int>)
fun getMessage(messageId: Int)
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2.interfaces
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Endpoint
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
/**
* A callback passed to all [Endpoint]s and [LoginMethod]s
*/
interface EndpointCallback {
fun onError(apiError: ApiError)
fun onProgress(step: Int)
fun onStartProgress(stringRes: Int)
}

View File

@ -1,20 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-20.
*/
package pl.szczodrzynski.edziennik.api.v2.interfaces
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
import pl.szczodrzynski.edziennik.datamodels.LoginStore
import pl.szczodrzynski.edziennik.datamodels.Profile
abstract class ILoginMethod(
val app: App,
val profile: Profile?,
val loginStore: LoginStore,
val callback: ProgressCallback,
val onSuccess: () -> Unit
) {
}

View File

@ -9,22 +9,30 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.datamodels.LoginStore
import pl.szczodrzynski.edziennik.datamodels.Profile
import pl.szczodrzynski.edziennik.datamodels.ProfileFull
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: SyncCallback) {
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
val internalErrorList = mutableListOf<Int>()
lateinit var data: DataLibrus
val data: DataLibrus
init {
data = DataLibrus(app, profile, loginStore).apply {
callback = wrapCallback(this@Librus.callback)
}
data.satisfyLoginMethods()
}
override fun sync(featureIds: List<Int>) {
}
override fun getMessage(messageId: Int) {
}

View File

@ -7,7 +7,6 @@ import pl.szczodrzynski.edziennik.api.interfaces.*
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.datamodels.LoginStore
import pl.szczodrzynski.edziennik.datamodels.MessageFull
@ -15,10 +14,9 @@ import pl.szczodrzynski.edziennik.datamodels.Profile
import pl.szczodrzynski.edziennik.datamodels.ProfileFull
import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo
import pl.szczodrzynski.edziennik.models.Endpoint
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.lang.Exception
class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface {
class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : OldEdziennikInterface {
private val TAG = "librus.Librus"
lateinit var syncCallback: SyncCallback

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.api.v2.librus
import android.content.Context
import android.content.Intent
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.AppError
@ -30,21 +31,24 @@ class LibrusTest(val app: App) {
putStudentData("accountLogin", "1234567")
putStudentData("accountToken", "token")
putStudentData("accountTokenTime", 1569523077)
//putStudentData("accountToken", "token")
//putStudentData("accountTokenTime", 1569458277)
}
val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply {
addProperty("email", "test@example.com")
addProperty("password", "zaq1@WSX")
addProperty("accessToken", "token")
addProperty("refreshToken", "refresh")
addProperty("tokenExpiryTime", 1569523077)
//addProperty("accessToken", "token")
//addProperty("refreshToken", "refresh")
//addProperty("tokenExpiryTime", 1569523077)
}).also {
it.mode = LOGIN_MODE_LIBRUS_EMAIL
}
fun go() {
app.startService(Intent(app, ApiService::class.java))
val data = DataLibrus(app, profile, loginStore).apply {
callback = object : ProgressCallback {
override fun onProgress(progressStep: Int) {

View File

@ -19,10 +19,11 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces
init {
for (loginMethodId in loginMethodIds) {
var requiredLoginMethod = loginMethodId
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
loginMethodList.add(requiredLoginMethod)
if (requiredLoginMethod != null)
loginMethodList.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
}

View File

@ -9,14 +9,9 @@ import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.JsonCallbackHandler
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.api.AppError.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import java.net.HttpURLConnection.*
class LoginLibrusApi {
@ -121,7 +116,7 @@ class LoginLibrusApi {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
json.getString("error")?.let { error ->
if (response?.code() != 200) json.getString("error")?.let { error ->
when (error) {
"librus_captcha_needed" -> ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED
"connection_problems" -> ERROR_LOGIN_LIBRUS_API_CONNECTION_PROBLEMS
@ -140,7 +135,7 @@ class LoginLibrusApi {
try {
data.apiAccessToken = json.getString("access_token")
data.apiRefreshToken = json.getString("refresh_token")
data.apiTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400)
data.apiTokenExpiryTime = response.getUnixDate() + json.getInt("expires_in", 86400)
onSuccess()
} catch (e: NullPointerException) {
data.error(TAG, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json)

View File

@ -13,6 +13,7 @@ import okhttp3.internal.http.HttpDate
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.getUnixDate
class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
companion object {
@ -73,7 +74,7 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
return
}
data.messagesSessionId = sessionId
data.messagesSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */
data.messagesSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
onSuccess()
}

View File

@ -121,7 +121,52 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
private var refreshTokenFailed = false
private fun accessToken(code: String?, refreshToken: String?) {
data.callback.onActionStarted(R.string.sync_action_getting_token)
val onSuccess = { json: JsonObject, response: Response? ->
data.portalAccessToken = json.getString("access_token")
data.portalRefreshToken = json.getString("refresh_token")
data.portalTokenExpiryTime = response.getUnixDate() + json.getInt("expires_in", 86400)
onSuccess()
}
val callback = object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response?) {
if (json == null) {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
val error = if (response?.code() == 200) null else
json.getString("hint")
error?.let { code ->
when (code) {
"Authorization code has expired" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED
"Authorization code has been revoked" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED
"Check the `client_id` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CLIENT_ID
"Check the `code` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE
"Check the `refresh_token` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH
"Check the `redirect_uri` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT
else -> when (json.getString("error")) {
"unsupported_grant_type" -> ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT
"invalid_client" -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID
else -> ERROR_LOGIN_LIBRUS_PORTAL_OTHER
}
}.let { errorCode ->
data.error(TAG, errorCode, apiResponse = json, response = response)
return
}
}
try {
onSuccess(json, response)
} catch (e: NullPointerException) {
data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json)
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable)
}
}
val params = ArrayList<Pair<String, Any>>()
params.add(Pair("client_id", LIBRUS_CLIENT_ID))
if (code != null) {
@ -132,46 +177,14 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
params.add(Pair("grant_type", "refresh_token"))
params.add(Pair("refresh_token", refreshToken))
}
Request.builder()
.url(LIBRUS_TOKEN_URL)
.userAgent(LIBRUS_USER_AGENT)
.addParams(params)
.allowErrorCode(HTTP_UNAUTHORIZED)
.post()
.callback(object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response) {
if (json == null) {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
json.getString("error")?.let { error ->
val hint = json.getString("hint", "")
//val message = json.getString("message", "")
if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) {
c(TAG, "refreshing the token failed. Trying to log in again.")
refreshTokenFailed = true
authorize(LIBRUS_AUTHORIZE_URL)
return
}
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR, response, apiResponse = json)
return
}
try {
data.portalAccessToken = json.getString("access_token")
data.portalRefreshToken = json.getString("refresh_token")
data.portalTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400)
onSuccess()
} catch (e: NullPointerException) {
data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json)
}
}
override fun onFailure(response: Response, throwable: Throwable) {
data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable)
}
})
.allowErrorCode(HTTP_UNAUTHORIZED)
.callback(callback)
.build()
.enqueue()
}

View File

@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import java.lang.Exception
import java.net.HttpURLConnection
@ -75,7 +76,10 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
val error = json.getString("Code") ?: json.getString("Message") ?: response?.parserErrorBody
val error = if (response?.code() == 200) null else
json.getString("Code") ?:
json.getString("Message") ?:
response?.parserErrorBody
error?.let { code ->
when (code) {
"TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED
@ -145,7 +149,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) {
return
}
data.synergiaSessionId = sessionId
data.synergiaSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */
data.synergiaSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
onSuccess()
}
else {

View File

@ -33,16 +33,80 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) {
}
else {
if (!synergiaAccount()) {
data.error(TAG, ERROR_LOGIN_DATA_MISSING)
}
}
}}
/**
* Get an Api token from the Portal account, using Portal API.
* If necessary, refreshes the token.
*/
private fun synergiaAccount(): Boolean {
val accountLogin = data.apiLogin ?: return false
val accessToken = data.portalAccessToken ?: return false
data.callback.onActionStarted(R.string.sync_action_getting_account)
d(TAG, "Requesting " + (LIBRUS_ACCOUNT_URL + accountLogin))
val onSuccess = { json: JsonObject, response: Response? ->
// synergiaAccount is executed when a synergia token needs a refresh
val accountId = json.getInt("id")
val accountToken = json.getString("accessToken")
if (accountId == null || accountToken == null) {
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json)
}
else {
data.apiAccessToken = accountToken
data.apiTokenExpiryTime = response.getUnixDate() + 6 * 60 * 60
// TODO remove this
data.profile?.studentNameLong = json.getString("studentName")
val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray()
data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
onSuccess()
}
}
val callback = object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response?) {
if (json == null) {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
val error = if (response?.code() == 200) null else
json.getString("reason") ?:
json.getString("message") ?:
json.getString("hint") ?:
json.getString("Code")
error?.let { code ->
when (code) {
"requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
else -> ERROR_LIBRUS_PORTAL_OTHER
}.let { errorCode ->
data.error(TAG, errorCode, apiResponse = json, response = response)
return
}
}
if (response?.code() == HTTP_OK) {
try {
onSuccess(json, response)
} catch (e: NullPointerException) {
e.printStackTrace()
data.error(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json)
}
} else {
data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json)
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable)
}
}
Request.builder()
.url(LIBRUS_ACCOUNT_URL + accountLogin)
.userAgent(LIBRUS_USER_AGENT)
@ -53,59 +117,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) {
.allowErrorCode(HTTP_UNAUTHORIZED)
.allowErrorCode(HTTP_BAD_REQUEST)
.allowErrorCode(HTTP_GONE)
.callback(object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response) {
if (json == null) {
data.error(TAG, ERROR_RESPONSE_EMPTY, response)
return
}
if (response.code() == 410) {
val reason = json.get("reason")
if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") {
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED, response, apiResponse = json)
return
}
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410, response, apiResponse = json)
return
}
if (json.get("message") != null) {
val message = json.get("message").asString
if (message == "Account not found") {
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND, response, apiResponse = json)
return
}
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER, response, apiResponse = json)
return
}
if (response.code() == HTTP_OK) {
try {
// synergiaAccount is executed when a synergia token needs a refresh
val accountId = json.getInt("id")
val accountToken = json.getString("accessToken")
if (accountId == null || accountToken == null) {
data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json)
return
}
data.apiAccessToken = accountToken
data.apiTokenExpiryTime = currentTimeUnix() + 6*60*60
data.profile?.studentNameLong = json.getString("studentName")
val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray()
data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
onSuccess()
} catch (e: NullPointerException) {
e.printStackTrace()
data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json)
}
} else {
data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json)
}
}
override fun onFailure(response: Response, throwable: Throwable) {
data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable)
}
})
.callback(callback)
.build()
.enqueue()
return true

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.models
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
class ApiError(val profileId: Int, val tag: String, val errorCode: Int) {
private var throwable: Throwable? = null
private var apiResponse: String? = null
private var request: Request? = null
private var response: Response? = null
fun withThrowable(throwable: Throwable?): ApiError {
this.throwable = throwable
return this
}
fun withApiResponse(apiResponse: String?): ApiError {
this.apiResponse = apiResponse
return this
}
fun withApiResponse(apiResponse: JsonObject?): ApiError {
this.apiResponse = apiResponse?.toString()
return this
}
fun withRequest(request: Request?): ApiError {
this.request = request
return this
}
fun withResponse(response: Response?): ApiError {
this.response = response
this.request = response?.request()
return this
}
}

View File

@ -1,6 +0,0 @@
package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.datamodels.LoginStore
data class ApiLoginResult(val loginStore: LoginStore, val error: AppError?)

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.models
open class ApiTask(open val profileId: Int) {
var taskId: Int = 0
}

View File

@ -7,15 +7,24 @@ import com.google.gson.JsonObject
import im.wangchao.mhttp.Response
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.api.AppError.*
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback
import pl.szczodrzynski.edziennik.datamodels.*
import pl.szczodrzynski.edziennik.models.Date
import java.io.InterruptedIOException
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) {
var fakeLogin = false
lateinit var callback: ProgressCallback
/**
* A callback passed to all [Endpoint]s and [LoginMethod]s
*/
lateinit var callback: EndpointCallback
/**
* A list of [LoginMethod]s *already fulfilled* during this sync.
@ -152,9 +161,25 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
}
fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
callback.onError(null, AppError(tag, 999, errorCode, response, throwable, apiResponse))
var code = when (throwable) {
is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET
is SocketTimeoutException -> CODE_TIMEOUT
else -> when (response?.code()) {
400, 401, 424, 500, 503, 404 -> CODE_MAINTENANCE
else -> errorCode
}
}
callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withThrowable(throwable).withApiResponse(apiResponse))
}
fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) {
callback.onError(null, AppError(tag, 999, errorCode, response, null, apiResponse))
var code = when (null) {
is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET
is SocketTimeoutException -> CODE_TIMEOUT
else -> when (response?.code()) {
400, 401, 424, 500, 503, 404 -> CODE_MAINTENANCE
else -> errorCode
}
}
callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withApiResponse(apiResponse))
}
}

View File

@ -7,21 +7,18 @@ import pl.szczodrzynski.edziennik.datamodels.Profile
* A Endpoint descriptor class.
*
* The API runs appropriate endpoints in order to fulfill its
* [Feature] list.
* feature list.
* An endpoint may have its [LoginMethod] dependencies which will be
* satisfied by the API before the [endpointClass]'s constructor is invoked.
*
* @param loginType type of the e-register this endpoint handles
* @param endpointId a unique ID of this endpoint
* @param featureIds a [List] of [Feature]s (their IDs) this endpoint can download
* May be null if no strict feature set is associated with this method.
* @param endpointClass a [Class] which constructor will be invoked when a data download is needed
* @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore].
* @param featureId a feature ID
* @param endpointIds a [List] of [Endpoint]s that satisfy this feature ID
* @param requiredLoginMethod a required login method, which will have to be executed before this endpoint.
*/
class Endpoint(
val loginType: Int,
val endpointId: Int,
val featureIds: List<Int>?,
val endpointClass: Class<*>,
val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int
val featureId: Int,
val endpointIds: List<Int>,
val requiredLoginMethod: Int
)

View File

@ -1,11 +0,0 @@
package pl.szczodrzynski.edziennik.api.v2.models
data class Feature(val featureId: Int, val loginOptions: Map<Int, List<Int>>) {
init {
}
}

View File

@ -1,13 +0,0 @@
package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.api.v2.*
val Features = listOf(
Feature(FEATURE_TIMETABLE, mapOf(
LOGIN_TYPE_LIBRUS to listOf(
LOGIN_MODE_LIBRUS_EMAIL,
LOGIN_MODE_LIBRUS_SYNERGIA,
LOGIN_MODE_LIBRUS_JST
)
))
)

View File

@ -1,6 +0,0 @@
package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.api.AppError
import pl.szczodrzynski.edziennik.datamodels.Profile
data class FirstLoginResult(val profileList: ArrayList<Profile>, val error: AppError?)

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED
import pl.szczodrzynski.edziennik.datamodels.LoginStore
import pl.szczodrzynski.edziennik.datamodels.Profile
@ -16,15 +17,30 @@ import pl.szczodrzynski.edziennik.datamodels.Profile
*
* @param loginType type of the e-register this login method handles
* @param loginMethodId a unique ID of this login method
* @param featureIds a [List] of [Feature]s (their IDs) this login method can provide access to
* May be null if no strict feature set is associated with this method.
* @param loginMethodClass a [Class] which constructor will be invoked when a log in is needed
* @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore].
* @param requiredLoginMethod a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore].
*/
class LoginMethod(
val loginType: Int,
val loginMethodId: Int,
val featureIds: List<Int>?,
val loginMethodClass: Class<*>,
val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int
)
private var mIsPossible: ((profile: Profile?, loginStore: LoginStore) -> Boolean)? = null,
private var mRequiredLoginMethod: ((profile: Profile?, loginStore: LoginStore) -> Int)? = null
) {
fun withIsPossible(isPossible: (profile: Profile?, loginStore: LoginStore) -> Boolean): LoginMethod {
this.mIsPossible = isPossible
return this
}
fun withRequiredLoginMethod(requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int): LoginMethod {
this.mRequiredLoginMethod = requiredLoginMethod
return this
}
fun isPossible(profile: Profile?, loginStore: LoginStore): Boolean {
return mIsPossible?.invoke(profile, loginStore) ?: false
}
fun requiredLoginMethod(profile: Profile?, loginStore: LoginStore): Int {
return mRequiredLoginMethod?.invoke(profile, loginStore) ?: LOGIN_METHOD_NOT_NEEDED
}
}

View File

@ -25,7 +25,10 @@ public interface ProfileDao {
LiveData<ProfileFull> getById(int profileId);
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
ProfileFull getByIdNow(int 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();
@ -45,6 +48,9 @@ public interface ProfileDao {
@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();

View File

@ -425,7 +425,7 @@ public class EventManualDialog {
return;
this.app = _app;
AsyncTask.execute(() -> {
this.profile = app.db.profileDao().getByIdNow(profileId);
this.profile = app.db.profileDao().getFullByIdNow(profileId);
if (profile != null) {
((Activity) context).runOnUiThread(() -> {
actualShow(editingEvent, defaultDate, defaultTime, dialogType);

View File

@ -159,7 +159,7 @@ public class WidgetLuckyNumber extends AppWidgetProvider {
}
}
Profile profile = app.db.profileDao().getByIdNow(widgetConfig.profileId);
Profile profile = app.db.profileDao().getFullByIdNow(widgetConfig.profileId);
IIcon icon = CommunityMaterial.Icon.cmd_emoticon_dead_outline;
boolean noNumberText = false;
if (profile == null) {