mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2024-11-24 19:04:38 -06:00
[APIv2/Szkolny] Add Szkolny API and add getting shared events.
This commit is contained in:
parent
d6f9b81de6
commit
40ba9e8434
@ -170,6 +170,9 @@ dependencies {
|
|||||||
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
||||||
|
|
||||||
implementation 'com.github.kuba2k2:Tachyon:551943a6b5'
|
implementation 'com.github.kuba2k2:Tachyon:551943a6b5'
|
||||||
|
|
||||||
|
implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}"
|
||||||
|
implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -222,7 +222,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
|
|||||||
byte[] signatureBytes = signature.toByteArray();
|
byte[] signatureBytes = signature.toByteArray();
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA");
|
MessageDigest md = MessageDigest.getInstance("SHA");
|
||||||
md.update(signatureBytes);
|
md.update(signatureBytes);
|
||||||
this.signature = Base64.encodeToString(md.digest(), Base64.DEFAULT);
|
this.signature = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||||
//Log.d(TAG, "Signature is "+this.signature);
|
//Log.d(TAG, "Signature is "+this.signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import android.text.*
|
|||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.text.style.StrikethroughSpan
|
import android.text.style.StrikethroughSpan
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
|
import android.util.Base64.NO_WRAP
|
||||||
|
import android.util.Base64.encodeToString
|
||||||
import android.util.LongSparseArray
|
import android.util.LongSparseArray
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
@ -34,15 +36,20 @@ import im.wangchao.mhttp.Response
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okio.Buffer
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
|
||||||
import pl.szczodrzynski.navlib.getColorFromRes
|
import pl.szczodrzynski.navlib.getColorFromRes
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.CRC32
|
import java.util.zip.CRC32
|
||||||
|
import javax.crypto.Mac
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
|
||||||
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
|
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
|
||||||
@ -358,6 +365,28 @@ fun String.crc32(): Long {
|
|||||||
return crc.value
|
return crc.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.hmacSHA1(password: String): String {
|
||||||
|
val key = SecretKeySpec(password.toByteArray(), "HmacSHA1")
|
||||||
|
|
||||||
|
val mac = Mac.getInstance("HmacSHA1").apply {
|
||||||
|
init(key)
|
||||||
|
update(this@hmacSHA1.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeToString(mac.doFinal(), NO_WRAP)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.md5(): String {
|
||||||
|
val md = MessageDigest.getInstance("MD5")
|
||||||
|
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RequestBody.bodyToString(): String {
|
||||||
|
val buffer = Buffer()
|
||||||
|
writeTo(buffer)
|
||||||
|
return buffer.readUtf8()
|
||||||
|
}
|
||||||
|
|
||||||
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
||||||
|
|
||||||
fun CharSequence?.asColoredSpannable(colorInt: Int): Spannable {
|
fun CharSequence?.asColoredSpannable(colorInt: Int): Spannable {
|
||||||
|
@ -15,12 +15,10 @@ import pl.szczodrzynski.edziennik.App
|
|||||||
import pl.szczodrzynski.edziennik.api.v2.events.*
|
import pl.szczodrzynski.edziennik.api.v2.events.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest
|
import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest
|
import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
import pl.szczodrzynski.edziennik.api.v2.events.task.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.task.IApiTask
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@ -41,8 +39,8 @@ class ApiService : Service() {
|
|||||||
private val app by lazy { applicationContext as App }
|
private val app by lazy { applicationContext as App }
|
||||||
|
|
||||||
private val finishingTaskQueue = mutableListOf(
|
private val finishingTaskQueue = mutableListOf(
|
||||||
NotifyTask(),
|
ServerSyncTask(),
|
||||||
ErrorReportTask()
|
NotifyTask()
|
||||||
)
|
)
|
||||||
private val taskQueue = mutableListOf<IApiTask>()
|
private val taskQueue = mutableListOf<IApiTask>()
|
||||||
private val errorList = mutableListOf<ApiError>()
|
private val errorList = mutableListOf<ApiError>()
|
||||||
@ -63,6 +61,8 @@ class ApiService : Service() {
|
|||||||
private var lastEventTime = System.currentTimeMillis()
|
private var lastEventTime = System.currentTimeMillis()
|
||||||
private var taskCancelTries = 0
|
private var taskCancelTries = 0
|
||||||
|
|
||||||
|
private val syncingProfiles = mutableListOf<ProfileFull>()
|
||||||
|
|
||||||
/* ______ _ _ _ _ _____ _ _ _ _
|
/* ______ _ _ _ _ _____ _ _ _ _
|
||||||
| ____| | | (_) (_) | / ____| | | | | | |
|
| ____| | | (_) (_) | / ____| | | | | | |
|
||||||
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
|
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
|
||||||
@ -156,11 +156,14 @@ class ApiService : Service() {
|
|||||||
// post an event
|
// post an event
|
||||||
EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile))
|
EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile))
|
||||||
|
|
||||||
|
task.profile?.let { syncingProfiles.add(it) }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
when (task) {
|
when (task) {
|
||||||
is EdziennikTask -> task.run(app, taskCallback)
|
is EdziennikTask -> task.run(app, taskCallback)
|
||||||
is NotifyTask -> task.run(app, taskCallback)
|
is NotifyTask -> task.run(app, taskCallback)
|
||||||
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
||||||
|
is ServerSyncTask -> task.run(app, syncingProfiles, taskCallback)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
||||||
|
@ -39,7 +39,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
|
taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
|
||||||
} else {
|
} else {
|
||||||
// get the requested profile and login store
|
// get the requested profile and login store
|
||||||
val profile = app.db.profileDao().getByIdNow(profileId)
|
val profile = app.db.profileDao().getFullByIdNow(profileId)
|
||||||
this.profile = profile
|
this.profile = profile
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
return
|
return
|
||||||
|
@ -11,11 +11,11 @@ import android.os.Build.VERSION_CODES.O
|
|||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.ApiService
|
import pl.szczodrzynski.edziennik.api.v2.ApiService
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||||
|
|
||||||
abstract class IApiTask(open val profileId: Int) {
|
abstract class IApiTask(open val profileId: Int) {
|
||||||
var taskId: Int = 0
|
var taskId: Int = 0
|
||||||
var profile: Profile? = null
|
var profile: ProfileFull? = null
|
||||||
var taskName: String? = null
|
var taskName: String? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,4 +39,4 @@ abstract class IApiTask(open val profileId: Int) {
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "IApiTask(profileId=$profileId, taskId=$taskId, profile=$profile, taskName=$taskName)"
|
return "IApiTask(profileId=$profileId, taskId=$taskId, profile=$profile, taskName=$taskName)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ package pl.szczodrzynski.edziennik.api.v2.events.task
|
|||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.SzkolnyApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||||
|
|
||||||
class ServerSyncTask : IApiTask(-1) {
|
class ServerSyncTask : IApiTask(-1) {
|
||||||
override fun prepare(app: App) {
|
override fun prepare(app: App) {
|
||||||
@ -17,9 +20,25 @@ class ServerSyncTask : IApiTask(-1) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run(app: App, taskCallback: EdziennikCallback) {
|
fun run(app: App, profiles: List<ProfileFull>, taskCallback: EdziennikCallback) {
|
||||||
|
val api = SzkolnyApi(app, profiles)
|
||||||
|
|
||||||
|
val events = api.getEvents()
|
||||||
|
|
||||||
|
if (events.isNotEmpty()) {
|
||||||
|
app.db.eventDao().addAll(events)
|
||||||
|
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||||
|
Metadata(
|
||||||
|
event.profileId,
|
||||||
|
Metadata.TYPE_EVENT,
|
||||||
|
event.id,
|
||||||
|
event.seen,
|
||||||
|
event.notified,
|
||||||
|
event.addedDate
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
taskCallback.onCompleted()
|
taskCallback.onCompleted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.BuildConfig
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.adapter.DateAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.adapter.TimeAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.interceptor.SignatureInterceptor
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.ServerSyncRequest
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import retrofit2.create
|
||||||
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
|
|
||||||
|
class SzkolnyApi(val app: App, val profiles: List<ProfileFull>) {
|
||||||
|
|
||||||
|
private var api: SzkolnyService
|
||||||
|
|
||||||
|
init {
|
||||||
|
val okHttpClient: OkHttpClient = app.http.newBuilder()
|
||||||
|
.followRedirects(true)
|
||||||
|
.callTimeout(30, SECONDS)
|
||||||
|
.addInterceptor(SignatureInterceptor(app))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val gsonConverterFactory = GsonConverterFactory.create(
|
||||||
|
GsonBuilder()
|
||||||
|
.setLenient()
|
||||||
|
.registerTypeAdapter(Date::class.java, DateAdapter())
|
||||||
|
.registerTypeAdapter(Time::class.java, TimeAdapter())
|
||||||
|
.create())
|
||||||
|
|
||||||
|
val retrofit: Retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl("https://api.szkolny.eu/")
|
||||||
|
.addConverterFactory(gsonConverterFactory)
|
||||||
|
.client(okHttpClient)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
api = retrofit.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getEvents(): List<EventFull> {
|
||||||
|
val teams = app.db.teamDao().allNow
|
||||||
|
|
||||||
|
val response = api.serverSync(ServerSyncRequest(
|
||||||
|
deviceId = app.deviceId,
|
||||||
|
device = ServerSyncRequest.Device(
|
||||||
|
osType = "Android",
|
||||||
|
osVersion = Build.VERSION.RELEASE,
|
||||||
|
hardware = "${Build.MANUFACTURER} ${Build.MODEL}",
|
||||||
|
pushToken = app.config.sync.tokenApp,
|
||||||
|
appVersion = BuildConfig.VERSION_NAME,
|
||||||
|
appType = BuildConfig.BUILD_TYPE,
|
||||||
|
appVersionCode = BuildConfig.VERSION_CODE,
|
||||||
|
syncInterval = app.config.sync.interval
|
||||||
|
),
|
||||||
|
userCodes = profiles.map { it.usernameId },
|
||||||
|
users = profiles.map { profile ->
|
||||||
|
ServerSyncRequest.User(
|
||||||
|
profile.usernameId,
|
||||||
|
profile.studentNameLong ?: "",
|
||||||
|
profile.studentNameShort ?: "",
|
||||||
|
profile.loginStoreType,
|
||||||
|
teams.filter { it.profileId == profile.id }.map { it.code }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)).execute().body()
|
||||||
|
|
||||||
|
val events = mutableListOf<EventFull>()
|
||||||
|
|
||||||
|
response?.data?.events?.forEach { event ->
|
||||||
|
teams.filter { it.code == event.teamCode }.forEach { team ->
|
||||||
|
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||||
|
|
||||||
|
events.add(event.apply {
|
||||||
|
profileId = team.profileId
|
||||||
|
teamId = team.id
|
||||||
|
addedManually = true
|
||||||
|
seen = profile?.empty ?: false
|
||||||
|
notified = profile?.empty ?: false
|
||||||
|
|
||||||
|
if (profile?.usernameId == event.sharedBy) sharedBy = "self"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.ServerSyncRequest
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.response.ApiResponse
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.szkolny.response.ServerSyncResponse
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
interface SzkolnyService {
|
||||||
|
|
||||||
|
@POST("appSync")
|
||||||
|
fun serverSync(@Body request: ServerSyncRequest): Call<ApiResponse<ServerSyncResponse>>
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.adapter
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonToken
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
|
class DateAdapter : TypeAdapter<Date>() {
|
||||||
|
override fun write(writer: JsonWriter?, value: Date?) {}
|
||||||
|
|
||||||
|
override fun read(reader: JsonReader?): Date? {
|
||||||
|
if (reader?.peek() == JsonToken.NULL) {
|
||||||
|
reader.nextNull()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return reader?.nextInt()?.let { Date.fromValue(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.adapter
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonToken
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
|
class TimeAdapter : TypeAdapter<Time>() {
|
||||||
|
override fun write(writer: JsonWriter?, value: Time?) {}
|
||||||
|
|
||||||
|
override fun read(reader: JsonReader?): Time? {
|
||||||
|
if (reader?.peek() == JsonToken.NULL) {
|
||||||
|
reader.nextNull()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return reader?.nextInt()?.let { Time.fromValue(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.interceptor
|
||||||
|
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
|
||||||
|
class SignatureInterceptor(val app: App) : Interceptor {
|
||||||
|
companion object {
|
||||||
|
private const val API_KEY = "szkolny_android_42a66f0842fc7da4e37c66732acf705a"
|
||||||
|
private const val API_PASSWORD = "HodrJ+6OAl9zqlK1IlYBUg=="
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
|
||||||
|
val timestamp = currentTimeUnix()
|
||||||
|
val body = request.body()?.bodyToString() ?: ""
|
||||||
|
val url = request.url().toString()
|
||||||
|
|
||||||
|
return chain.proceed(
|
||||||
|
request.newBuilder()
|
||||||
|
.header("X-ApiKey", API_KEY)
|
||||||
|
.header("X-AppVersion", BuildConfig.VERSION_CODE.toString())
|
||||||
|
.header("X-Timestamp", timestamp.toString())
|
||||||
|
.header("X-Signature", sign(timestamp, body, url))
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sign(timestamp: Long, body: String, url: String): String {
|
||||||
|
val content = timestamp.toString().md5() + body.md5() + url.md5()
|
||||||
|
val password = API_PASSWORD + BuildConfig.VERSION_CODE.toString() + app.signature
|
||||||
|
|
||||||
|
return content.hmacSHA1(password)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.request
|
||||||
|
|
||||||
|
data class ServerSyncRequest(
|
||||||
|
|
||||||
|
val deviceId: String,
|
||||||
|
val device: Device? = null,
|
||||||
|
|
||||||
|
val userCodes: List<String>,
|
||||||
|
val users: List<User>? = null
|
||||||
|
) {
|
||||||
|
data class Device(
|
||||||
|
val osType: String,
|
||||||
|
val osVersion: String,
|
||||||
|
val hardware: String,
|
||||||
|
val pushToken: String?,
|
||||||
|
val appVersion: String,
|
||||||
|
val appType: String,
|
||||||
|
val appVersionCode: Int,
|
||||||
|
val syncInterval: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class User(
|
||||||
|
val userCode: String,
|
||||||
|
val studentName: String,
|
||||||
|
val studentNameShort: String,
|
||||||
|
val loginType: Int,
|
||||||
|
val teamCodes: List<String>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.response
|
||||||
|
|
||||||
|
data class ApiResponse<T> (
|
||||||
|
|
||||||
|
val success: Boolean,
|
||||||
|
|
||||||
|
val errors: List<Error>? = null,
|
||||||
|
|
||||||
|
val data: T? = null
|
||||||
|
) {
|
||||||
|
data class Error (val code: String, val reason: String)
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.szkolny.response
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||||
|
|
||||||
|
data class ServerSyncResponse(val events: List<EventFull>)
|
@ -10,6 +10,7 @@ public class EventFull extends Event {
|
|||||||
public String subjectShortName = "";
|
public String subjectShortName = "";
|
||||||
|
|
||||||
public String teamName = "";
|
public String teamName = "";
|
||||||
|
public String teamCode = null;
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
public boolean seen;
|
public boolean seen;
|
||||||
|
@ -223,13 +223,13 @@ class ProfileFull : Profile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun loginStoreType(): String {
|
fun loginStoreType(): String {
|
||||||
when (loginStoreType) {
|
return when (loginStoreType) {
|
||||||
LOGIN_TYPE_MOBIDZIENNIK -> return "LOGIN_TYPE_MOBIDZIENNIK"
|
LOGIN_TYPE_MOBIDZIENNIK -> "LOGIN_TYPE_MOBIDZIENNIK"
|
||||||
LOGIN_TYPE_LIBRUS -> return "LOGIN_TYPE_LIBRUS"
|
LOGIN_TYPE_LIBRUS -> "LOGIN_TYPE_LIBRUS"
|
||||||
LOGIN_TYPE_IUCZNIOWIE -> return "LOGIN_TYPE_IDZIENNIK"
|
LOGIN_TYPE_IUCZNIOWIE -> "LOGIN_TYPE_IDZIENNIK"
|
||||||
LOGIN_TYPE_VULCAN -> return "LOGIN_TYPE_VULCAN"
|
LOGIN_TYPE_VULCAN -> "LOGIN_TYPE_VULCAN"
|
||||||
LOGIN_TYPE_DEMO -> return "LOGIN_TYPE_DEMO"
|
LOGIN_TYPE_DEMO -> "LOGIN_TYPE_DEMO"
|
||||||
else -> return "LOGIN_TYPE_UNKNOWN"
|
else -> "LOGIN_TYPE_UNKNOWN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,9 @@ public abstract class TeamDao {
|
|||||||
@Query("SELECT * FROM teams WHERE profileId = :profileId ORDER BY teamType, teamName ASC")
|
@Query("SELECT * FROM teams WHERE profileId = :profileId ORDER BY teamType, teamName ASC")
|
||||||
public abstract List<Team> getAllNow(int profileId);
|
public abstract List<Team> getAllNow(int profileId);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM teams ORDER BY teamType, teamName ASC")
|
||||||
|
public abstract List<Team> getAllNow();
|
||||||
|
|
||||||
@Query("SELECT * FROM teams WHERE profileId = :profileId AND teamType = 1")
|
@Query("SELECT * FROM teams WHERE profileId = :profileId AND teamType = 1")
|
||||||
public abstract LiveData<Team> getClass(int profileId);
|
public abstract LiveData<Team> getClass(int profileId);
|
||||||
|
|
||||||
|
@ -103,6 +103,13 @@ public class Time implements Comparable<Time> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Time fromValue(int value) {
|
||||||
|
int hours = value / 10000;
|
||||||
|
int minutes = (value - hours * 10000) / 100;
|
||||||
|
int seconds = (value - hours * 10000 - minutes * 100);
|
||||||
|
return new Time(hours, minutes, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
public long getInMillis() {
|
public long getInMillis() {
|
||||||
Calendar c = Calendar.getInstance();
|
Calendar c = Calendar.getInstance();
|
||||||
c.set(2000, 0, 1, hour, minute, second);
|
c.set(2000, 0, 1, hour, minute, second);
|
||||||
|
@ -47,7 +47,9 @@ buildscript {
|
|||||||
|
|
||||||
navlib : "8b31921697",
|
navlib : "8b31921697",
|
||||||
|
|
||||||
gifdrawable : "1.2.15"
|
gifdrawable : "1.2.15",
|
||||||
|
|
||||||
|
retrofit : '2.6.2'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user