mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-13 14:10:46 +02:00
Compare commits
25 Commits
code-refor
...
v3.9.4-dev
Author | SHA1 | Date | |
---|---|---|---|
810976d976 | |||
eb0540b5cb | |||
124437fd73 | |||
69b512e3d1 | |||
29d74e14bd | |||
f42ec8435a | |||
1052b824db | |||
0742a6a74c | |||
d4e9e1730f | |||
4eeaa54a47 | |||
5fa7409317 | |||
0bcd190714 | |||
563f08b0ab | |||
1b75424604 | |||
01ac26e67b | |||
434ddd1342 | |||
3925496595 | |||
5711c02170 | |||
ca1c691bf0 | |||
39c8a743bb | |||
14cd548dff | |||
b72324805f | |||
a049effa61 | |||
23d55ec571 | |||
385fe21d16 |
@ -159,11 +159,17 @@ dependencies {
|
||||
//implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT'
|
||||
//implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1'
|
||||
|
||||
implementation "org.redundent:kotlin-xml-builder:1.5.3"
|
||||
//implementation "org.redundent:kotlin-xml-builder:1.5.3"
|
||||
|
||||
implementation "io.github.wulkanowy:signer-android:0.1.1"
|
||||
|
||||
implementation "androidx.work:work-runtime-ktx:${versions.work}"
|
||||
|
||||
implementation 'com.hypertrack:hyperlog:0.0.10'
|
||||
|
||||
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
||||
|
||||
implementation 'com.linkedin.android.tachyon:tachyon:1.0.2'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
9
app/sampledata/check/ic_check.xml
Normal file
9
app/sampledata/check/ic_check.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF4caf50"
|
||||
android:pathData="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/>
|
||||
</vector>
|
@ -15,10 +15,14 @@
|
||||
android:theme="@style/SplashTheme"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:excludeFromRecents="true"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
@ -29,7 +33,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity"
|
||||
android:name=".ui.modules.messages.MessagesComposeActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/messages_compose_title"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
@ -39,7 +43,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name="pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity"
|
||||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
@ -101,22 +105,18 @@
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.modules.settings.SettingsLicenseActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.modules.webpush.WebPushConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme.Dark" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.modules.home.CounterActivity"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
@ -169,7 +169,6 @@
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_notifications_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".widgets.luckynumber.WidgetLuckyNumber"
|
||||
android:label="@string/widget_lucky_number_title">
|
||||
@ -188,7 +187,6 @@
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
@ -196,14 +194,7 @@
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service
|
||||
android:name=".sync.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".sync.FirebaseBroadcastReceiver"
|
||||
android:exported="true"
|
||||
@ -212,6 +203,23 @@
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.SzkolnyReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="pl.szczodrzynski.edziennik.SZKOLNY_MAIN" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".sync.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".widgets.timetable.WidgetTimetableService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
@ -222,14 +230,6 @@
|
||||
|
||||
<service android:name=".Notifier$GetDataRetryService" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.SzkolnyReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="pl.szczodrzynski.edziennik.SZKOLNY_MAIN" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".api.v2.ApiService" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
@ -244,4 +244,4 @@
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
@ -31,57 +31,11 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Wersja 3.1.1, 2019-10-09</h3>
|
||||
<h3>Wersja 4.0, 2019-jeszcze-nie-wiem-kiedy</h3>
|
||||
<ul>
|
||||
<li>Librus: poprawiona synchronizacja kategorii i kolorów ocen.</li>
|
||||
<li>Zmieniony kolor dolnego paska w ciemnym motywie.</li>
|
||||
<li>Zaktualizowany licznik czasu lekcji.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Wersja 3.1, 2019-09-29</h3>
|
||||
<ul>
|
||||
<li>Poprawiony interfejs zadań domowych.</li>
|
||||
<li>Librus: wyświetlanie komentarzy ocen.</li>
|
||||
<li>Librus: wyświetlanie nieobecności nauczycieli w Terminarzu.</li>
|
||||
<li>Librus: usprawniona synchronizacja ocen.</li>
|
||||
<li>Poprawki angielskiego tłumaczenia.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Wersja 3.0.3, 2019-09-26</h3>
|
||||
<ul>
|
||||
<li>Librus: poprawka kilku błędów synchronizacji.</li>
|
||||
<li>Vulcan: prawidłowe oznaczanie wiadomości jako przeczytana.</li>
|
||||
<li>Vulcan: poprawiona synchronizacja wiadomości i frekwencji.</li>
|
||||
<li>Vulcan: poprawka błędów logowania.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Wersja 3.0.2, 2019-09-24</h3>
|
||||
<ul>
|
||||
<li>Librus: pobieranie Bieżących ocen opisowych.</li>
|
||||
<li>Poprawki UI: kolor ikon paska statusu w jasnym motywie.</li>
|
||||
<li>Poprawka braku skanera QR do przekazywania powiadomień.</li>
|
||||
<li>Poprawka wyboru koloru i daty własnego wydarzenia, które crashowały aplikację.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Wersja 3.0.1, 2019-09-19</h3>
|
||||
<ul>
|
||||
<li>Librus: Poprawa błędu synchronizacji.</li>
|
||||
<li>Poprawki UI związane z paskiem nawigacji.</li>
|
||||
<li>Mobidziennik: Pobieranie ocen w niektórych przedmiotach.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Wersja 3.0, 2019-09-13</h3>
|
||||
<ul>
|
||||
<li><b>Nowy wygląd i sposób nawigacji</b> w całej aplikacji.</li>
|
||||
<li>Menu nawigacji można teraz otworzyć przyciskiem na <b>dolnym pasku</b>. Pociągnięcie w górę tego paska wyświetla <b>menu kontekstowe</b> dotyczące danego widoku.</li>
|
||||
<li>Założyliśmy serwer Discord! <a href="https://discord.gg/n9e8pWr">https://discord.gg/n9e8pWr</a></li>
|
||||
<br>
|
||||
<li>Librus: poprawka powielonych ogłoszeń szkolnych.</li>
|
||||
<li>Naprawiłem błąd nieskończonej synchronizacji w Vulcanie.</li>
|
||||
<li>Naprawiłem crash launchera przy dodaniu widgetu.</li>
|
||||
<li>Naprawiłem częste crashe związane z widokiem kalendarza.</li>
|
||||
<li>Nowe, ładniejsze (choć trochę) motywy kolorów.</li>
|
||||
<li>Dużo drobnych poprawek UI i działania aplikacji.</li>
|
||||
<li>UWAGA. To jest wersja in-development. Wiele funkcji może nie działać prawidłowo (lub wcale), co oznacza tylko że nie zostały jeszcze przeniesione
|
||||
z wersji 3.x. Proszę o cierpliwość oraz <b>nie udostępnianie</b> tej wersji <u>nikomu</u>.</li>
|
||||
<li>Bardzo dużo zmian</li>
|
||||
</ul>
|
||||
|
||||
<!--<i>
|
||||
|
@ -36,6 +36,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.hypertrack.hyperlog.HyperLog;
|
||||
import com.mikepenz.iconics.Iconics;
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
@ -81,6 +82,7 @@ import pl.szczodrzynski.edziennik.network.NetworkUtils;
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity;
|
||||
import pl.szczodrzynski.edziennik.utils.DebugLogFormat;
|
||||
import pl.szczodrzynski.edziennik.utils.PermissionChecker;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
@ -294,6 +296,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
|
||||
}
|
||||
|
||||
if (App.devMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this);
|
||||
HyperLog.setLogLevel(Log.VERBOSE);
|
||||
HyperLog.setLogFormat(new DebugLogFormat(this));
|
||||
|
||||
ChuckerCollector chuckerCollector = new ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR);
|
||||
ChuckerInterceptor chuckerInterceptor = new ChuckerInterceptor(this, chuckerCollector);
|
||||
httpBuilder.addInterceptor(chuckerInterceptor);
|
||||
|
@ -6,8 +6,13 @@ import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.util.forEach
|
||||
import com.google.gson.JsonArray
|
||||
@ -326,4 +331,81 @@ fun String.crc32(): Long {
|
||||
return crc.value
|
||||
}
|
||||
|
||||
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 {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(ForegroundColorSpan(colorInt), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence?.asStrikethroughSpannable(): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(StrikethroughSpan(), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new read-only list only of those given elements, that are not empty.
|
||||
* Applies for CharSequence and descendants.
|
||||
*/
|
||||
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||
|
||||
fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||
if (this.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (this.size == 1) {
|
||||
return this[0]
|
||||
}
|
||||
|
||||
var spanned = false
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var first = true
|
||||
if (spanned) {
|
||||
val ssb = SpannableStringBuilder()
|
||||
for (piece in this) {
|
||||
if (!first && delimiter != null)
|
||||
ssb.append(delimiter)
|
||||
first = false
|
||||
ssb.append(piece)
|
||||
}
|
||||
return SpannedString(ssb)
|
||||
} else {
|
||||
val sb = StringBuilder()
|
||||
for (piece in this) {
|
||||
if (!first && delimiter != null)
|
||||
sb.append(delimiter)
|
||||
first = false
|
||||
sb.append(piece)
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
|
||||
text = context.getString(resid, *formatArgs)
|
||||
}
|
||||
|
||||
fun JsonObject(vararg properties: Pair<String, Any>): JsonObject {
|
||||
return JsonObject().apply {
|
||||
for (property in properties) {
|
||||
when (property.second) {
|
||||
is JsonElement -> add(property.first, property.second as JsonElement)
|
||||
is String -> addProperty(property.first, property.second as String)
|
||||
is Char -> addProperty(property.first, property.second as Char)
|
||||
is Number -> addProperty(property.first, property.second as Number)
|
||||
is Boolean -> addProperty(property.first, property.second as Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
@ -2,10 +2,15 @@ package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.*
|
||||
import android.provider.Settings
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
@ -31,7 +36,6 @@ import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App.APP_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.ApiService
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface.*
|
||||
@ -58,12 +62,13 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.dpToPx
|
||||
import pl.szczodrzynski.edziennik.utils.appManagerIntentList
|
||||
import pl.szczodrzynski.edziennik.utils.models.NavTarget
|
||||
import pl.szczodrzynski.navlib.*
|
||||
import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT
|
||||
@ -518,17 +523,14 @@ class MainActivity : AppCompatActivity() {
|
||||
fun syncCurrentFeature() {
|
||||
swipeRefreshLayout.isRefreshing = true
|
||||
Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show()
|
||||
ApiService.start(this)
|
||||
val fragmentParam = when (navTargetId) {
|
||||
DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection
|
||||
else -> 0
|
||||
}
|
||||
EventBus.getDefault().postSticky(
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
listOf(navTargetId to fragmentParam)
|
||||
)
|
||||
)
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
listOf(navTargetId to fragmentParam)
|
||||
).enqueue(this)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncStartedEvent(event: ApiTaskStartedEvent) {
|
||||
@ -582,29 +584,18 @@ class MainActivity : AppCompatActivity() {
|
||||
.setMessage(R.string.app_manager_dialog_text)
|
||||
.setPositiveButton(R.string.ok) { dialog, which ->
|
||||
try {
|
||||
val intent = Intent()
|
||||
intent.component = ComponentName(
|
||||
"com.huawei.systemmanager",
|
||||
"com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity"
|
||||
)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
try {
|
||||
val intent = Intent()
|
||||
intent.component = ComponentName(
|
||||
"com.asus.mobilemanager",
|
||||
"com.asus.mobilemanager.MainActivity"
|
||||
)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
startActivity(Intent(android.provider.Settings.ACTION_SETTINGS))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(this, R.string.app_manager_open_failed, Toast.LENGTH_SHORT).show()
|
||||
for (intent in appManagerIntentList) {
|
||||
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
startActivity(Intent(Settings.ACTION_SETTINGS))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(this, R.string.app_manager_open_failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNeutralButton(R.string.dont_ask_again) { dialog, which ->
|
||||
@ -788,7 +779,7 @@ class MainActivity : AppCompatActivity() {
|
||||
fun loadProfile(id: Int) = loadProfile(id, navTargetId)
|
||||
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
|
||||
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
|
||||
d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)")
|
||||
//d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)")
|
||||
if (app.profile != null && App.profileId == id) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
|
@ -90,6 +90,7 @@ class ApiService : Service() {
|
||||
errorList.add(apiError)
|
||||
apiError.throwable?.printStackTrace()
|
||||
if (apiError.isCritical) {
|
||||
taskRunning?.cancel()
|
||||
notification.setCriticalError().post()
|
||||
taskRunning = null
|
||||
taskIsRunning = false
|
||||
@ -154,10 +155,14 @@ class ApiService : Service() {
|
||||
// post an event
|
||||
EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile))
|
||||
|
||||
when (task) {
|
||||
is EdziennikTask -> task.run(app, taskCallback)
|
||||
is NotifyTask -> task.run(app, taskCallback)
|
||||
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
||||
try {
|
||||
when (task) {
|
||||
is EdziennikTask -> task.run(app, taskCallback)
|
||||
is NotifyTask -> task.run(app, taskCallback)
|
||||
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,11 +232,13 @@ class ApiService : Service() {
|
||||
____) | __/ | \ V /| | (_| __/ | (_) \ V / __/ | | | | | (_| | __/\__ \
|
||||
|_____/ \___|_| \_/ |_|\___\___| \___/ \_/ \___|_| |_| |_|\__,_|\___||__*/
|
||||
override fun onCreate() {
|
||||
d(TAG, "Service created")
|
||||
EventBus.getDefault().register(this)
|
||||
notification.setIdle().setCloseAction()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
d(TAG, "Foreground service onStartCommand")
|
||||
startForeground(EdziennikNotification.NOTIFICATION_ID, notification.notification)
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
@ -14,6 +14,14 @@ val SYSTEM_USER_AGENT = System.getProperty("http.agent") ?: "Dalvik/2.1.0 Androi
|
||||
|
||||
val SERVER_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME} $SYSTEM_USER_AGENT"
|
||||
|
||||
const val FAKE_LIBRUS_API = "http://librus.szkolny.eu/api"
|
||||
const val FAKE_LIBRUS_PORTAL = "http://librus.szkolny.eu"
|
||||
const val FAKE_LIBRUS_AUTHORIZE = "http://librus.szkolny.eu/authorize.php"
|
||||
const val FAKE_LIBRUS_LOGIN = "http://librus.szkolny.eu/login_action.php"
|
||||
const val FAKE_LIBRUS_TOKEN = "http://librus.szkolny.eu/access_token.php"
|
||||
const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login="
|
||||
const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
|
||||
|
||||
val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT 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"
|
||||
|
@ -150,6 +150,7 @@ const val ERROR_IDZIENNIK_API_OTHER = 451
|
||||
|
||||
const val ERROR_TEMPLATE_WEB_OTHER = 801
|
||||
|
||||
const val EXCEPTION_API_TASK = 900
|
||||
const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901
|
||||
const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902
|
||||
const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903
|
||||
|
@ -66,13 +66,13 @@ val librusLoginMethods = listOf(
|
||||
},
|
||||
|
||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
||||
.withIsPossible { _, _ -> true }
|
||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||
.withRequiredLoginMethod { profile, _ ->
|
||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
||||
},
|
||||
|
||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
|
||||
.withIsPossible { _, _ -> true }
|
||||
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||
.withRequiredLoginMethod { profile, _ ->
|
||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.events.task
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
@ -18,7 +19,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
|
||||
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
|
||||
fun sync() = EdziennikTask(-1, SyncRequest())
|
||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds))
|
||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments))
|
||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||
fun messageGet(profileId: Int, messageId: Int) = EdziennikTask(profileId, MessageGetRequest(messageId))
|
||||
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
|
||||
@ -50,7 +51,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
|
||||
private var edziennikInterface: EdziennikInterface? = null
|
||||
|
||||
fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
edziennikInterface = when (loginStore.type) {
|
||||
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
|
||||
@ -66,7 +67,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
when (request) {
|
||||
is SyncProfileRequest -> edziennikInterface?.sync(
|
||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(),
|
||||
viewId = request.viewIds?.get(0)?.first)
|
||||
viewId = request.viewIds?.get(0)?.first,
|
||||
arguments = request.arguments)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.messageId)
|
||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
||||
@ -83,7 +85,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
|
||||
data class FirstLoginRequest(val loginStore: LoginStore)
|
||||
class SyncRequest
|
||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null)
|
||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null)
|
||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||
data class MessageGetRequest(val messageId: Int)
|
||||
class AnnouncementsReadRequest
|
||||
|
@ -6,6 +6,8 @@ package pl.szczodrzynski.edziennik.api.v2.events.task
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.O
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.ApiService
|
||||
@ -25,7 +27,12 @@ abstract class IApiTask(open val profileId: Int) {
|
||||
abstract fun cancel()
|
||||
|
||||
fun enqueue(context: Context) {
|
||||
context.startService(Intent(context, ApiService::class.java))
|
||||
Intent(context, ApiService::class.java).let {
|
||||
if (SDK_INT >= O)
|
||||
context.startForegroundService(it)
|
||||
else
|
||||
context.startService(it)
|
||||
}
|
||||
EventBus.getDefault().postSticky(this)
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData
|
||||
@ -48,7 +49,8 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
|
@ -41,43 +41,43 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
when (endpointId) {
|
||||
ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
IdziennikWebTimetable(data) { onSuccess() }
|
||||
IdziennikWebTimetable(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_GRADES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
IdziennikWebGrades(data) { onSuccess() }
|
||||
IdziennikWebGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades)
|
||||
IdziennikWebProposedGrades(data) { onSuccess() }
|
||||
IdziennikWebProposedGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_EXAMS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_exams)
|
||||
IdziennikWebExams(data) { onSuccess() }
|
||||
IdziennikWebExams(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_NOTICES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notices)
|
||||
IdziennikWebNotices(data) { onSuccess() }
|
||||
IdziennikWebNotices(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_announcements)
|
||||
IdziennikWebAnnouncements(data) { onSuccess() }
|
||||
IdziennikWebAnnouncements(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_ATTENDANCE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
IdziennikWebAttendance(data) { onSuccess() }
|
||||
IdziennikWebAttendance(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
|
||||
IdziennikApiCurrentRegister(data) { onSuccess() }
|
||||
IdziennikApiCurrentRegister(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
IdziennikApiMessagesInbox(data) { onSuccess() }
|
||||
IdziennikApiMessagesInbox(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_API_MESSAGES_SENT -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
|
||||
IdziennikApiMessagesSent(data) { onSuccess() }
|
||||
IdziennikApiMessagesSent(data, onSuccess)
|
||||
}
|
||||
else -> onSuccess()
|
||||
}
|
||||
|
@ -4,8 +4,10 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.interfaces
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
interface EdziennikInterface {
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null)
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||
fun getMessage(messageId: Int)
|
||||
fun markAllAnnouncementsAsRead()
|
||||
fun firstLogin()
|
||||
|
@ -4,8 +4,9 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
|
||||
@ -14,9 +15,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
@ -51,14 +50,31 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login() {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
LibrusLogin(data) {
|
||||
LibrusData(data) {
|
||||
completed()
|
||||
}
|
||||
data()
|
||||
}
|
||||
}
|
||||
|
||||
private fun data() {
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
LibrusData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,15 +118,70 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
return
|
||||
}
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_PORTAL)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.portalTokenExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
ERROR_LIBRUS_API_ACCESS_DENIED,
|
||||
ERROR_LIBRUS_API_TOKEN_EXPIRED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_API)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.apiTokenExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_SYNERGIA)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.synergiaSessionIdExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_MESSAGES)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.messagesSessionIdExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE,
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING,
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED,
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> {
|
||||
login()
|
||||
}
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH,
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED,
|
||||
ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID -> {
|
||||
data.portalRefreshToken = null
|
||||
login()
|
||||
}
|
||||
ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID,
|
||||
ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN,
|
||||
ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID -> {
|
||||
login()
|
||||
}
|
||||
ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> {
|
||||
login()
|
||||
}
|
||||
// TODO PORTAL CAPTCHA
|
||||
ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC -> {
|
||||
loginStore.putLoginData("timetableNotPublic", true)
|
||||
data()
|
||||
}
|
||||
ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE,
|
||||
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
|
||||
data()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ open class LibrusApi(open val data: DataLibrus) {
|
||||
|
||||
fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Api - $LIBRUS_API_URL/$endpoint")
|
||||
d(tag, "Request: Librus/Api - ${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
@ -90,7 +90,7 @@ open class LibrusApi(open val data: DataLibrus) {
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_API_URL/$endpoint")
|
||||
.url("${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint")
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("Authorization", "Bearer ${data.apiAccessToken}")
|
||||
.apply {
|
||||
|
@ -45,92 +45,99 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
*/
|
||||
ENDPOINT_LIBRUS_API_ME -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
||||
LibrusApiMe(data) { onSuccess() }
|
||||
LibrusApiMe(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_SCHOOLS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_school_info)
|
||||
LibrusApiSchools(data) { onSuccess() }
|
||||
LibrusApiSchools(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_CLASSES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_classes)
|
||||
LibrusApiClasses(data) { onSuccess() }
|
||||
LibrusApiClasses(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teams)
|
||||
LibrusApiVirtualClasses(data) { onSuccess() }
|
||||
LibrusApiVirtualClasses(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_UNITS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_units)
|
||||
LibrusApiUnits(data) { onSuccess() }
|
||||
LibrusApiUnits(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_USERS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teachers)
|
||||
LibrusApiUsers(data) { onSuccess() }
|
||||
LibrusApiUsers(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_SUBJECTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_subjects)
|
||||
LibrusApiSubjects(data) { onSuccess() }
|
||||
LibrusApiSubjects(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_CLASSROOMS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_classrooms)
|
||||
LibrusApiClassrooms(data) { onSuccess() }
|
||||
LibrusApiClassrooms(data, onSuccess)
|
||||
}
|
||||
// TODO push config
|
||||
// TODO timetable
|
||||
ENDPOINT_LIBRUS_API_TIMETABLES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
LibrusApiTimetables(data, onSuccess)
|
||||
}
|
||||
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
LibrusApiGrades(data) { onSuccess() }
|
||||
LibrusApiGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GC -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
|
||||
LibrusApiGradeCategories(data, onSuccess)
|
||||
}
|
||||
// TODO grades
|
||||
|
||||
ENDPOINT_LIBRUS_API_EVENT_TYPES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_event_types)
|
||||
LibrusApiEventTypes(data) { onSuccess() }
|
||||
LibrusApiEventTypes(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_EVENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_events)
|
||||
LibrusApiEvents(data) { onSuccess() }
|
||||
LibrusApiEvents(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_HOMEWORK -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_homework)
|
||||
LibrusApiHomework(data) { onSuccess() }
|
||||
LibrusApiHomework(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
|
||||
LibrusApiLuckyNumber(data) { onSuccess() }
|
||||
LibrusApiLuckyNumber(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NOTICE_TYPES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notice_types)
|
||||
LibrusApiNoticeTypes(data) { onSuccess() }
|
||||
LibrusApiNoticeTypes(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NOTICES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notices)
|
||||
LibrusApiNotices(data) { onSuccess() }
|
||||
LibrusApiNotices(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance_types)
|
||||
LibrusApiAttendanceTypes(data) { onSuccess() }
|
||||
LibrusApiAttendanceTypes(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_ATTENDANCES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
LibrusApiAttendances(data) { onSuccess() }
|
||||
LibrusApiAttendances(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_announcements)
|
||||
LibrusApiAnnouncements(data) { onSuccess() }
|
||||
LibrusApiAnnouncements(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_PT_MEETINGS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings)
|
||||
LibrusApiPtMeetings(data) { onSuccess() }
|
||||
LibrusApiPtMeetings(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types)
|
||||
LibrusApiTeacherFreeDayTypes(data) { onSuccess() }
|
||||
LibrusApiTeacherFreeDayTypes(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days)
|
||||
LibrusApiTeacherFreeDays(data) { onSuccess() }
|
||||
LibrusApiTeacherFreeDays(data, onSuccess)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,11 +145,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
*/
|
||||
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_homework)
|
||||
LibrusSynergiaHomework(data) { onSuccess() }
|
||||
LibrusSynergiaHomework(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_SYNERGIA_INFO -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
||||
LibrusSynergiaInfo(data) { onSuccess() }
|
||||
LibrusSynergiaInfo(data, onSuccess)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,11 +157,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
*/
|
||||
ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED) { onSuccess() }
|
||||
LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, onSuccess = onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_MESSAGES_SENT -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
|
||||
LibrusMessagesGetList(data, type = Message.TYPE_SENT) { onSuccess() }
|
||||
LibrusMessagesGetList(data, type = Message.TYPE_SENT, onSuccess = onSuccess)
|
||||
}
|
||||
|
||||
else -> onSuccess()
|
||||
|
@ -12,13 +12,17 @@ import okhttp3.Cookie
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.parser.Parser
|
||||
import org.redundent.kotlin.xml.PrintOptions
|
||||
import org.redundent.kotlin.xml.xml
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.io.StringWriter
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
|
||||
open class LibrusMessages(open val data: DataLibrus) {
|
||||
companion object {
|
||||
@ -80,7 +84,27 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
val requestXml = xml("service") {
|
||||
|
||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = docBuilder.newDocument()
|
||||
val serviceElement = doc.createElement("service")
|
||||
val headerElement = doc.createElement("header")
|
||||
val dataElement = doc.createElement("data")
|
||||
for ((key, value) in parameters.orEmpty()) {
|
||||
val element = doc.createElement(key)
|
||||
element.appendChild(doc.createTextNode(value.toString()))
|
||||
dataElement.appendChild(element)
|
||||
}
|
||||
serviceElement.appendChild(headerElement)
|
||||
serviceElement.appendChild(dataElement)
|
||||
doc.appendChild(serviceElement)
|
||||
val transformer = TransformerFactory.newInstance().newTransformer()
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
|
||||
val stringWriter = StringWriter()
|
||||
transformer.transform(DOMSource(doc), StreamResult(stringWriter))
|
||||
val requestXml = stringWriter.toString()
|
||||
|
||||
/*val requestXml = xml("service") {
|
||||
"header" { }
|
||||
"data" {
|
||||
for ((key, value) in parameters.orEmpty()) {
|
||||
@ -92,7 +116,7 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
}.toString(PrintOptions(
|
||||
singleLineTextElements = true,
|
||||
useSelfClosingTags = true
|
||||
))
|
||||
))*/
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_MESSAGES_URL/$endpoint")
|
||||
|
@ -24,7 +24,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
||||
|
||||
fun portalGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject, response: Response?) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Portal - $LIBRUS_PORTAL_URL$endpoint")
|
||||
d(tag, "Request: Librus/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_PORTAL else LIBRUS_PORTAL_URL}$endpoint")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
@ -44,7 +44,10 @@ open class LibrusPortal(open val data: DataLibrus) {
|
||||
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
|
||||
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
|
||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||
else -> when (json.getString("hint")) {
|
||||
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
|
||||
else -> ERROR_LIBRUS_PORTAL_OTHER
|
||||
}
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(tag, errorCode)
|
||||
.withApiResponse(json)
|
||||
@ -78,7 +81,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url(LIBRUS_PORTAL_URL + endpoint)
|
||||
.url((if (data.fakeLogin) FAKE_LIBRUS_PORTAL else LIBRUS_PORTAL_URL) + endpoint)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("Authorization", "Bearer ${data.portalAccessToken}")
|
||||
.apply {
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-5
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
|
||||
|
||||
import android.graphics.Color
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GC
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
|
||||
|
||||
class LibrusApiGradeCategories(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApiGradeCategories"
|
||||
}
|
||||
|
||||
init {
|
||||
apiGet(TAG, "Grades/Categories") { json ->
|
||||
json.getJsonArray("Categories")?.asJsonObjectList()?.forEach { category ->
|
||||
val id = category.getLong("Id") ?: return@forEach
|
||||
val name = category.getString("Name") ?: ""
|
||||
val weight = when (category.getBoolean("CountToTheAverage")) {
|
||||
true -> category.getFloat("Weight") ?: 0f
|
||||
else -> 0f
|
||||
}
|
||||
val color = category.getJsonObject("Color")?.getInt("Id")
|
||||
?.let { data.getColor(it) } ?: Color.BLUE
|
||||
|
||||
val gradeCategoryObject = GradeCategory(
|
||||
profileId,
|
||||
id,
|
||||
weight,
|
||||
color,
|
||||
name
|
||||
)
|
||||
|
||||
data.gradeCategories.put(id, gradeCategoryObject)
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GC, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
|
||||
|
||||
import androidx.core.util.isEmpty
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TIMETABLES
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LibrusApiTimetables(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApiTimetables"
|
||||
}
|
||||
|
||||
init {
|
||||
if (data.classrooms.isEmpty()) {
|
||||
data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id }
|
||||
}
|
||||
|
||||
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) }
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
apiGet(TAG, "Timetables?weekStart=$getDate") { json ->
|
||||
val days = json.getJsonObject("Timetable")
|
||||
|
||||
days?.entrySet()?.forEach { (dateString, dayEl) ->
|
||||
val day = dayEl?.asJsonArray
|
||||
|
||||
val lessonDate = dateString?.let { Date.fromY_m_d(it) } ?: return@forEach
|
||||
|
||||
var lessonsFound = false
|
||||
day?.forEach { lessonRangeEl ->
|
||||
val lessonRange = lessonRangeEl?.asJsonArray?.asJsonObjectList()
|
||||
if (lessonRange?.isNullOrEmpty() == false)
|
||||
lessonsFound = true
|
||||
lessonRange?.forEachIndexed { index, lesson ->
|
||||
parseLesson(lessonDate, lesson)
|
||||
}
|
||||
}
|
||||
|
||||
if (day.isNullOrEmpty() || !lessonsFound) {
|
||||
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
|
||||
type = Lesson.TYPE_NO_LESSONS
|
||||
date = lessonDate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_TIMETABLES, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseLesson(lessonDate: Date, lesson: JsonObject) {
|
||||
val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false
|
||||
val isCancelled = lesson.getBoolean("IsCanceled") ?: false
|
||||
|
||||
val lessonNo = lesson.getInt("LessonNo") ?: return
|
||||
val startTime = lesson.getString("HourFrom")?.let { Time.fromH_m(it) } ?: return
|
||||
val endTime = lesson.getString("HourTo")?.let { Time.fromH_m(it) } ?: return
|
||||
val subjectId = lesson.getJsonObject("Subject")?.getLong("Id")
|
||||
val teacherId = lesson.getJsonObject("Teacher")?.getLong("Id")
|
||||
val classroomId = lesson.getJsonObject("Classroom")?.getLong("Id") ?: -1
|
||||
val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id")
|
||||
val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId
|
||||
|
||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
val lessonObject = Lesson(profileId, id)
|
||||
|
||||
if (isSubstitution && isCancelled) {
|
||||
// shifted lesson - source
|
||||
val newDate = lesson.getString("NewDate")?.let { Date.fromY_m_d(it) } ?: return
|
||||
val newLessonNo = lesson.getInt("NewLessonNo") ?: return
|
||||
val newStartTime = lesson.getString("NewHourFrom")?.let { Time.fromH_m(it) } ?: return
|
||||
val newEndTime = lesson.getString("NewHourTo")?.let { Time.fromH_m(it) } ?: return
|
||||
val newSubjectId = lesson.getJsonObject("NewSubject")?.getLong("Id")
|
||||
val newTeacherId = lesson.getJsonObject("NewTeacher")?.getLong("Id")
|
||||
val newClassroomId = lesson.getJsonObject("NewClassroom")?.getLong("Id") ?: -1
|
||||
val newVirtualClassId = lesson.getJsonObject("NewVirtualClass")?.getLong("Id")
|
||||
val newTeamId = lesson.getJsonObject("NewClass")?.getLong("Id") ?: newVirtualClassId
|
||||
|
||||
lessonObject.let {
|
||||
it.type = Lesson.TYPE_SHIFTED_SOURCE
|
||||
it.oldDate = lessonDate
|
||||
it.oldLessonNumber = lessonNo
|
||||
it.oldStartTime = startTime
|
||||
it.oldEndTime = endTime
|
||||
it.oldSubjectId = subjectId
|
||||
it.oldTeacherId = teacherId
|
||||
it.oldTeamId = teamId
|
||||
it.oldClassroom = data.classrooms[classroomId]?.name
|
||||
|
||||
it.date = newDate
|
||||
it.lessonNumber = newLessonNo
|
||||
it.startTime = newStartTime
|
||||
it.endTime = newEndTime
|
||||
it.subjectId = newSubjectId
|
||||
it.teacherId = newTeacherId
|
||||
it.teamId = newTeamId
|
||||
it.classroom = data.classrooms[newClassroomId]?.name
|
||||
}
|
||||
}
|
||||
else if (isSubstitution) {
|
||||
// lesson change OR shifted lesson - target
|
||||
val oldDate = lesson.getString("OrgDate")?.let { Date.fromY_m_d(it) } ?: return
|
||||
val oldLessonNo = lesson.getInt("OrgLessonNo") ?: return
|
||||
val oldStartTime = lesson.getString("OrgHourFrom")?.let { Time.fromH_m(it) } ?: return
|
||||
val oldEndTime = lesson.getString("OrgHourTo")?.let { Time.fromH_m(it) } ?: return
|
||||
val oldSubjectId = lesson.getJsonObject("OrgSubject")?.getLong("Id")
|
||||
val oldTeacherId = lesson.getJsonObject("OrgTeacher")?.getLong("Id")
|
||||
val oldClassroomId = lesson.getJsonObject("OrgClassroom")?.getLong("Id") ?: -1
|
||||
val oldVirtualClassId = lesson.getJsonObject("OrgVirtualClass")?.getLong("Id")
|
||||
val oldTeamId = lesson.getJsonObject("OrgClass")?.getLong("Id") ?: oldVirtualClassId
|
||||
|
||||
lessonObject.let {
|
||||
it.type = if (lessonDate == oldDate && lessonNo == oldLessonNo) Lesson.TYPE_CHANGE else Lesson.TYPE_SHIFTED_TARGET
|
||||
it.oldDate = oldDate
|
||||
it.oldLessonNumber = oldLessonNo
|
||||
it.oldStartTime = oldStartTime
|
||||
it.oldEndTime = oldEndTime
|
||||
it.oldSubjectId = oldSubjectId
|
||||
it.oldTeacherId = oldTeacherId
|
||||
it.oldTeamId = oldTeamId
|
||||
it.oldClassroom = data.classrooms[oldClassroomId]?.name
|
||||
|
||||
it.date = lessonDate
|
||||
it.lessonNumber = lessonNo
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = data.classrooms[classroomId]?.name
|
||||
}
|
||||
}
|
||||
else if (isCancelled) {
|
||||
lessonObject.let {
|
||||
it.type = Lesson.TYPE_CANCELLED
|
||||
it.oldDate = lessonDate
|
||||
it.oldLessonNumber = lessonNo
|
||||
it.oldStartTime = startTime
|
||||
it.oldEndTime = endTime
|
||||
it.oldSubjectId = subjectId
|
||||
it.oldTeacherId = teacherId
|
||||
it.oldTeamId = teamId
|
||||
it.oldClassroom = data.classrooms[classroomId]?.name
|
||||
}
|
||||
}
|
||||
else {
|
||||
lessonObject.let {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
it.date = lessonDate
|
||||
it.lessonNumber = lessonNo
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = data.classrooms[classroomId]?.name
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonObject.id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList += lessonObject
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT
|
||||
import pl.szczodrzynski.edziennik.api.v2.FAKE_LIBRUS_ACCOUNTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
@ -29,7 +30,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) {
|
||||
// email login: use Portal for account list
|
||||
LibrusLoginPortal(data) {
|
||||
portal.portalGet(TAG, LIBRUS_ACCOUNTS_URL) { json, response ->
|
||||
portal.portalGet(TAG, if (data.fakeLogin) FAKE_LIBRUS_ACCOUNTS else LIBRUS_ACCOUNTS_URL) { json, response ->
|
||||
val accounts = json.getJsonArray("accounts")
|
||||
|
||||
if (accounts == null || accounts.size() < 1) {
|
||||
|
@ -7,14 +7,15 @@ import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||
import java.util.ArrayList
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
@ -42,7 +43,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
else {
|
||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
||||
authorize(LIBRUS_AUTHORIZE_URL)
|
||||
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
||||
}
|
||||
}}
|
||||
|
||||
@ -86,10 +87,10 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
private fun login(csrfToken: String) {
|
||||
d(TAG, "Request: Librus/Login/Portal - $LIBRUS_LOGIN_URL")
|
||||
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL}")
|
||||
|
||||
Request.builder()
|
||||
.url(LIBRUS_LOGIN_URL)
|
||||
.url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addParameter("email", data.portalEmail)
|
||||
.addParameter("password", data.portalPassword)
|
||||
@ -135,7 +136,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
|
||||
private var refreshTokenFailed = false
|
||||
private fun accessToken(code: String?, refreshToken: String?) {
|
||||
d(TAG, "Request: Librus/Login/Portal - $LIBRUS_TOKEN_URL")
|
||||
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL}")
|
||||
|
||||
val onSuccess = { json: JsonObject, response: Response? ->
|
||||
data.portalAccessToken = json.getString("access_token")
|
||||
@ -204,7 +205,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url(LIBRUS_TOKEN_URL)
|
||||
.url(if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addParams(params)
|
||||
.post()
|
||||
|
@ -43,7 +43,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () ->
|
||||
val accountLogin = data.apiLogin ?: return false
|
||||
data.portalAccessToken ?: return false
|
||||
|
||||
d(TAG, "Request: Librus/SynergiaTokenExtractor - $LIBRUS_ACCOUNT_URL$accountLogin")
|
||||
d(TAG, "Request: Librus/SynergiaTokenExtractor - ${if (data.fakeLogin) FAKE_LIBRUS_ACCOUNT else LIBRUS_ACCOUNT_URL}$accountLogin")
|
||||
|
||||
val onSuccess = { json: JsonObject, response: Response? ->
|
||||
// synergiaAccount is executed when a synergia token needs a refresh
|
||||
@ -67,7 +67,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () ->
|
||||
}
|
||||
}
|
||||
|
||||
portalGet(TAG, LIBRUS_ACCOUNT_URL+accountLogin, onSuccess = onSuccess)
|
||||
portalGet(TAG, (if (data.fakeLogin) FAKE_LIBRUS_ACCOUNT else LIBRUS_ACCOUNT_URL)+accountLogin, onSuccess = onSuccess)
|
||||
return true
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
@ -48,7 +49,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
|
@ -21,10 +21,10 @@ const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000
|
||||
|
||||
val MobidziennikFeatures = listOf(
|
||||
// always synced
|
||||
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf(
|
||||
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf(
|
||||
ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB,
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),*/
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), // TODO divide features into separate view IDs (all with API_MAIN)
|
||||
|
||||
// push config
|
||||
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf(
|
||||
|
@ -42,7 +42,7 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
when (endpointId) {
|
||||
ENDPOINT_MOBIDZIENNIK_API_MAIN -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_data)
|
||||
MobidziennikApi(data) { onSuccess() }
|
||||
MobidziennikApi(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
@ -75,4 +75,4 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
else -> onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,102 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.App.profileId
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
init {
|
||||
for (lessonStr in rows) {
|
||||
val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") }
|
||||
|
||||
val dataStart = Date.getToday()
|
||||
val dataEnd = dataStart.clone().stepForward(0, 0, 7 + (6 - dataStart.weekDay))
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(dataStart.clone(), dataEnd))
|
||||
|
||||
val dataDays = mutableListOf<Int>()
|
||||
while (dataStart <= dataEnd) {
|
||||
dataDays += dataStart.value
|
||||
dataStart.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
for (lesson in lessons) {
|
||||
val date = Date.fromYmd(lesson[2])
|
||||
val startTime = Time.fromYmdHm(lesson[3])
|
||||
val endTime = Time.fromYmdHm(lesson[4])
|
||||
val id = date.combineWith(startTime) / 6L * 10L + (lesson.joinToString("|").hashCode() and 0xFFFF)
|
||||
|
||||
dataDays.remove(date.value)
|
||||
|
||||
val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5] }?.id ?: -1
|
||||
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1
|
||||
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||
val classroom = lesson[11]
|
||||
|
||||
Lesson(data.profileId, id).also {
|
||||
when (lesson[1]) {
|
||||
"plan_lekcji", "lekcja" -> {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
"lekcja_odwolana" -> {
|
||||
it.type = Lesson.TYPE_CANCELLED
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.oldSubjectId = subjectId
|
||||
//it.oldTeacherId = teacherId
|
||||
it.oldTeamId = teamId
|
||||
//it.oldClassroom = classroom
|
||||
}
|
||||
"zastepstwo" -> {
|
||||
it.type = Lesson.TYPE_CHANGE
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
it.id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList += it
|
||||
}
|
||||
}
|
||||
|
||||
for (day in dataDays) {
|
||||
val lessonDate = Date.fromValue(day)
|
||||
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
|
||||
type = Lesson.TYPE_NO_LESSONS
|
||||
date = lessonDate
|
||||
}
|
||||
}
|
||||
|
||||
/*for (lessonStr in rows) {
|
||||
if (lessonStr.isNotEmpty()) {
|
||||
val lesson = lessonStr.split("|")
|
||||
|
||||
@ -76,9 +162,9 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
if (originalLesson == null) {
|
||||
// original lesson doesn't exist, save a new addition
|
||||
// TODO
|
||||
/*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||
*//*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||
app.profile.timetable.addLessonAddition(registerLessonChange);
|
||||
}*/
|
||||
}*//*
|
||||
} else {
|
||||
// original lesson exists, so we need to compare them
|
||||
if (!lessonChange.matches(originalLesson)) {
|
||||
@ -108,6 +194,6 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -60,6 +60,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
val profileId
|
||||
get() = profile?.id ?: -1
|
||||
|
||||
var arguments: JsonObject? = null
|
||||
|
||||
/**
|
||||
* A callback passed to all [Feature]s and [LoginMethod]s
|
||||
*/
|
||||
@ -133,23 +135,20 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
mTeamClass = value
|
||||
}
|
||||
|
||||
var lessonsToRemove: DataRemoveModel? = null
|
||||
var toRemove = mutableListOf<DataRemoveModel>()
|
||||
|
||||
val lessonList = mutableListOf<Lesson>()
|
||||
val lessonChangeList = mutableListOf<LessonChange>()
|
||||
val lessonNewList = mutableListOf<pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson>()
|
||||
|
||||
var gradesToRemove: DataRemoveModel? = null
|
||||
val gradeList = mutableListOf<Grade>()
|
||||
|
||||
var eventsToRemove: DataRemoveModel? = null
|
||||
val eventList = mutableListOf<Event>()
|
||||
|
||||
var noticesToRemove: DataRemoveModel? = null
|
||||
val noticeList = mutableListOf<Notice>()
|
||||
|
||||
var attendancesToRemove: DataRemoveModel? = null
|
||||
val attendanceList = mutableListOf<Attendance>()
|
||||
|
||||
var announcementsToRemove: DataRemoveModel? = null
|
||||
val announcementList = mutableListOf<Announcement>()
|
||||
|
||||
val luckyNumberList = mutableListOf<LuckyNumber>()
|
||||
@ -166,6 +165,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
val db: AppDb by lazy { app.db }
|
||||
|
||||
init {
|
||||
if (App.devMode) {
|
||||
fakeLogin = loginStore.hasLoginData("fakeLogin")
|
||||
}
|
||||
clear()
|
||||
if (profile != null) {
|
||||
endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList()
|
||||
@ -180,6 +182,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
fun clear() {
|
||||
loginMethods.clear()
|
||||
|
||||
toRemove.clear()
|
||||
endpointTimers.clear()
|
||||
teacherList.clear()
|
||||
subjectList.clear()
|
||||
@ -195,6 +198,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
|
||||
lessonList.clear()
|
||||
lessonChangeList.clear()
|
||||
lessonNewList.clear()
|
||||
gradeList.clear()
|
||||
noticeList.clear()
|
||||
attendanceList.clear()
|
||||
@ -248,6 +252,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
app.profile.loginStoreData = loginStore.data
|
||||
}
|
||||
|
||||
// always present and not empty, during every sync
|
||||
db.endpointTimerDao().addAll(endpointTimers)
|
||||
db.teacherDao().clear(profileId)
|
||||
db.teacherDao().addAll(teacherList.values())
|
||||
@ -260,6 +265,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
db.gradeCategoryDao().clear(profileId)
|
||||
db.gradeCategoryDao().addAll(gradeCategories.values())
|
||||
|
||||
// may be empty - extracted from DB on demand, by an endpoint
|
||||
if (classrooms.size > 0)
|
||||
db.classroomDao().addAll(classrooms.values())
|
||||
if (attendanceTypes.size > 0)
|
||||
@ -271,17 +277,24 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
if (teacherAbsenceTypes.size > 0)
|
||||
db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values())
|
||||
|
||||
gradesToRemove?.let { it ->
|
||||
it.removeAll?.let { _ -> db.gradeDao().clear(profileId) }
|
||||
it.removeSemester?.let { semester -> db.gradeDao().clearForSemester(profileId, semester) }
|
||||
// clear DB with DataRemoveModels added by endpoints
|
||||
for (model in toRemove) {
|
||||
when (model) {
|
||||
is DataRemoveModel.Timetable -> model.commit(profileId, db.timetableDao())
|
||||
is DataRemoveModel.Grades -> model.commit(profileId, db.gradeDao())
|
||||
}
|
||||
}
|
||||
|
||||
// not extracted from DB - always new data
|
||||
if (lessonList.isNotEmpty()) {
|
||||
db.lessonDao().clear(profile.id)
|
||||
db.lessonDao().addAll(lessonList)
|
||||
}
|
||||
if (lessonChangeList.isNotEmpty())
|
||||
db.lessonChangeDao().addAll(lessonChangeList)
|
||||
if (lessonNewList.isNotEmpty()) {
|
||||
db.timetableDao() += lessonNewList
|
||||
}
|
||||
if (gradeList.isNotEmpty()) {
|
||||
db.gradeDao().addAll(gradeList)
|
||||
}
|
||||
@ -390,8 +403,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
}
|
||||
|
||||
fun error(apiError: ApiError) {
|
||||
if (apiError.isCritical)
|
||||
cancel()
|
||||
callback.onError(apiError)
|
||||
}
|
||||
|
||||
|
@ -4,28 +4,37 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeDao
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class DataRemoveModel {
|
||||
var removeAll: Boolean? = null
|
||||
var removeSemester: Int? = null
|
||||
var removeDateFrom: Date? = null
|
||||
var removeDateTo: Date? = null
|
||||
|
||||
constructor() {
|
||||
this.removeAll = true
|
||||
open class DataRemoveModel {
|
||||
class Timetable(private val dateFrom: Date?, private val dateTo: Date?) : DataRemoveModel() {
|
||||
companion object {
|
||||
fun from(dateFrom: Date) = Timetable(dateFrom, null)
|
||||
fun to(dateTo: Date) = Timetable(null, dateTo)
|
||||
fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo)
|
||||
}
|
||||
fun commit(profileId: Int, dao: TimetableDao) {
|
||||
if (dateFrom != null && dateTo != null) {
|
||||
dao.clearBetweenDates(profileId, dateFrom, dateTo)
|
||||
}
|
||||
else {
|
||||
dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) }
|
||||
dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(semester: Int) {
|
||||
this.removeSemester = semester
|
||||
}
|
||||
|
||||
constructor(dateFrom: Date?, dateTo: Date) {
|
||||
this.removeDateFrom = dateFrom
|
||||
this.removeDateTo = dateTo
|
||||
}
|
||||
|
||||
constructor(dateFrom: Date) {
|
||||
this.removeDateFrom = dateFrom
|
||||
class Grades(val all: Boolean, val semester: Int?) : DataRemoveModel() {
|
||||
companion object {
|
||||
fun all() = Grades(true, null)
|
||||
fun semester(semester: Int) = Grades(false, semester)
|
||||
}
|
||||
fun commit(profileId: Int, dao: GradeDao) {
|
||||
if (all) {
|
||||
dao.clear(profileId)
|
||||
}
|
||||
semester?.let { dao.clearForSemester(profileId, it) }
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.template
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
@ -48,7 +49,8 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
@ -48,7 +49,8 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
|
@ -38,35 +38,39 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
when (endpointId) {
|
||||
ENDPOINT_VULCAN_API_DICTIONARIES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_dictionaries)
|
||||
VulcanApiDictionaries(data) { onSuccess() }
|
||||
VulcanApiDictionaries(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_GRADES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
VulcanApiGrades(data) { onSuccess() }
|
||||
VulcanApiGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_GRADES_SUMMARY -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades)
|
||||
VulcanApiProposedGrades(data) { onSuccess() }
|
||||
VulcanApiProposedGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_EVENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_events)
|
||||
VulcanApiEvents(data, isHomework = false) { onSuccess() }
|
||||
VulcanApiEvents(data, isHomework = false, onSuccess = onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_HOMEWORK -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_homework)
|
||||
VulcanApiEvents(data, isHomework = true) { onSuccess() }
|
||||
VulcanApiEvents(data, isHomework = true, onSuccess = onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_NOTICES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notices)
|
||||
VulcanApiNotices(data) { onSuccess() }
|
||||
VulcanApiNotices(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_ATTENDANCE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
VulcanApiAttendance(data) { onSuccess() }
|
||||
VulcanApiAttendance(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_MESSAGES_INBOX -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
VulcanApiMessagesInbox(data) { onSuccess() }
|
||||
VulcanApiMessagesInbox(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_MESSAGES_SENT -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
|
||||
VulcanApiMessagesSent(data, onSuccess)
|
||||
}
|
||||
else -> onSuccess()
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INB
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -20,64 +21,68 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
const val TAG = "VulcanApiMessagesInbox"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
|
||||
val startDate: String = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d
|
||||
else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d
|
||||
}
|
||||
val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
"DataKoncowa" to endDate,
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { json, _ ->
|
||||
json.getJsonArray("Data").asJsonObjectList()?.forEach { message ->
|
||||
val id = message.getLong("WiadomoscId") ?: return@forEach
|
||||
val subject = message.getString("Tytul") ?: ""
|
||||
val body = message.getString("Tresc") ?: ""
|
||||
|
||||
val senderLoginId = message.getString("NadawcaId") ?: return@forEach
|
||||
val senderId = data.teacherList
|
||||
.singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach
|
||||
|
||||
val addedDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } ?: -1
|
||||
val readDate = message.getLong("DataPrzeczytaniaUnixEpoch")?.let { it * 1000 } ?: -1
|
||||
|
||||
val messageObject = Message(
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
Message.TYPE_RECEIVED,
|
||||
senderId,
|
||||
-1
|
||||
)
|
||||
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
readDate,
|
||||
id
|
||||
)
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
readDate > 0,
|
||||
readDate > 0,
|
||||
addedDate
|
||||
))
|
||||
val startDate: String = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d
|
||||
else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d
|
||||
}
|
||||
val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess()}
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
"DataKoncowa" to endDate,
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { json, _ ->
|
||||
json.getJsonArray("Data").asJsonObjectList()?.forEach { message ->
|
||||
val id = message.getLong("WiadomoscId") ?: return@forEach
|
||||
val subject = message.getString("Tytul") ?: ""
|
||||
val body = message.getString("Tresc") ?: ""
|
||||
|
||||
val senderLoginId = message.getString("NadawcaId") ?: return@forEach
|
||||
val senderId = data.teacherList
|
||||
.singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach
|
||||
|
||||
val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 }
|
||||
?: -1
|
||||
val readDate = message.getLong("DataPrzeczytaniaUnixEpoch")?.let { it * 1000 }
|
||||
?: -1
|
||||
|
||||
val messageObject = Message(
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
TYPE_RECEIVED,
|
||||
senderId,
|
||||
-1
|
||||
)
|
||||
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
readDate,
|
||||
id
|
||||
)
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
readDate > 0,
|
||||
readDate > 0,
|
||||
sentDate
|
||||
))
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-5
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_MESSAGES_SENT
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_MESSAGES_SENT
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
const val TAG = "VulcanApiMessagesSent"
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
val startDate: String = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d
|
||||
else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d
|
||||
}
|
||||
val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_SENT, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
"DataKoncowa" to endDate,
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { json, _ ->
|
||||
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { message ->
|
||||
val id = message.getLong("WiadomoscId") ?: return@forEach
|
||||
val subject = message.getString("Tytul") ?: ""
|
||||
val body = message.getString("Tresc") ?: ""
|
||||
val readBy = message.getInt("Przeczytane") ?: 0
|
||||
val unreadBy = message.getInt("Nieprzeczytane") ?: 0
|
||||
val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } ?: -1
|
||||
|
||||
val messageObject = Message(
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
TYPE_SENT,
|
||||
-1,
|
||||
-1
|
||||
)
|
||||
|
||||
message.getJsonArray("Adresaci")?.asJsonObjectList()
|
||||
?.forEachIndexed { _, recipient ->
|
||||
|
||||
val recipientLoginId = recipient.getString("LoginId")
|
||||
?: return@forEachIndexed
|
||||
val recipientId = data.teacherList.singleOrNull { it.loginId == recipientLoginId }?.id
|
||||
?: return@forEachIndexed
|
||||
|
||||
val readDate: Long = when (readBy) {
|
||||
0 -> 0
|
||||
else -> when (unreadBy) {
|
||||
0 -> 1
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
recipientId,
|
||||
-1,
|
||||
readDate,
|
||||
id
|
||||
)
|
||||
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
true,
|
||||
true,
|
||||
sentDate
|
||||
))
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_SENT, 1 * DAY, DRAWER_ITEM_MESSAGES)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
|
||||
class VulcanApiTemplate(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
@ -15,10 +13,12 @@ class VulcanApiTemplate(override val data: DataVulcan, val onSuccess: () -> Unit
|
||||
}
|
||||
|
||||
init {
|
||||
/* apiGet(TAG, VULCAN_API_ENDPOINT_) { json, _ ->
|
||||
/* data.profile?.also { profile ->
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_) { json, _ ->
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceTypeDao
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherDao;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
@Database(entities = {
|
||||
@ -103,7 +104,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
Classroom.class,
|
||||
NoticeType.class,
|
||||
AttendanceType.class,
|
||||
Metadata.class}, version = 63)
|
||||
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
|
||||
Metadata.class}, version = 64)
|
||||
@TypeConverters({
|
||||
ConverterTime.class,
|
||||
ConverterDate.class,
|
||||
@ -141,6 +143,7 @@ public abstract class AppDb extends RoomDatabase {
|
||||
public abstract ClassroomDao classroomDao();
|
||||
public abstract NoticeTypeDao noticeTypeDao();
|
||||
public abstract AttendanceTypeDao attendanceTypeDao();
|
||||
public abstract TimetableDao timetableDao();
|
||||
public abstract MetadataDao metadataDao();
|
||||
|
||||
private static volatile AppDb INSTANCE;
|
||||
@ -729,6 +732,37 @@ public abstract class AppDb extends RoomDatabase {
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_63_64 = new Migration(63, 64) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
//database.execSQL("ALTER TABLE lessons RENAME TO lessonsOld;");
|
||||
database.execSQL("CREATE TABLE timetable (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"id INTEGER NOT NULL," +
|
||||
"type INTEGER NOT NULL," +
|
||||
|
||||
"date TEXT DEFAULT NULL," +
|
||||
"lessonNumber INTEGER DEFAULT NULL," +
|
||||
"startTime TEXT DEFAULT NULL," +
|
||||
"endTime TEXT DEFAULT NULL," +
|
||||
"subjectId INTEGER DEFAULT NULL," +
|
||||
"teacherId INTEGER DEFAULT NULL," +
|
||||
"teamId INTEGER DEFAULT NULL," +
|
||||
"classroom TEXT DEFAULT NULL," +
|
||||
|
||||
"oldDate TEXT DEFAULT NULL," +
|
||||
"oldLessonNumber INTEGER DEFAULT NULL," +
|
||||
"oldStartTime TEXT DEFAULT NULL," +
|
||||
"oldEndTime TEXT DEFAULT NULL," +
|
||||
"oldSubjectId INTEGER DEFAULT NULL," +
|
||||
"oldTeacherId INTEGER DEFAULT NULL," +
|
||||
"oldTeamId INTEGER DEFAULT NULL," +
|
||||
"oldClassroom TEXT DEFAULT NULL," +
|
||||
"PRIMARY KEY(id));");
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_date ON timetable (profileId, type, date);");
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static AppDb getDatabase(final Context context) {
|
||||
@ -789,7 +823,8 @@ public abstract class AppDb extends RoomDatabase {
|
||||
MIGRATION_59_60,
|
||||
MIGRATION_60_61,
|
||||
MIGRATION_61_62,
|
||||
MIGRATION_62_63
|
||||
MIGRATION_62_63,
|
||||
MIGRATION_63_64
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
//.fallbackToDestructiveMigration()
|
||||
|
@ -8,6 +8,7 @@ import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
|
||||
@ -73,7 +74,7 @@ public class LoginStore {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
if (element != null && !(element instanceof JsonNull)) {
|
||||
return element.getAsString();
|
||||
}
|
||||
return defaultValue;
|
||||
@ -83,7 +84,7 @@ public class LoginStore {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
if (element != null && !(element instanceof JsonNull)) {
|
||||
return element.getAsInt();
|
||||
}
|
||||
return defaultValue;
|
||||
@ -93,7 +94,7 @@ public class LoginStore {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
if (element != null && !(element instanceof JsonNull)) {
|
||||
return element.getAsLong();
|
||||
}
|
||||
return defaultValue;
|
||||
@ -103,7 +104,7 @@ public class LoginStore {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
if (element != null && !(element instanceof JsonNull)) {
|
||||
return element.getAsFloat();
|
||||
}
|
||||
return defaultValue;
|
||||
@ -112,7 +113,7 @@ public class LoginStore {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
if (element != null && !(element instanceof JsonNull)) {
|
||||
return element.getAsBoolean();
|
||||
}
|
||||
return defaultValue;
|
||||
|
@ -4,12 +4,20 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
open class Lesson(val profileId: Int) {
|
||||
@Entity(tableName = "timetable",
|
||||
indices = [
|
||||
Index(value = ["profileId", "type", "date"]),
|
||||
Index(value = ["profileId", "type", "oldDate"])
|
||||
])
|
||||
open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
companion object {
|
||||
const val TYPE_NO_LESSONS = -1
|
||||
const val TYPE_NORMAL = 0
|
||||
const val TYPE_CANCELLED = 1
|
||||
const val TYPE_CHANGE = 2
|
||||
@ -17,15 +25,14 @@ open class Lesson(val profileId: Int) {
|
||||
const val TYPE_SHIFTED_TARGET = 4 /* target lesson */
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "lessonType")
|
||||
var type: Int = TYPE_NORMAL
|
||||
|
||||
var date: Date? = null
|
||||
var lessonNumber: Int? = null
|
||||
var startTime: Time? = null
|
||||
var endTime: Time? = null
|
||||
var teacherId: Long? = null
|
||||
var subjectId: Long? = null
|
||||
var teacherId: Long? = null
|
||||
var teamId: Long? = null
|
||||
var classroom: String? = null
|
||||
|
||||
@ -33,8 +40,58 @@ open class Lesson(val profileId: Int) {
|
||||
var oldLessonNumber: Int? = null
|
||||
var oldStartTime: Time? = null
|
||||
var oldEndTime: Time? = null
|
||||
var oldTeacherId: Long? = null
|
||||
var oldSubjectId: Long? = null
|
||||
var oldTeacherId: Long? = null
|
||||
var oldTeamId: Long? = null
|
||||
var oldClassroom: String? = null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Lesson(profileId=$profileId, " +
|
||||
"id=$id, " +
|
||||
"type=$type, " +
|
||||
"date=$date, " +
|
||||
"lessonNumber=$lessonNumber, " +
|
||||
"startTime=$startTime, " +
|
||||
"endTime=$endTime, " +
|
||||
"subjectId=$subjectId, " +
|
||||
"teacherId=$teacherId, " +
|
||||
"teamId=$teamId, " +
|
||||
"classroom=$classroom, " +
|
||||
"oldDate=$oldDate, " +
|
||||
"oldLessonNumber=$oldLessonNumber, " +
|
||||
"oldStartTime=$oldStartTime, " +
|
||||
"oldEndTime=$oldEndTime, " +
|
||||
"oldSubjectId=$oldSubjectId, " +
|
||||
"oldTeacherId=$oldTeacherId, " +
|
||||
"oldTeamId=$oldTeamId, " +
|
||||
"oldClassroom=$oldClassroom)"
|
||||
}
|
||||
}
|
||||
/*
|
||||
DROP TABLE lessons;
|
||||
DROP TABLE lessonChanges;
|
||||
CREATE TABLE lessons (
|
||||
profileId INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
|
||||
date TEXT DEFAULT NULL,
|
||||
lessonNumber INTEGER DEFAULT NULL,
|
||||
startTime TEXT DEFAULT NULL,
|
||||
endTime TEXT DEFAULT NULL,
|
||||
teacherId INTEGER DEFAULT NULL,
|
||||
subjectId INTEGER DEFAULT NULL,
|
||||
teamId INTEGER DEFAULT NULL,
|
||||
classroom TEXT DEFAULT NULL,
|
||||
|
||||
oldDate TEXT DEFAULT NULL,
|
||||
oldLessonNumber INTEGER DEFAULT NULL,
|
||||
oldStartTime TEXT DEFAULT NULL,
|
||||
oldEndTime TEXT DEFAULT NULL,
|
||||
oldTeacherId INTEGER DEFAULT NULL,
|
||||
oldSubjectId INTEGER DEFAULT NULL,
|
||||
oldTeamId INTEGER DEFAULT NULL,
|
||||
oldClassroom TEXT DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY(profileId)
|
||||
);
|
||||
*/
|
@ -0,0 +1,69 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
var subjectName: String? = null
|
||||
var teacherName: String? = null
|
||||
var teamName: String? = null
|
||||
var oldSubjectName: String? = null
|
||||
var oldTeacherName: String? = null
|
||||
var oldTeamName: String? = null
|
||||
|
||||
val displayDate: Date?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldDate
|
||||
return date ?: oldDate
|
||||
}
|
||||
val displayLessonNumber: Int?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldLessonNumber
|
||||
return lessonNumber ?: oldLessonNumber
|
||||
}
|
||||
val displayStartTime: Time?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldStartTime
|
||||
return startTime ?: oldStartTime
|
||||
}
|
||||
val displayEndTime: Time?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldEndTime
|
||||
return endTime ?: oldEndTime
|
||||
}
|
||||
|
||||
val displaySubjectName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldSubjectName
|
||||
return subjectName ?: oldSubjectName
|
||||
}
|
||||
val displayTeacherName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeacherName
|
||||
return teacherName ?: oldTeacherName
|
||||
}
|
||||
val displayTeamName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeamName
|
||||
return teamName ?: oldTeamName
|
||||
}
|
||||
|
||||
val displayClassroom: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldClassroom
|
||||
return classroom ?: oldClassroom
|
||||
}
|
||||
|
||||
// metadata
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
var addedDate: Long = 0
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@Dao
|
||||
interface TimetableDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
operator fun plusAssign(lessonList: List<Lesson>)
|
||||
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom)")
|
||||
fun clearFromDate(profileId: Int, dateFrom: Date)
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo)")
|
||||
fun clearToDate(profileId: Int, dateTo: Date)
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)")
|
||||
fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date)
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
timetable.*,
|
||||
subjects.subjectLongName AS subjectName,
|
||||
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
|
||||
teams.teamName AS teamName,
|
||||
oldS.subjectLongName AS oldSubjectName,
|
||||
oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
|
||||
oldG.teamName AS oldTeamName,
|
||||
metadata.seen, metadata.notified, metadata.addedDate
|
||||
FROM timetable
|
||||
LEFT JOIN subjects USING(profileId, subjectId)
|
||||
LEFT JOIN teachers USING(profileId, teacherId)
|
||||
LEFT JOIN teams USING(profileId, teamId)
|
||||
LEFT JOIN subjects AS oldS ON timetable.profileId = oldS.profileId AND timetable.oldSubjectId = oldS.subjectId
|
||||
LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId
|
||||
LEFT JOIN teams AS oldG ON timetable.profileId = oldG.profileId AND timetable.oldTeamId = oldG.teamId
|
||||
LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId
|
||||
WHERE timetable.profileId = :profileId AND (type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date)
|
||||
ORDER BY type
|
||||
""")
|
||||
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
|
||||
}
|
@ -112,7 +112,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||
app.notifier.postAll(profile);
|
||||
app.saveConfig("notifications");*/
|
||||
d(TAG, "Syncing profile " + profile.getId());
|
||||
EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app);
|
||||
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
||||
} else {
|
||||
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
|
||||
.withProfileData(profile.id, profile.name)
|
||||
@ -123,7 +123,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||
app.notifier.postAll(profile);
|
||||
app.saveConfig("notifications");*/
|
||||
d(TAG, "Syncing profile " + profile.getId());
|
||||
EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app);
|
||||
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -6,6 +6,7 @@ import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -30,11 +31,14 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.chuckerteam.chucker.api.Chucker;
|
||||
import com.hypertrack.hyperlog.HyperLog;
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -50,6 +54,7 @@ import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding;
|
||||
import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding;
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginLibrusCaptchaActivity;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
@ -104,6 +109,27 @@ public class HomeFragment extends Fragment {
|
||||
|
||||
b.pruneWorkButton.setOnClickListener((v -> WorkManager.getInstance(app).pruneWork()));
|
||||
|
||||
b.runChucker.setOnClickListener((v -> {
|
||||
startActivity(Chucker.getLaunchIntent(activity, 1));
|
||||
}));
|
||||
|
||||
b.librusCaptchaButton.setOnClickListener((v -> {
|
||||
startActivity(new Intent(activity, LoginLibrusCaptchaActivity.class));
|
||||
}));
|
||||
|
||||
b.getLogs.setOnClickListener((v -> {
|
||||
File logs = HyperLog.getDeviceLogsInFile(activity, true);
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
|
||||
if(logs.exists()) {
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+logs.getAbsolutePath()));
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "Share debug logs");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, "Share debug logs");
|
||||
startActivity(Intent.createChooser(intent, "Share debug logs"));
|
||||
}
|
||||
}));
|
||||
|
||||
//((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS()));
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ public class LoginChooserFragment extends Fragment {
|
||||
private NavController nav;
|
||||
private FragmentLoginChooserBinding b;
|
||||
private static final String TAG = "LoginTemplate";
|
||||
public static boolean fakeLogin = false;
|
||||
|
||||
public LoginChooserFragment() { }
|
||||
|
||||
@ -71,6 +72,10 @@ public class LoginChooserFragment extends Fragment {
|
||||
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));
|
||||
}));
|
||||
|
@ -0,0 +1,116 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.login
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Base64
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.hexFromColorInt
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class LoginLibrusCaptchaActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val TAG = "LoginLibrusCaptchaActivity"
|
||||
}
|
||||
|
||||
private lateinit var webView: WebView
|
||||
private lateinit var dialog: AlertDialog
|
||||
private lateinit var jsInterface: CaptchaCallbackInterface
|
||||
|
||||
@SuppressLint("AddJavascriptInterface", "SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setTheme(Themes.appThemeNoDisplay)
|
||||
setFinishOnTouchOutside(false)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
WebView.setWebContentsDebuggingEnabled(true)
|
||||
}
|
||||
|
||||
val base64Content = """
|
||||
PCFET0NUWVBFIGh0bWw+PGh0bWw+PGhlYWQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vd3d3Lmdvb2ds
|
||||
ZS5jb20vcmVjYXB0Y2hhL2FwaS5qcz9vbmxvYWQ9cmVhZHkmcmVuZGVyPWV4cGxpY2l0Ij48L3Nj
|
||||
cmlwdD48L2hlYWQ+PGJvZHk+PGJyPjxjZW50ZXIgaWQ9ImdyIj48L2NlbnRlcj48YnI+PHNjcmlw
|
||||
dD5mdW5jdGlvbiByZWFkeSgpe2dyZWNhcHRjaGEucmVuZGVyKCdncicse3NpdGVrZXk6JzZMZjQ4
|
||||
bW9VQUFBQUFCOUNsaGR2SHI0NmdSV1ItQ04zMUNYUVBHMlUnLHRoZW1lOidUSEVNRScsY2FsbGJh
|
||||
Y2s6ZnVuY3Rpb24oZSl7d2luZG93LmlmLmNhbGxiYWNrKGUpO30sImV4cGlyZWQtY2FsbGJhY2si
|
||||
OmZ1bmN0aW9uKCl7d2luZG93LmlmLmV4cGlyZWRDYWxsYmFjayhlKTt9LCJlcnJvci1jYWxsYmFj
|
||||
ayI6ZnVuY3Rpb24oKXt3aW5kb3cuaWYuZXJyb3JDYWxsYmFjayhlKTt9fSk7fTwvc2NyaXB0Pjwv
|
||||
Ym9keT48L2h0bWw+"""
|
||||
|
||||
val backgroundColor = if (Themes.isDark) 0x424242 else 0xffffff
|
||||
val backgroundColorString = hexFromColorInt(backgroundColor)
|
||||
|
||||
val htmlContent = Base64.decode(base64Content, Base64.DEFAULT)
|
||||
.toString(Charset.defaultCharset())
|
||||
.replace("COLOR", backgroundColorString, true)
|
||||
.replace("THEME", if (Themes.isDark) "dark" else "light")
|
||||
|
||||
jsInterface = object : CaptchaCallbackInterface {
|
||||
@JavascriptInterface
|
||||
override fun callback(recaptchaResponse: String) {
|
||||
MaterialDialog.Builder(this@LoginLibrusCaptchaActivity)
|
||||
.title("Captcha checked")
|
||||
.content("Response: $recaptchaResponse")
|
||||
.positiveText("OK")
|
||||
.show()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
override fun expiredCallback() {
|
||||
MaterialDialog.Builder(this@LoginLibrusCaptchaActivity)
|
||||
.title("Captcha expired")
|
||||
.content("Captcha expired")
|
||||
.positiveText("OK")
|
||||
.show()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
override fun errorCallback() {
|
||||
MaterialDialog.Builder(this@LoginLibrusCaptchaActivity)
|
||||
.title("Captcha error")
|
||||
.content("Captcha error")
|
||||
.positiveText("OK")
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
webView = WebView(this).apply {
|
||||
//setBackgroundColor((backgroundColor.toLong() or 0xff000000).toInt())
|
||||
setBackgroundColor(Color.TRANSPARENT)
|
||||
settings.javaScriptEnabled = true
|
||||
settings.userAgentString = LIBRUS_USER_AGENT
|
||||
addJavascriptInterface(jsInterface, "if")
|
||||
loadDataWithBaseURL("https://portal.librus.pl/rodzina/login/", htmlContent, "text/html", "UTF-8", null)
|
||||
setLayerType(WebView.LAYER_TYPE_SOFTWARE, null)
|
||||
}
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.login_librus_captcha_title)
|
||||
.setView(webView)
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
finish()
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
interface CaptchaCallbackInterface {
|
||||
@JavascriptInterface
|
||||
fun callback(recaptchaResponse: String)
|
||||
@JavascriptInterface
|
||||
fun expiredCallback()
|
||||
@JavascriptInterface
|
||||
fun errorCallback()
|
||||
}
|
||||
}
|
@ -89,6 +89,10 @@ public class LoginProgressFragment extends Fragment {
|
||||
LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject());
|
||||
loginStore.copyFrom(args);
|
||||
|
||||
if (App.devMode && LoginChooserFragment.fakeLogin) {
|
||||
loginStore.putLoginData("fakeLogin", true);
|
||||
}
|
||||
|
||||
EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext());
|
||||
}
|
||||
|
||||
|
@ -533,7 +533,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
syncCardIntervalItem.setChecked(app.appConfig.registerSyncEnabled);
|
||||
syncCardIntervalItem.setOnClickAction(() -> {
|
||||
List<CharSequence> intervalNames = new ArrayList<>();
|
||||
if (App.devMode) {
|
||||
if (App.devMode && false) {
|
||||
intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_seconds, 30));
|
||||
intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 2));
|
||||
}
|
||||
@ -545,7 +545,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_hours, 3));
|
||||
intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_hours, 4));
|
||||
List<Integer> intervals = new ArrayList<>();
|
||||
if (App.devMode) {
|
||||
if (App.devMode && false) {
|
||||
intervals.add(30);
|
||||
intervals.add(2 * 60);
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class TimetableFragment : Fragment() {
|
||||
companion object {
|
||||
private const val TAG = "TimetableFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentTimetableV2Binding
|
||||
private var fabShown = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
if (context == null)
|
||||
return null
|
||||
app = activity.application as App
|
||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false)
|
||||
// activity, context and profile is valid
|
||||
b = FragmentTimetableV2Binding.inflate(inflater)
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getLoginData("timetableNotPublic", false)) {
|
||||
b.timetableLayout.visibility = View.GONE
|
||||
b.timetableNotPublicLayout.visibility = View.VISIBLE
|
||||
return
|
||||
}
|
||||
b.timetableLayout.visibility = View.VISIBLE
|
||||
b.timetableNotPublicLayout.visibility = View.GONE
|
||||
|
||||
val items = mutableListOf<Date>()
|
||||
|
||||
val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
|
||||
val today = Date.getToday().value
|
||||
val yearStart = app.profile.dateSemester1Start?.clone() ?: return
|
||||
val yearEnd = app.profile.dateYearEnd ?: return
|
||||
while (yearStart.value <= yearEnd.value) {
|
||||
items += yearStart.clone()
|
||||
var maxDays = monthDayCount[yearStart.month-1]
|
||||
if (yearStart.month == 2 && yearStart.isLeap)
|
||||
maxDays++
|
||||
yearStart.day++
|
||||
if (yearStart.day > maxDays) {
|
||||
yearStart.day = 1
|
||||
yearStart.month++
|
||||
}
|
||||
if (yearStart.month > 12) {
|
||||
yearStart.month = 1
|
||||
yearStart.year++
|
||||
}
|
||||
}
|
||||
|
||||
val pagerAdapter = TimetablePagerAdapter(fragmentManager ?: return, items)
|
||||
b.viewPager.offscreenPageLimit = 2
|
||||
b.viewPager.adapter = pagerAdapter
|
||||
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
activity.navView.bottomBar.fabEnable = items[position].value != today
|
||||
if (activity.navView.bottomBar.fabEnable && !fabShown) {
|
||||
activity.gainAttentionFAB()
|
||||
fabShown = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.tabLayout.setUpWithViewPager(b.viewPager)
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, false)
|
||||
|
||||
//activity.navView.bottomBar.fabEnable = true
|
||||
activity.navView.bottomBar.fabExtendedText = getString(R.string.timetable_today)
|
||||
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon.cmd_calendar_today
|
||||
activity.navView.setFabOnClickListener(View.OnClickListener {
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: List<Date>) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
companion object {
|
||||
private const val TAG = "TimetablePagerAdapter"
|
||||
}
|
||||
|
||||
private val today by lazy { Date.getToday() }
|
||||
private val weekStart by lazy { today.clone().stepForward(0, 0, -today.weekDay) }
|
||||
private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) }
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
return pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day.TimetableDayFragment(items[position])
|
||||
/*return TimetableDayFragment().apply {
|
||||
arguments = Bundle().also {
|
||||
it.putLong("date", items[position].value.toLong())
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
val date = items[position]
|
||||
val pageTitle = StringBuilder(Week.getFullDayName(date.weekDay))
|
||||
if (date > weekEnd || date < weekStart) {
|
||||
pageTitle.append(", ").append(date.stringDm)
|
||||
}
|
||||
return pageTitle
|
||||
}
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import com.linkedin.android.tachyon.DayView
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class TimetableDayFragment(val date: Date) : Fragment() {
|
||||
companion object {
|
||||
private const val TAG = "TimetableDayFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentTimetableV2DayBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
if (context == null)
|
||||
return null
|
||||
app = activity.application as App
|
||||
b = FragmentTimetableV2DayBinding.inflate(inflater)
|
||||
Log.d(TAG, "onCreateView, date=$date")
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
Log.d(TAG, "onViewCreated, date=$date")
|
||||
|
||||
// Inflate a label view for each hour the day view will display
|
||||
val hourLabelViews = ArrayList<View>()
|
||||
for (i in b.day.startHour..b.day.endHour) {
|
||||
val hourLabelView = layoutInflater.inflate(R.layout.timetable_hour_label, b.day, false) as TextView
|
||||
hourLabelView.text = "$i:00"
|
||||
hourLabelViews.add(hourLabelView)
|
||||
}
|
||||
b.day.setHourLabelViews(hourLabelViews)
|
||||
|
||||
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer<List<LessonFull>> { lessons ->
|
||||
buildLessonViews(lessons)
|
||||
})
|
||||
}
|
||||
|
||||
private fun buildLessonViews(lessons: List<LessonFull>) {
|
||||
if (lessons.isEmpty()) {
|
||||
b.dayScroll.visibility = View.GONE
|
||||
b.noTimetableLayout.visibility = View.VISIBLE
|
||||
b.noLessonsLayout.visibility = View.GONE
|
||||
val weekStart = date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d
|
||||
b.noTimetableSync.setOnClickListener {
|
||||
EdziennikTask.syncProfile(
|
||||
profileId = App.profileId,
|
||||
viewIds = listOf(
|
||||
DRAWER_ITEM_TIMETABLE to 0
|
||||
),
|
||||
arguments = JsonObject(
|
||||
"weekStart" to weekStart
|
||||
)
|
||||
).enqueue(activity)
|
||||
}
|
||||
b.noTimetableWeek.setText(R.string.timetable_no_timetable_week, weekStart)
|
||||
return
|
||||
}
|
||||
if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) {
|
||||
b.dayScroll.visibility = View.GONE
|
||||
b.noTimetableLayout.visibility = View.GONE
|
||||
b.noLessonsLayout.visibility = View.VISIBLE
|
||||
return
|
||||
}
|
||||
|
||||
// reload the fragment when: no lessons, user wants to sync the week, the timetable is not public, pager gets removed
|
||||
if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getLoginData("timetableNotPublic", false)) {
|
||||
activity.reloadTarget()
|
||||
// TODO fix for (not really)possible infinite loops
|
||||
return
|
||||
}
|
||||
|
||||
b.dayScroll.visibility = View.VISIBLE
|
||||
b.noTimetableLayout.visibility = View.GONE
|
||||
b.noLessonsLayout.visibility = View.GONE
|
||||
|
||||
var firstEventMinute = 24*60
|
||||
|
||||
val eventViews = mutableListOf<View>()
|
||||
val eventTimeRanges = mutableListOf<DayView.EventTimeRange>()
|
||||
|
||||
// Reclaim all of the existing event views so we can reuse them if needed, this process
|
||||
// can be useful if your day view is hosted in a recycler view for example
|
||||
val recycled = b.day.removeEventViews()
|
||||
var remaining = recycled?.size ?: 0
|
||||
|
||||
val arrowRight = " → "
|
||||
val bullet = " • "
|
||||
val colorSecondary = getColorFromAttr(activity, android.R.attr.textColorSecondary)
|
||||
|
||||
for (lesson in lessons) {
|
||||
val startTime = lesson.displayStartTime ?: continue
|
||||
val endTime = lesson.displayEndTime ?: continue
|
||||
|
||||
firstEventMinute = min(firstEventMinute, startTime.hour*60 + startTime.minute)
|
||||
|
||||
// Try to recycle an existing event view if there are enough left, otherwise inflate
|
||||
// a new one
|
||||
val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, b.day, false))
|
||||
?: continue
|
||||
val lb = TimetableLessonBinding.bind(eventView)
|
||||
eventViews += eventView
|
||||
|
||||
eventView.tag = lesson
|
||||
|
||||
eventView.setOnClickListener {
|
||||
Log.d(TAG, "Clicked ${it.tag}")
|
||||
}
|
||||
|
||||
|
||||
val timeRange = "${startTime.stringHM} - ${endTime.stringHM}".asColoredSpannable(colorSecondary)
|
||||
|
||||
// teacher
|
||||
val teacherInfo = if (lesson.teacherId != null && lesson.teacherId == lesson.oldTeacherId)
|
||||
lesson.teacherName ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldTeacherName?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.teacherName?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
// team
|
||||
val teamInfo = if (lesson.teamId != null && lesson.teamId == lesson.oldTeamId)
|
||||
lesson.teamName ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldTeamName?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.teamName?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
// classroom
|
||||
val classroomInfo = if (lesson.classroom != null && lesson.classroom == lesson.oldClassroom)
|
||||
lesson.classroom ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldClassroom?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.classroom?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
|
||||
lb.lessonNumber = lesson.displayLessonNumber
|
||||
lb.subjectName.text = lesson.displaySubjectName?.let {
|
||||
if (lesson.type == Lesson.TYPE_CANCELLED || lesson.type == Lesson.TYPE_SHIFTED_SOURCE)
|
||||
it.asStrikethroughSpannable().asColoredSpannable(colorSecondary)
|
||||
else
|
||||
it
|
||||
}
|
||||
lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
lb.annotationVisible = false
|
||||
}
|
||||
Lesson.TYPE_CANCELLED -> {
|
||||
lb.annotationVisible = true
|
||||
lb.annotation.setText(R.string.timetable_lesson_cancelled)
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
//lb.subjectName.typeface = Typeface.DEFAULT
|
||||
}
|
||||
Lesson.TYPE_CHANGE -> {
|
||||
lb.annotationVisible = true
|
||||
if (lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
|
||||
)
|
||||
}
|
||||
else if (lesson.subjectId != lesson.oldSubjectId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
lesson.oldSubjectName ?: "?"
|
||||
)
|
||||
}
|
||||
else if (lesson.teacherId != lesson.oldTeacherId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
lesson.oldTeacherName ?: "?"
|
||||
)
|
||||
}
|
||||
else {
|
||||
lb.annotation.setText(R.string.timetable_lesson_change)
|
||||
}
|
||||
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_change_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
}
|
||||
Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
lb.annotationVisible = true
|
||||
if (lesson.date != lesson.oldDate) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_shifted_other_day,
|
||||
lesson.date?.stringY_m_d ?: "?",
|
||||
lesson.startTime?.stringHM ?: "?"
|
||||
)
|
||||
}
|
||||
else if (lesson.startTime != lesson.oldStartTime) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_shifted_same_day,
|
||||
lesson.startTime?.stringHM ?: "?"
|
||||
)
|
||||
}
|
||||
else {
|
||||
lb.annotation.setText(R.string.timetable_lesson_shifted)
|
||||
}
|
||||
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_shifted_source_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
}
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
lb.annotationVisible = true
|
||||
if (lesson.date != lesson.oldDate) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_shifted_from_other_day,
|
||||
lesson.oldDate?.stringY_m_d ?: "?",
|
||||
lesson.oldStartTime?.stringHM ?: "?"
|
||||
)
|
||||
}
|
||||
else if (lesson.startTime != lesson.oldStartTime) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_shifted_from_same_day,
|
||||
lesson.oldStartTime?.stringHM ?: "?"
|
||||
)
|
||||
}
|
||||
else {
|
||||
lb.annotation.setText(R.string.timetable_lesson_shifted_from)
|
||||
}
|
||||
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_shifted_target_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The day view needs the event time ranges in the start minute/end minute format,
|
||||
// so calculate those here
|
||||
val startMinute = 60 * (lesson.displayStartTime?.hour ?: 0) + (lesson.displayStartTime?.minute ?: 0)
|
||||
val endMinute = startMinute + 45
|
||||
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
|
||||
}
|
||||
|
||||
val minuteHeight = (b.day.getHourTop(1) - b.day.getHourTop(0)).toFloat() / 60f
|
||||
val firstEventTop = (firstEventMinute - b.day.startHour * 60) * minuteHeight
|
||||
b.day.setEventViews(eventViews, eventTimeRanges)
|
||||
b.dayScroll.scrollTo(0, firstEventTop.toInt())
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
|
||||
val appManagerIntentList = listOf(
|
||||
Intent().setClassName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity")
|
||||
.putExtra("packageName", "pl.szczodrzynski.edziennik")
|
||||
.putExtra("package_label", "Szkolny.eu"),
|
||||
Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
|
||||
Intent().setComponent(ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")),
|
||||
Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
|
||||
Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
|
||||
Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
|
||||
Intent().setComponent(ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
|
||||
Intent().setComponent(ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")),
|
||||
Intent().setComponent(ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")),
|
||||
Intent().setComponent(ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity"))
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import android.content.Context
|
||||
import com.hypertrack.hyperlog.LogFormat
|
||||
|
||||
class DebugLogFormat(context: Context) : LogFormat(context) {
|
||||
override fun getFormattedLogMessage(logLevelName: String?, tag: String?, message: String?, timeStamp: String?, senderName: String?, osVersion: String?, deviceUUID: String?): String {
|
||||
return "${timeStamp?.replace("[TZ]".toRegex(), " ")}D/$tag: $message"
|
||||
}
|
||||
}
|
@ -26,6 +26,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.hypertrack.hyperlog.HyperLog;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
@ -106,15 +108,15 @@ public class Utils {
|
||||
|
||||
public static void d(String TAG, String message) {
|
||||
if (App.devMode) {
|
||||
Log.d("Szkolny/"+TAG, message);
|
||||
HyperLog.d("Szkolny/"+TAG, message);
|
||||
//debugLog.add(TAG+": "+message);
|
||||
}
|
||||
}
|
||||
public static void c(String TAG, String message) {
|
||||
if (App.devMode) {
|
||||
/*if (App.devMode) {
|
||||
Log.d(TAG, "// " + message);
|
||||
///debugLog.add(TAG+": // "+message);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package pl.szczodrzynski.edziennik.utils.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
@ -131,6 +132,13 @@ public class Date implements Comparable<Date> {
|
||||
return year * 10000 + month * 100 + day;
|
||||
}
|
||||
|
||||
public static Date fromValue(int value) {
|
||||
int year = value / 10000;
|
||||
int month = (value-year*10000) / 100;
|
||||
int day = (value-year*10000-month*100);
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
|
||||
public String getStringValue()
|
||||
{
|
||||
return Integer.toString(getValue());
|
||||
@ -182,6 +190,10 @@ public class Date implements Comparable<Date> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLeap() {
|
||||
return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
|
||||
}
|
||||
|
||||
public static Date getToday()
|
||||
{
|
||||
Calendar cal = Calendar.getInstance();
|
||||
@ -214,6 +226,11 @@ public class Date implements Comparable<Date> {
|
||||
return this.getValue() - o.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return obj instanceof Date && this.getValue() == ((Date) obj).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Date{" +
|
||||
|
@ -1,5 +1,7 @@
|
||||
package pl.szczodrzynski.edziennik.utils.models;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class Time {
|
||||
@ -173,6 +175,11 @@ public class Time {
|
||||
return (currentTime.getValue() >= startTime.getValue() && currentTime.getValue() <= endTime.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return obj instanceof Time && this.getValue() == ((Time) obj).getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Time{" +
|
||||
|
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#000000" />
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
<item android:state_focused="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
<item android:state_selected="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
</selector>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke android:color="@color/dividerColor" android:width="1dp" />
|
||||
<solid android:color="#DCDCDC" />
|
||||
</shape>
|
61
app/src/main/res/drawable/ic_no_timetable.xml
Normal file
61
app/src/main/res/drawable/ic_no_timetable.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="m117,76h4c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7h-13c-2.199,0 -4,-1.801 -4,-4s1.801,-4 4,-4h11c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7h-1c-2.211,0 -4,-1.789 -4,-4 0,-2.211 1.789,-4 4,-4h4c3.695,0 6.637,-3.387 5.879,-7.211 -0.566,-2.844 -3.238,-4.789 -6.141,-4.789h-15.738c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h6.824c2.277,0 4.402,-1.441 4.992,-3.641 0.895,-3.328 -1.625,-6.359 -4.816,-6.359h-96c-3.852,0 -7,3.148 -7,7 0,3.852 3.148,7 7,7h3c2.211,0 4,1.789 4,4 0,2.211 -1.789,4 -4,4h-10.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h15l-6,24h-10.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h1c2.211,0 4,1.789 4,4 0,2.211 -1.789,4 -4,4h-1c-4.336,0 -7.785,3.996 -6.844,8.484 0.684,3.273 3.734,5.516 7.074,5.516h10.77c2.211,0 4,1.789 4,4s-1.789,4 -4,4h-2.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h97c3.313,0 6,-2.688 6,-6s-2.688,-6 -6,-6h-1c-2.762,0 -5,-2.238 -5,-5s2.238,-5 5,-5h6c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7 -2.75,0 -5,-2.25 -5,-5s2.25,-5 5,-5z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="100.522"
|
||||
android:centerX="63.746"
|
||||
android:centerY="71.138"
|
||||
android:type="radial"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#1F0FCCFF"/>
|
||||
<item android:offset="0.1927" android:color="#1F0FCEFF"/>
|
||||
<item android:offset="0.7025" android:color="#1F0FD5FF"/>
|
||||
<item android:offset="1" android:color="#1F0FD7FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m103.24,15.168 l-7.883,7.871c-8.746,-7.063 -19.719,-11.039 -31.355,-11.039 -27.57,0 -50,22.43 -50,50 0,27.57 22.43,50 50,50 17.758,0 34.348,-9.367 43.301,-24.449 1.41,-2.375 0.625,-5.441 -1.75,-6.852 -2.367,-1.41 -5.438,-0.629 -6.852,1.746 -7.156,12.062 -20.453,19.555 -34.699,19.555 -22.055,0 -40,-17.945 -40,-40 0,-22.055 17.945,-40 40,-40 8.934,0 17.371,2.938 24.223,8.16l-9.063,9.047c-2.48,2.5 -0.719,6.762 2.801,6.762h24.039c2.199,0 4,-1.801 4,-4v-24c0,-3.52 -4.262,-5.301 -6.762,-2.801z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="125.452"
|
||||
android:startX="62"
|
||||
android:endY="6.4762"
|
||||
android:endX="62"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFEAA53"/>
|
||||
<item android:offset="0.6124" android:color="#FFFFCD49"/>
|
||||
<item android:offset="1" android:color="#FFFFDE44"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m68,85c0,2.762 -2.238,5 -5,5s-5,-2.238 -5,-5 2.238,-5 5,-5 5,2.238 5,5zM70,41c0,-3.867 -3.133,-7 -7,-7 -3.867,0 -7,3.133 -7,7 0,0.047 0.016,0.094 0.016,0.141h-0.016l2.438,28.59c0.203,2.414 2.188,4.269 4.563,4.269s4.359,-1.855 4.563,-4.269l2.438,-28.59h-0.016c0,-0.047 0.016,-0.094 0.016,-0.141z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="90"
|
||||
android:startX="63"
|
||||
android:endY="11.7176"
|
||||
android:endX="63"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFF634D"/>
|
||||
<item android:offset="0.2043" android:color="#FFFE6464"/>
|
||||
<item android:offset="0.5209" android:color="#FFFC6581"/>
|
||||
<item android:offset="0.7936" android:color="#FFFA6694"/>
|
||||
<item android:offset="0.9892" android:color="#FFFA669A"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
45
app/src/main/res/drawable/ic_sync.xml
Normal file
45
app/src/main/res/drawable/ic_sync.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
-->
|
||||
|
||||
<vector android:height="128dp" android:viewportHeight="64"
|
||||
android:viewportWidth="64" android:width="128dp"
|
||||
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:pathData="m59,16h2c1.848,0 3.319,-1.693 2.94,-3.605 -0.283,-1.423 -1.62,-2.395 -3.071,-2.395h-10.369c-0.828,0 -1.5,-0.672 -1.5,-1.5s0.672,-1.5 1.5,-1.5h5.912c1.139,0 2.202,-0.721 2.497,-1.821 0.446,-1.663 -0.813,-3.179 -2.409,-3.179h-48c-1.925,0 -3.5,1.575 -3.5,3.5s1.575,3.5 3.5,3.5h1.5c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-5.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h7.5l-3,12h-5.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h0.5c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-0.5c-2.168,0 -3.892,1.998 -3.422,4.243 0.342,1.635 1.867,2.757 3.537,2.757h5.385c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-1.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h48.5c1.657,0 3,-1.343 3,-3s-1.343,-3 -3,-3h-0.5c-1.381,0 -2.5,-1.119 -2.5,-2.5s1.119,-2.5 2.5,-2.5h4.5c1.657,0 3,-1.343 3,-3s-1.343,-3 -3,-3h-8.377c2.141,-3.494 3.377,-7.602 3.377,-12 0,-2.441 -0.384,-4.792 -1.088,-7h6.501c1.139,0 2.202,-0.721 2.497,-1.821 0.445,-1.663 -0.814,-3.179 -2.41,-3.179h-1.5c-1.105,0 -2,-0.895 -2,-2s0.895,-2 2,-2z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:centerX="32" android:centerY="31.500021"
|
||||
android:gradientRadius="32" android:tileMode="mirror" android:type="radial">
|
||||
<item android:color="#1F0FCCFF" android:offset="0"/>
|
||||
<item android:color="#1F0FCEFF" android:offset="0.193"/>
|
||||
<item android:color="#1F0FD5FF" android:offset="0.703"/>
|
||||
<item android:color="#1F0ECEFF" android:offset="1"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M51.483,15.326l3.937,-3.93c1.24,-1.25 0.36,-3.38 -1.4,-3.38H42c-1.1,0 -2,0.9 -2,2v12c0,1.76 2.13,2.65 3.38,1.4l4.537,-4.529C50.532,22.313 52,26.533 52,31c0,11.215 -8.565,20 -19.5,20c-1.381,0 -2.5,1.119 -2.5,2.5s1.119,2.5 2.5,2.5C46.238,56 57,45.019 57,31C57,25.183 55.012,19.699 51.483,15.326z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:endX="43.5" android:endY="-21.15"
|
||||
android:startX="43.5" android:startY="59.034"
|
||||
android:tileMode="mirror" android:type="linear">
|
||||
<item android:color="#FFFF634D" android:offset="0"/>
|
||||
<item android:color="#FFFE6464" android:offset="0.204"/>
|
||||
<item android:color="#FFFC6581" android:offset="0.521"/>
|
||||
<item android:color="#FFFA6694" android:offset="0.794"/>
|
||||
<item android:color="#FFFA669A" android:offset="0.989"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:pathData="M20.604,38.58l-4.523,4.53C13.468,39.683 12,35.463 12,31c0,-11.215 8.565,-20 19.5,-20c1.381,0 2.5,-1.119 2.5,-2.5S32.881,6 31.5,6C17.762,6 7,16.981 7,31c0,5.815 1.989,11.303 5.52,15.677L8.584,50.62c-1.25,1.25 -0.36,3.38 1.4,3.38h12c1.1,0 2,-0.9 2,-2V39.98C23.984,38.22 21.854,37.34 20.604,38.58z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:endX="20.5" android:endY="-24.844"
|
||||
android:startX="20.5" android:startY="55.227"
|
||||
android:tileMode="mirror" android:type="linear">
|
||||
<item android:color="#FFFF634D" android:offset="0"/>
|
||||
<item android:color="#FFFE6464" android:offset="0.204"/>
|
||||
<item android:color="#FFFC6581" android:offset="0.521"/>
|
||||
<item android:color="#FFFA6694" android:offset="0.794"/>
|
||||
<item android:color="#FFFA669A" android:offset="0.989"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
202
app/src/main/res/drawable/ic_timetable.xml
Normal file
202
app/src/main/res/drawable/ic_timetable.xml
Normal file
@ -0,0 +1,202 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="m118,62.129h2.605c3.789,0 7.188,-2.84 7.383,-6.625 0.211,-4.035 -3,-7.375 -6.988,-7.375h-4.805c-1.965,0 -3.785,-1.328 -4.129,-3.262 -0.043,-0.25 -0.066,-0.5 -0.066,-0.746 0.004,-2.168 1.734,-3.93 3.887,-3.988 3.023,-0.082 5.731,-2.293 6.07,-5.297 0.027,-0.242 0.039,-0.477 0.039,-0.711 0,-3.313 -2.688,-5.996 -6,-5.996h-4.602c-0.434,0 -4.863,-0.035 -5.281,-0.105 -0.035,-0.008 -0.074,-0.016 -0.113,-0.023v18h-42v-18h25.715c0.438,-1.688 0.539,-3.512 -0.945,-5.719 -1.836,-2.742 -5.023,-4.281 -8.324,-4.281h-9.445c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h0.66c3.25,0 6.16,-2.434 6.332,-5.68 0.18,-3.461 -2.57,-6.32 -5.992,-6.32h-57.66c-3.25,0 -6.16,2.434 -6.332,5.68 -0.18,3.457 2.57,6.32 5.992,6.32h15c1.656,0 3,1.344 3,3s-1.344,3 -3,3h-17c-4.418,0 -8,3.582 -8,8s3.582,8 8,8h26v12h-14l2,21.109c-1.07,0.801 -1.836,1.98 -1.973,3.375 -0.184,1.84 0.633,3.5 1.973,4.504v5.047c-0.027,0.148 -0.043,0.297 -0.027,0.449 0.301,2.992 -2.039,5.516 -4.973,5.516h-14.66c-2.984,0 -5.762,2.023 -6.25,4.965 -0.246,1.465 0.043,2.848 0.684,4.012 1.074,1.938 3.234,3.023 5.449,3.023h3.164c2.375,0 4.207,1.328 4.551,3.27 0.039,0.246 0.063,0.488 0.063,0.723 0.004,2.215 -1.789,4.008 -3.996,4.008h-0.004c-2.27,0 -4.473,1.203 -5.398,3.277 -1.98,4.426 1.207,8.723 5.398,8.723h62c3.313,0 6,-2.688 6,-6s-2.688,-6 -6,-6h-10v-20l46,0.129h9.66c3.141,0 6.168,-2.539 6.328,-5.676 0.184,-3.461 -2.57,-6.324 -5.992,-6.324h-0.023,-0.035c-1.555,0 -3.078,-0.508 -4.156,-1.535 -0.34,-0.324 -0.637,-0.699 -0.875,-1.129 -2.574,-4.637 0.711,-9.336 5.094,-9.336z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="80.322"
|
||||
android:centerX="60.334"
|
||||
android:centerY="65.146"
|
||||
android:type="radial"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#1F0FCCFF"/>
|
||||
<item android:offset="0.1927" android:color="#1F0FCEFF"/>
|
||||
<item android:offset="0.7025" android:color="#1F0FD5FF"/>
|
||||
<item android:offset="1" android:color="#1F0FD7FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m18,100v-60h92v60c0,5.523 -4.477,10 -10,10h-72c-5.523,0 -10,-4.477 -10,-10z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="110"
|
||||
android:startX="64"
|
||||
android:endY="40"
|
||||
android:endX="64"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFFC662"/>
|
||||
<item android:offset="0.0036" android:color="#FFFFC662"/>
|
||||
<item android:offset="0.6085" android:color="#FFFFC582"/>
|
||||
<item android:offset="1" android:color="#FFFFC491"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m110,29.602v16.398h-92v-16.398c0,-5.309 4.332,-9.602 9.684,-9.602h72.633c5.352,0 9.684,4.293 9.684,9.602">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="46"
|
||||
android:startX="64"
|
||||
android:endY="20"
|
||||
android:endX="64"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFF634D"/>
|
||||
<item android:offset="0.2083" android:color="#FFFD6464"/>
|
||||
<item android:offset="0.5223" android:color="#FFFC6582"/>
|
||||
<item android:offset="0.7935" android:color="#FFFA6694"/>
|
||||
<item android:offset="0.9892" android:color="#FFFA669A"/>
|
||||
<item android:offset="1" android:color="#FFFA669A"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m49.309,20h-12.621c-2.832,1.988 -4.688,5.277 -4.688,9 0,6.07 4.93,11 11,11 6.07,0 11,-4.93 11,-11 0,-3.723 -1.855,-7.012 -4.691,-9z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="40"
|
||||
android:startX="43"
|
||||
android:endY="20"
|
||||
android:endX="43"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFF5840"/>
|
||||
<item android:offset="0.0072" android:color="#FFFF5840"/>
|
||||
<item android:offset="0.9892" android:color="#FFFA528C"/>
|
||||
<item android:offset="1" android:color="#FFFA528C"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m43,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="12"
|
||||
android:startX="43"
|
||||
android:endY="34"
|
||||
android:endX="43"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFA4A4A4"/>
|
||||
<item android:offset="0.6301" android:color="#FF7F7F7F"/>
|
||||
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m91.309,20h-12.621c-2.832,1.988 -4.688,5.277 -4.688,9 0,6.07 4.93,11 11,11 6.07,0 11,-4.93 11,-11 0,-3.723 -1.855,-7.012 -4.691,-9z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="40"
|
||||
android:startX="85"
|
||||
android:endY="20"
|
||||
android:endX="85"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFF5840"/>
|
||||
<item android:offset="0.0072" android:color="#FFFF5840"/>
|
||||
<item android:offset="0.9892" android:color="#FFFA528C"/>
|
||||
<item android:offset="1" android:color="#FFFA528C"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m85,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="12"
|
||||
android:startX="85"
|
||||
android:endY="34"
|
||||
android:endX="85"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFA4A4A4"/>
|
||||
<item android:offset="0.6301" android:color="#FF7F7F7F"/>
|
||||
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m78,81.008c0,2.762 2.238,5 5,4.996h2.992c3.316,-0.008 6.008,2.684 6.008,5.996s-2.688,6 -6,6h-8c-3.313,0 -6,2.688 -6,6s2.688,6 6,6h22c5.523,0 10,-4.477 10,-10v-24l-27.008,0.012c-2.758,-0 -4.992,2.234 -4.992,4.996z"
|
||||
android:fillColor="#ffb86a"/>
|
||||
<path
|
||||
android:pathData="m18,68h17.785c1.992,0 3.84,-1.363 4.16,-3.328 0.406,-2.508 -1.516,-4.672 -3.945,-4.672h-3c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h14.785c1.992,0 3.84,-1.363 4.16,-3.328 0.406,-2.508 -1.516,-4.672 -3.945,-4.672h-30z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="108.25"
|
||||
android:startX="35"
|
||||
android:endY="43.742"
|
||||
android:endX="35"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFFCE76"/>
|
||||
<item android:offset="0.0036" android:color="#FFFFCE76"/>
|
||||
<item android:offset="0.6054" android:color="#FFFFCD92"/>
|
||||
<item android:offset="1" android:color="#FFFFCCA0"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m98,62c0,3.313 -2.688,6 -6,6s-6,-2.688 -6,-6 2.688,-6 6,-6 6,2.688 6,6z"
|
||||
android:fillColor="#ffb977"/>
|
||||
<path
|
||||
android:pathData="m48,74h-8c-2.211,0 -4,-1.789 -4,-4v-8c0,-2.211 1.789,-4 4,-4h8c2.211,0 4,1.789 4,4v8c0,2.211 -1.789,4 -4,4zM72,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM52,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM72,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="94"
|
||||
android:startX="64"
|
||||
android:endY="58"
|
||||
android:endX="64"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FFFFE79F"/>
|
||||
<item android:offset="0.1186" android:color="#FFFFE9A6"/>
|
||||
<item android:offset="1" android:color="#FFFFF5D5"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m124,104c0,11.047 -8.953,20 -20,20 -11.047,0 -20,-8.953 -20,-20 0,-11.047 8.953,-20 20,-20 11.047,0 20,8.953 20,20z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="m104,80c-13.254,0 -24,10.746 -24,24 0,13.254 10.746,24 24,24 13.254,0 24,-10.746 24,-24 0,-13.254 -10.746,-24 -24,-24zM104,120c-8.836,0 -16,-7.164 -16,-16 0,-8.836 7.164,-16 16,-16 8.836,0 16,7.164 16,16 0,8.836 -7.164,16 -16,16z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="128"
|
||||
android:startX="104"
|
||||
android:endY="80"
|
||||
android:endX="104"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FF155CDE"/>
|
||||
<item android:offset="0.6248" android:color="#FF2289E7"/>
|
||||
<item android:offset="1" android:color="#FF289FEC"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m113.41,110.59 l-5.563,-5.563c0.086,-0.328 0.148,-0.668 0.148,-1.023 0,-1.477 -0.809,-2.754 -2,-3.445v-6.555c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2v6.555c-1.191,0.691 -2,1.969 -2,3.445 0,2.211 1.789,4 4,4 0.355,0 0.695,-0.063 1.023,-0.148l5.563,5.563c0.391,0.391 0.902,0.586 1.414,0.586s1.023,-0.195 1.414,-0.586c0.781,-0.781 0.781,-2.047 0,-2.828z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="92"
|
||||
android:startX="107"
|
||||
android:endY="114"
|
||||
android:endX="107"
|
||||
android:type="linear"
|
||||
android:tileMode="mirror">
|
||||
<item android:offset="0" android:color="#FF919191"/>
|
||||
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
11
app/src/main/res/drawable/timetable_lesson_annotation.xml
Normal file
11
app/src/main/res/drawable/timetable_lesson_annotation.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:shape="rectangle">
|
||||
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
|
||||
<solid android:color="#2196f3" tools:color="?timetable_lesson_change_color" />
|
||||
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape android:shape="rectangle"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/colorSurface_4dp" />
|
||||
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape android:shape="rectangle"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="4dp" />
|
||||
<stroke android:width="1dp" android:color="#1e000000" />
|
||||
</shape>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||
-->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
|
||||
<solid android:color="#e91e63" />
|
||||
</shape>
|
@ -36,24 +36,54 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="visible"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/getLogs"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="Save Debug Logs" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/librusCaptchaButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="Librus Captcha" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/runChucker"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="Launch Chucker" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/mobidziennikMessagesSwitch"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Zmień moduł wiadomości" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/composeButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="Compose" />
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -151,6 +151,16 @@
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/fakeLogin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:text="Fake login" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
90
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
90
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/timetableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:visibility="gone">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorSurface"
|
||||
style="@style/Widget.MaterialComponents.AppBarLayout.Surface">
|
||||
|
||||
<com.nshmura.recyclertablayout.RecyclerTabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@color/colorSurface_6dp"
|
||||
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab"
|
||||
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||
app:rtl_tabMinWidth="90dp"
|
||||
app:rtl_tabMaxWidth="300dp"
|
||||
app:rtl_tabSelectedTextColor="?colorPrimary"
|
||||
app:rtl_tabPaddingStart="16dp"
|
||||
app:rtl_tabPaddingEnd="16dp"
|
||||
app:rtl_tabPaddingTop="12dp"
|
||||
app:rtl_tabPaddingBottom="12dp"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timetableNotPublicLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_no_timetable" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_not_public_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_not_public_hint"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
153
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
153
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/dayScroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.linkedin.android.tachyon.DayView
|
||||
android:id="@+id/day"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
app:dividerHeight="1dp"
|
||||
app:endHour="18"
|
||||
app:eventMargin="2dp"
|
||||
app:halfHourDividerColor="#e0e0e0"
|
||||
app:halfHourHeight="60dp"
|
||||
app:hourDividerColor="#b0b0b0"
|
||||
app:hourLabelMarginEnd="10dp"
|
||||
app:hourLabelWidth="40dp"
|
||||
app:startHour="5"
|
||||
tools:visibility="gone"/>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noLessonsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_timetable" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_lessons_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/freeDayLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_sunbed" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_free_day_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_text"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/freeDayText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
tools:text="Dzień wolny dla szkoły z puli dyrektorskiej z okazji obchodów Światowego Dnia Wtorku w mieście Poznań i na przedmieśiach"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/freeDayShowTimetable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_free_day_show" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/noTimetableLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:gravity="center"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_sync" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/timetable_no_timetable_title"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_no_timetable_text"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/noTimetableSync"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/timetable_no_timetable_sync" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noTimetableWeek"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/timetable_no_timetable_week"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2019 LinkedIn Corporation -->
|
||||
<!-- All Rights Reserved. -->
|
||||
<!-- -->
|
||||
<!-- Licensed under the BSD 2-Clause License (the "License"). See License in the project root -->
|
||||
<!-- for license information. -->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/Base.TextAppearance.AppCompat.Small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAlignment="viewEnd"
|
||||
tools:text="1 PM" />
|
139
app/src/main/res/layout/timetable_lesson.xml
Normal file
139
app/src/main/res/layout/timetable_lesson.xml
Normal file
@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="annotationVisible"
|
||||
type="boolean"/>
|
||||
<variable
|
||||
name="lessonNumber"
|
||||
type="Integer" />
|
||||
</data>
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:colorBackground"
|
||||
android:foreground="@drawable/bg_rounded_ripple_4dp"
|
||||
tools:padding="32dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout_height="90dp"
|
||||
android:background="?timetable_lesson_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/annotation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/timetable_lesson_annotation"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/timetable_lesson_cancelled"
|
||||
android:textColor="#000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
android:visibility="@{annotationVisible ? View.VISIBLE : View.GONE}"
|
||||
tools:text="Zastępstwo: zamiast lekcji język polski z Adam Dodatkowy"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
<!--tools:background="@drawable/timetable_subject_color_rounded"-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subjectName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:maxLines="@{annotationVisible ? 1 : 2}"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeMaxTextSize="16sp"
|
||||
app:autoSizeMinTextSize="12sp"
|
||||
app:autoSizeTextType="uniform"
|
||||
tools:maxLines="2"
|
||||
tools:text="pracownia urządzeń techniki komputerowej" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/attendanceIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
tools:srcCompat="@sample/check"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView4"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_weight="0"
|
||||
app:srcCompat="@drawable/bg_circle"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView6"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:fontFamily="sans-serif-condensed-light"
|
||||
android:includeFontPadding="false"
|
||||
android:layout_marginBottom="-4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:text="@{Integer.toString(lessonNumber)}"
|
||||
android:textSize="28sp"
|
||||
android:visibility="@{lessonNumber != null ? View.VISIBLE : View.GONE}"
|
||||
tools:text="3"/>
|
||||
<!--android:layout_marginTop="@{annotationVisible ? `-4dp` : `4dp`}"
|
||||
android:layout_marginBottom="@{annotationVisible ? `-4dp` : `0dp`}"-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailsFirst"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="8:10 - 8:55 • 015 językowa → 016 językowa" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailsSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
android:textSize="12sp"
|
||||
tools:text="Paweł Informatyczny • 2b3T n1" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</layout>
|
@ -3,4 +3,9 @@
|
||||
<attr name="colorSection" format="color" />
|
||||
<attr name="cardBackgroundDimmed" format="color" />
|
||||
<attr name="cardBackgroundHighlight" format="color" />
|
||||
<attr name="timetable_lesson_bg" format="reference" />
|
||||
<attr name="timetable_lesson_cancelled_color" format="color" />
|
||||
<attr name="timetable_lesson_change_color" format="color" />
|
||||
<attr name="timetable_lesson_shifted_source_color" format="color" />
|
||||
<attr name="timetable_lesson_shifted_target_color" format="color" />
|
||||
</resources>
|
@ -930,6 +930,7 @@
|
||||
<string name="settings_register_show_teacher_absences_text">Pokazuj nieobecności nauczycieli w Terminarzu</string>
|
||||
<string name="edziennik_progress_endpoint_school_info">Pobieram informacje o szkole...</string>
|
||||
<string name="edziennik_progress_endpoint_grades">Pobieranie ocen ucznia...</string>
|
||||
<string name="edziennik_progress_endpoint_grade_categories">Pobieranie kategorii ocen...</string>
|
||||
<string name="edziennik_progress_login_template_web">Logowanie do Template WEB...</string>
|
||||
<string name="edziennik_progress_login_template_api">Logowanie do Template API...</string>
|
||||
<string name="edziennik_progress_login_mobidziennik_web">Logowanie do MobiDziennika...</string>
|
||||
@ -983,4 +984,26 @@
|
||||
<string name="dont_ask_again">Nie pytaj ponownie</string>
|
||||
<string name="app_manager_open_failed">Nie udało się otworzyć ustawień</string>
|
||||
<string name="edziennik_notification_api_notify_title">Tworzenie powiadomień</string>
|
||||
<string name="login_librus_captcha_title">Librus - logowanie</string>
|
||||
<string name="timetable_today">Dzisiaj</string>
|
||||
<string name="timetable_lesson_cancelled">Lekcja odwołana</string>
|
||||
<string name="timetable_lesson_change">Zastępstwo</string>
|
||||
<string name="timetable_lesson_change_format">Zastępstwo: zamiast %s</string>
|
||||
<string name="timetable_lesson_shifted_same_day">Lekcja przeniesiona na godz. %s</string>
|
||||
<string name="timetable_lesson_shifted_other_day">Lekcja przeniesiona na %s, godz. %s</string>
|
||||
<string name="timetable_lesson_shifted_from_same_day">Lekcja przeniesiona z godz. %s</string>
|
||||
<string name="timetable_lesson_shifted_from_other_day">Lekcja przeniesiona z dnia %s, godz. %s</string>
|
||||
<string name="timetable_lesson_shifted">Lekcja przeniesiona na inny termin</string>
|
||||
<string name="timetable_lesson_shifted_from">Lekcja przeniesiona z innego terminu</string>
|
||||
<string name="timetable_not_public_title">Brak planu lekcji</string>
|
||||
<string name="timetable_not_public_text">Plan lekcji nie został opublikowany przez szkołę.</string>
|
||||
<string name="timetable_not_public_hint">Skontaktuj się z wychowawcą w celu udostępnienia planu lekcji.</string>
|
||||
<string name="timetable_free_day_title">Dzień wolny</string>
|
||||
<string name="timetable_free_day_text">W tym dniu nie ma lekcji:</string>
|
||||
<string name="timetable_free_day_show">Pokaż plan lekcji</string>
|
||||
<string name="timetable_no_lessons_title">Brak lekcji tego dnia</string>
|
||||
<string name="timetable_no_timetable_title">Brak planu lekcji</string>
|
||||
<string name="timetable_no_timetable_text">Nie pobrano planu lekcji na ten tydzień.</string>
|
||||
<string name="timetable_no_timetable_sync">Pobierz plan lekcji</string>
|
||||
<string name="timetable_no_timetable_week">na tydzień %s</string>
|
||||
</resources>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources>
|
||||
|
||||
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
@ -95,6 +95,12 @@
|
||||
<item name="mal_color_secondary">?android:textColorSecondary</item>
|
||||
<item name="mal_card_background">?colorSurface</item>
|
||||
<item name="mal_divider_color">@color/dividerColor</item>
|
||||
|
||||
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_light</item>
|
||||
<item name="timetable_lesson_cancelled_color">#9f9f9f</item>
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
</style>
|
||||
<style name="AppTheme.Dark" parent="NavView.Dark">
|
||||
<item name="colorPrimary">#64b5f6</item>
|
||||
@ -119,6 +125,12 @@
|
||||
<item name="mal_color_secondary">@color/secondaryTextDark</item>
|
||||
<item name="mal_card_background">?colorSurface</item>
|
||||
<item name="mal_divider_color">@color/dividerColor</item>
|
||||
|
||||
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_dark</item>
|
||||
<item name="timetable_lesson_cancelled_color">#838383</item>
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
@ -5,8 +5,8 @@ buildscript {
|
||||
kotlin_version = '1.3.50'
|
||||
|
||||
release = [
|
||||
versionName: "3.1.1",
|
||||
versionCode: 3010199
|
||||
versionName: "3.9.4-dev",
|
||||
versionCode: 3090400
|
||||
]
|
||||
|
||||
setup = [
|
||||
|
Reference in New Issue
Block a user