forked from github/szkolny
Compare commits
57 Commits
v3.9.4-dev
...
v3.9.10-de
Author | SHA1 | Date | |
---|---|---|---|
19bc2b8b37 | |||
673116e27e | |||
59fcb0a050 | |||
cd76f99bbf | |||
6a4994b9c2 | |||
63960c5e05 | |||
540afb6a28 | |||
ae10b8abbd | |||
db2ebab879 | |||
6ec3d062df | |||
86b6060a09 | |||
83d123e341 | |||
34061695f9 | |||
de68476442 | |||
678a81a44b | |||
cfb3096d53 | |||
9b7aca745a | |||
82852389fa | |||
ce06084e6f | |||
3ca051983f | |||
cd379e4175 | |||
62fdfa2b6f | |||
9866017f7e | |||
67f98b08c6 | |||
fdb5f7ec02 | |||
04c3c7ca6e | |||
f424315d97 | |||
c907a8df37 | |||
37ea65e3fc | |||
a3e5f824c8 | |||
e0c850a455 | |||
1c6815f708 | |||
9a20511935 | |||
965f5e73d9 | |||
13b58a1d56 | |||
0a3261b8b3 | |||
dbdfc7fdd8 | |||
56062f5bfa | |||
0cbba2eb45 | |||
aa84356dd6 | |||
efaad0a4dd | |||
71015c0137 | |||
85b5667a7e | |||
dfdc6817a1 | |||
058345b9c9 | |||
831b7876b4 | |||
729cf6f08e | |||
860a16b32d | |||
9f871c077b | |||
a8f89abf7d | |||
16102de619 | |||
472e768369 | |||
131a769c26 | |||
4a0a6c54e4 | |||
c83abe57d5 | |||
c6e2519dcc | |||
74db524db6 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -81,3 +81,5 @@ lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
app/schemas/
|
||||
|
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@ -6,8 +6,9 @@
|
||||
</configurations>
|
||||
</component>
|
||||
<component name="EntryPointsManager">
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
|
||||
<list size="2">
|
||||
<item index="0" class="java.lang.String" itemvalue="androidx.databinding.BindingAdapter" />
|
||||
<item index="1" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
|
@ -131,7 +131,7 @@ dependencies {
|
||||
implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true}
|
||||
implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update
|
||||
implementation "com.jaredrummler:colorpicker:1.0.2"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.12.0"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.12.2"
|
||||
implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update
|
||||
implementation "com.wdullaer:materialdatetimepicker:4.1.2"
|
||||
implementation "com.yuyh.json:jsonviewer:1.0.6"
|
||||
@ -169,7 +169,7 @@ dependencies {
|
||||
|
||||
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
||||
|
||||
implementation 'com.linkedin.android.tachyon:tachyon:1.0.2'
|
||||
implementation 'com.github.kuba2k2:Tachyon:551943a6b5'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -39,4 +39,13 @@
|
||||
|
||||
-keep class okhttp3.** { *; }
|
||||
|
||||
-keep class com.google.android.material.tabs.** {*;}
|
||||
-keep class com.google.android.material.tabs.** {*;}
|
||||
|
||||
# ServiceLoader support
|
||||
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
|
||||
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
|
||||
|
||||
# Most of volatile fields are updated with AFU and should not be mangled
|
||||
-keepclassmembernames class kotlinx.** {
|
||||
volatile <fields>;
|
||||
}
|
@ -105,6 +105,11 @@
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
<activity android:name=".widgets.timetable.LessonDialogActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
<activity
|
||||
android:name=".ui.modules.settings.SettingsLicenseActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
|
@ -68,11 +68,6 @@ import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.TlsVersion;
|
||||
import pl.szczodrzynski.edziennik.data.api.Edziennik;
|
||||
import pl.szczodrzynski.edziennik.data.api.Iuczniowie;
|
||||
import pl.szczodrzynski.edziennik.data.api.Librus;
|
||||
import pl.szczodrzynski.edziennik.data.api.Mobidziennik;
|
||||
import pl.szczodrzynski.edziennik.data.api.Vulcan;
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore;
|
||||
@ -141,12 +136,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
|
||||
public PersistentCookieJar cookieJar;
|
||||
public OkHttpClient http;
|
||||
public OkHttpClient httpLazy;
|
||||
//public Jakdojade apiJakdojade;
|
||||
public Edziennik apiEdziennik;
|
||||
public Mobidziennik apiMobidziennik;
|
||||
public Iuczniowie apiIuczniowie;
|
||||
public Librus apiLibrus;
|
||||
public Vulcan apiVulcan;
|
||||
|
||||
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
|
||||
public AppConfig appConfig; // APPCONFIG: common for all profiles
|
||||
@ -204,12 +193,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
|
||||
db = AppDb.getDatabase(this);
|
||||
gson = new Gson();
|
||||
networkUtils = new NetworkUtils(this);
|
||||
apiEdziennik = new Edziennik(this);
|
||||
//apiJakdojade = new Jakdojade(this);
|
||||
apiMobidziennik = new Mobidziennik(this);
|
||||
apiIuczniowie = new Iuczniowie(this);
|
||||
apiLibrus = new Librus(this);
|
||||
apiVulcan = new Vulcan(this);
|
||||
|
||||
Iconics.init(getApplicationContext());
|
||||
Iconics.registerFont(SzkolnyFont.INSTANCE);
|
||||
|
21
app/src/main/java/pl/szczodrzynski/edziennik/Binding.java
Normal file
21
app/src/main/java/pl/szczodrzynski/edziennik/Binding.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.databinding.BindingAdapter;
|
||||
|
||||
public class Binding {
|
||||
@BindingAdapter("strikeThrough")
|
||||
public static void strikeThrough(TextView textView, Boolean strikeThrough) {
|
||||
if (strikeThrough) {
|
||||
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
} else {
|
||||
textView.setPaintFlags(textView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,17 +4,27 @@ import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.*
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.util.forEach
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
@ -89,6 +99,15 @@ fun String.swapFirstLastName(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getFirstLastName(): Pair<String, String>? {
|
||||
return this.split(" ").let {
|
||||
if (it.size >= 2) Pair(it[0], it[1])
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getLastFirstName() = this.getFirstLastName()
|
||||
|
||||
fun changeStringCase(s: String): String {
|
||||
val delimiters = " '-/"
|
||||
val sb = StringBuilder()
|
||||
@ -147,8 +166,9 @@ fun colorFromName(context: Context, name: String?): Int {
|
||||
return context.getColorFromRes(color)
|
||||
}
|
||||
|
||||
fun MutableList<out Profile>.filterOutArchived() {
|
||||
fun MutableList<Profile>.filterOutArchived(): MutableList<Profile> {
|
||||
this.removeAll { it.archived }
|
||||
return this
|
||||
}
|
||||
|
||||
fun Activity.isStoragePermissionGranted(): Boolean {
|
||||
@ -343,6 +363,11 @@ fun CharSequence?.asStrikethroughSpannable(): Spannable {
|
||||
spannable.setSpan(StrikethroughSpan(), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence?.asItalicSpannable(): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(StyleSpan(Typeface.ITALIC), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new read-only list only of those given elements, that are not empty.
|
||||
@ -408,4 +433,73 @@ fun JsonObject(vararg properties: Pair<String, Any>): JsonObject {
|
||||
}
|
||||
|
||||
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
|
||||
setOnClickListener { v: View ->
|
||||
onClickListener(v as T)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
|
||||
setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
onChangeListener(buttonView as T, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
|
||||
observe(lifecycleOwner, object : Observer<T> {
|
||||
override fun onChanged(t: T?) {
|
||||
observer.onChanged(t)
|
||||
removeObserver(this)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value in dp to pixels.
|
||||
*/
|
||||
val Int.dp: Int
|
||||
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||
/**
|
||||
* Convert a value in pixels to dp.
|
||||
*/
|
||||
val Int.px: Int
|
||||
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
||||
|
||||
@ColorInt
|
||||
fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int {
|
||||
val typedValue = TypedValue()
|
||||
context?.theme?.resolveAttribute(this, typedValue, true)
|
||||
return typedValue.data
|
||||
}
|
||||
@ColorInt
|
||||
fun @receiver:ColorRes Int.resolveColor(context: Context): Int {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
context.resources.getColor(this, context.theme)
|
||||
}
|
||||
else {
|
||||
context.resources.getColor(this)
|
||||
}
|
||||
}
|
||||
fun @receiver:DrawableRes Int.resolveDrawable(context: Context): Drawable {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
context.resources.getDrawable(this, context.theme)
|
||||
}
|
||||
else {
|
||||
context.resources.getDrawable(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.findParentById(targetId: Int): View? {
|
||||
if (id == targetId) {
|
||||
return this
|
||||
}
|
||||
val viewParent = this.parent ?: return null
|
||||
if (viewParent is View) {
|
||||
return viewParent.findParentById(targetId)
|
||||
}
|
||||
return null
|
||||
}
|
@ -38,18 +38,20 @@ import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App.APP_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest
|
||||
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
|
||||
@ -57,7 +59,7 @@ import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesDetailsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
@ -122,7 +124,7 @@ class MainActivity : AppCompatActivity() {
|
||||
// home item
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class)
|
||||
.withTitle(R.string.app_name)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.withPopToHome(false)
|
||||
@ -133,50 +135,50 @@ class MainActivity : AppCompatActivity() {
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, AgendaFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_outline)
|
||||
.withBadgeTypeId(TYPE_EVENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
|
||||
.withBadgeTypeId(TYPE_GRADE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_email)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_email_outline)
|
||||
.withBadgeTypeId(TYPE_MESSAGE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_HOMEWORK, R.string.menu_homework, HomeworkFragment::class)
|
||||
.withIcon(SzkolnyFont.Icon.szf_file_document_edit)
|
||||
.withIcon(SzkolnyFont.Icon.szf_notebook_outline)
|
||||
.withBadgeTypeId(TYPE_HOMEWORK)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_BEHAVIOUR, R.string.menu_notices, BehaviourFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_message_alert)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_emoticon_outline)
|
||||
.withBadgeTypeId(TYPE_NOTICE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ATTENDANCE, R.string.menu_attendance, AttendanceFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove_outline)
|
||||
.withBadgeTypeId(TYPE_ATTENDANCE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, AnnouncementsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bulletin_board)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bullhorn_outline)
|
||||
.withBadgeTypeId(TYPE_ANNOUNCEMENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
|
||||
// static drawer items
|
||||
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
@ -195,7 +197,7 @@ class MainActivity : AppCompatActivity() {
|
||||
.isInProfileList(false)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.isInProfileList(true)
|
||||
|
||||
|
||||
@ -203,7 +205,7 @@ class MainActivity : AppCompatActivity() {
|
||||
list += NavTarget(TARGET_GRADES_EDITOR, R.string.menu_grades_editor, GradesEditorFragment::class)
|
||||
list += NavTarget(TARGET_HELP, R.string.menu_help, HelpFragment::class)
|
||||
list += NavTarget(TARGET_FEEDBACK, R.string.menu_feedback, FeedbackFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessagesDetailsFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class)
|
||||
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
||||
|
||||
list
|
||||
@ -214,6 +216,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val navView: NavView by lazy { b.navView }
|
||||
val drawer: NavDrawer by lazy { navView.drawer }
|
||||
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
|
||||
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
|
||||
|
||||
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
||||
|
||||
@ -246,6 +249,8 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(b.root)
|
||||
|
||||
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
|
||||
navLoading = true
|
||||
|
||||
b.navView.apply {
|
||||
@ -345,7 +350,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (!profileListEmpty) {
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
app.db.profileDao().getAllFull().observe(this, Observer { profiles ->
|
||||
app.db.profileDao().allFull.observe(this, Observer { profiles ->
|
||||
// TODO fix weird -1 profiles ???
|
||||
profiles.removeAll { it.id < 0 }
|
||||
drawer.setProfileList(profiles)
|
||||
@ -362,7 +367,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (app.profile != null)
|
||||
setDrawerItems()
|
||||
|
||||
app.db.metadataDao().getUnreadCounts().observe(this, Observer { unreadCounters ->
|
||||
app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters ->
|
||||
unreadCounters.map {
|
||||
it.type = it.thingType
|
||||
}
|
||||
@ -371,6 +376,11 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
b.swipeRefreshLayout.isEnabled = true
|
||||
b.swipeRefreshLayout.setOnRefreshListener { this.syncCurrentFeature() }
|
||||
b.swipeRefreshLayout.setColorSchemeResources(
|
||||
R.color.md_blue_500,
|
||||
R.color.md_amber_500,
|
||||
R.color.md_green_500
|
||||
)
|
||||
|
||||
isStoragePermissionGranted()
|
||||
|
||||
@ -424,7 +434,7 @@ class MainActivity : AppCompatActivity() {
|
||||
navView.coordinator.postDelayed({
|
||||
CafeBar.builder(this)
|
||||
.content(R.string.rate_snackbar_text)
|
||||
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
|
||||
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
|
||||
.positiveText(R.string.rate_snackbar_positive)
|
||||
.positiveColor(-0xb350b0)
|
||||
.negativeText(R.string.rate_snackbar_negative)
|
||||
@ -461,25 +471,25 @@ class MainActivity : AppCompatActivity() {
|
||||
bottomSheet.appendItems(
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_sync)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
bottomSheet.close()
|
||||
app.apiEdziennik.guiSyncFeature(app, this, App.profileId, R.string.sync_dialog_title, R.string.sync_dialog_text, R.string.sync_done, fragmentToFeature(navTargetId))
|
||||
SyncViewListDialog(this, navTargetId)
|
||||
}),
|
||||
BottomSheetSeparatorItem(false),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_feedback)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_help_circle)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
|
||||
)
|
||||
if (App.devMode) {
|
||||
bottomSheet += BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_debug)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
|
||||
}
|
||||
|
||||
@ -527,9 +537,16 @@ class MainActivity : AppCompatActivity() {
|
||||
DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection
|
||||
else -> 0
|
||||
}
|
||||
val arguments = when (navTargetId) {
|
||||
DRAWER_ITEM_TIMETABLE -> JsonObject().apply {
|
||||
addProperty("weekStart", TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
listOf(navTargetId to fragmentParam)
|
||||
listOf(navTargetId to fragmentParam),
|
||||
arguments
|
||||
).enqueue(this)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@ -573,7 +590,12 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
|
||||
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
|
||||
subtitle = "Gotowe"
|
||||
}
|
||||
errorSnackbar.addError(event.error).show()
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) {
|
||||
@ -605,22 +627,7 @@ class MainActivity : AppCompatActivity() {
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
private fun fragmentToFeature(currentFragment: Int): Int {
|
||||
return when (currentFragment) {
|
||||
DRAWER_ITEM_TIMETABLE -> FEATURE_TIMETABLE
|
||||
DRAWER_ITEM_AGENDA -> FEATURE_AGENDA
|
||||
DRAWER_ITEM_GRADES -> FEATURE_GRADES
|
||||
DRAWER_ITEM_HOMEWORK -> FEATURE_HOMEWORK
|
||||
DRAWER_ITEM_BEHAVIOUR -> FEATURE_NOTICES
|
||||
DRAWER_ITEM_ATTENDANCE -> FEATURE_ATTENDANCE
|
||||
DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
|
||||
1 -> FEATURE_MESSAGES_OUTBOX
|
||||
else -> FEATURE_MESSAGES_INBOX
|
||||
}
|
||||
DRAWER_ITEM_ANNOUNCEMENTS -> FEATURE_ANNOUNCEMENTS
|
||||
else -> FEATURE_ALL
|
||||
}
|
||||
}
|
||||
|
||||
private fun fragmentToSyncName(currentFragment: Int): Int {
|
||||
return when (currentFragment) {
|
||||
DRAWER_ITEM_TIMETABLE -> R.string.sync_feature_timetable
|
||||
@ -694,14 +701,15 @@ class MainActivity : AppCompatActivity() {
|
||||
app.profile == null -> {
|
||||
if (intentProfileId == -1)
|
||||
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentTargetId != -1 -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(intentTargetId, extras)
|
||||
if (navTargetId != intentTargetId)
|
||||
loadTarget(intentTargetId, extras)
|
||||
}
|
||||
else -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
@ -1042,7 +1050,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
loadTarget(DRAWER_ITEM_SETTINGS, null)
|
||||
} else if (item.itemId == 2) {
|
||||
app.apiEdziennik.guiRemoveProfile(this@MainActivity, profileId, profile.name?.getText(this).toString())
|
||||
ProfileRemoveDialog(this, profileId, profile.name?.getText(this)?.toString() ?: "?")
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -311,13 +311,14 @@ public class Notifier {
|
||||
\____/| .__/ \__,_|\__,_|\__\___||___/
|
||||
| |
|
||||
|*/
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename) {
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
|
||||
if (!app.appConfig.notifyAboutUpdates)
|
||||
return;
|
||||
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
|
||||
.putExtra("update_version", updateVersion)
|
||||
.putExtra("update_url", updateUrl)
|
||||
.putExtra("update_filename", updateFilename);
|
||||
.putExtra("update_filename", updateFilename)
|
||||
.putExtra("update_direct", updateDirect);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
@ -1,450 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
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.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week;
|
||||
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.LessonDetailsActivity;
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.WidgetTimetableService;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.ExtensionsKt.filterOutArchived;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
|
||||
|
||||
public class WidgetTimetable extends AppWidgetProvider {
|
||||
|
||||
|
||||
public static final String ACTION_SYNC_DATA = "ACTION_SYNC_DATA";
|
||||
private static final String TAG = "WidgetTimetable";
|
||||
private static int modeInt = 0;
|
||||
|
||||
public WidgetTimetable() {
|
||||
// Start the worker thread
|
||||
//HandlerThread sWorkerThread = new HandlerThread("WidgetTimetable-worker");
|
||||
//sWorkerThread.start();
|
||||
//Handler sWorkerQueue = new Handler(sWorkerThread.getLooper());
|
||||
}
|
||||
|
||||
public static SparseArray<List<ItemWidgetTimetableModel>> timetables = null;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_SYNC_DATA.equals(intent.getAction())) {
|
||||
EdziennikTask.Companion.sync().enqueue(context);
|
||||
}
|
||||
super.onReceive(context, intent);
|
||||
}
|
||||
|
||||
public static PendingIntent getPendingSelfIntent(Context context, String action) {
|
||||
Intent intent = new Intent(context, WidgetTimetable.class);
|
||||
intent.setAction(action);
|
||||
return getPendingSelfIntent(context, intent);
|
||||
}
|
||||
public static PendingIntent getPendingSelfIntent(Context context, Intent intent) {
|
||||
return PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
}
|
||||
|
||||
public static Bitmap drawableToBitmap (Drawable drawable) {
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable)drawable).getBitmap();
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
ComponentName thisWidget = new ComponentName(context, WidgetTimetable.class);
|
||||
|
||||
timetables = new SparseArray<>();
|
||||
//timetables.clear();
|
||||
|
||||
App app = (App)context.getApplicationContext();
|
||||
|
||||
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (int appWidgetId : allWidgetIds) {
|
||||
|
||||
//d(TAG, "thr "+Thread.currentThread().getName());
|
||||
|
||||
WidgetConfig widgetConfig = app.appConfig.widgetTimetableConfigs.get(appWidgetId);
|
||||
if (widgetConfig == null) {
|
||||
widgetConfig = new WidgetConfig(app.profileFirstId());
|
||||
app.appConfig.widgetTimetableConfigs.put(appWidgetId, widgetConfig);
|
||||
app.appConfig.savePending = true;
|
||||
}
|
||||
|
||||
RemoteViews views;
|
||||
if (widgetConfig.bigStyle) {
|
||||
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_timetable_dark_big : R.layout.widget_timetable_big);
|
||||
}
|
||||
else {
|
||||
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_timetable_dark : R.layout.widget_timetable);
|
||||
}
|
||||
|
||||
PorterDuff.Mode mode = PorterDuff.Mode.DST_IN;
|
||||
/*if (widgetConfig.darkTheme) {
|
||||
switch (modeInt) {
|
||||
case 0:
|
||||
mode = PorterDuff.Mode.ADD;
|
||||
d(TAG, "ADD");
|
||||
break;
|
||||
case 1:
|
||||
mode = PorterDuff.Mode.DST_ATOP;
|
||||
d(TAG, "DST_ATOP");
|
||||
break;
|
||||
case 2:
|
||||
mode = PorterDuff.Mode.DST_IN;
|
||||
d(TAG, "DST_IN");
|
||||
break;
|
||||
case 3:
|
||||
mode = PorterDuff.Mode.DST_OUT;
|
||||
d(TAG, "DST_OUT");
|
||||
break;
|
||||
case 4:
|
||||
mode = PorterDuff.Mode.DST_OVER;
|
||||
d(TAG, "DST_OVER");
|
||||
break;
|
||||
case 5:
|
||||
mode = PorterDuff.Mode.LIGHTEN;
|
||||
d(TAG, "LIGHTEN");
|
||||
break;
|
||||
case 6:
|
||||
mode = PorterDuff.Mode.MULTIPLY;
|
||||
d(TAG, "MULTIPLY");
|
||||
break;
|
||||
case 7:
|
||||
mode = PorterDuff.Mode.OVERLAY;
|
||||
d(TAG, "OVERLAY");
|
||||
break;
|
||||
case 8:
|
||||
mode = PorterDuff.Mode.SCREEN;
|
||||
d(TAG, "SCREEN");
|
||||
break;
|
||||
case 9:
|
||||
mode = PorterDuff.Mode.SRC_ATOP;
|
||||
d(TAG, "SRC_ATOP");
|
||||
break;
|
||||
case 10:
|
||||
mode = PorterDuff.Mode.SRC_IN;
|
||||
d(TAG, "SRC_IN");
|
||||
break;
|
||||
case 11:
|
||||
mode = PorterDuff.Mode.SRC_OUT;
|
||||
d(TAG, "SRC_OUT");
|
||||
break;
|
||||
case 12:
|
||||
mode = PorterDuff.Mode.SRC_OVER;
|
||||
d(TAG, "SRC_OVER");
|
||||
break;
|
||||
case 13:
|
||||
mode = PorterDuff.Mode.XOR;
|
||||
d(TAG, "XOR");
|
||||
break;
|
||||
default:
|
||||
modeInt = 0;
|
||||
mode = PorterDuff.Mode.ADD;
|
||||
d(TAG, "ADD");
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
// this code seems to crash the launcher on >= P
|
||||
float transparency = widgetConfig.opacity; //0...1
|
||||
long colorFilter = 0x01000000L * (long) (255f * transparency);
|
||||
try {
|
||||
final Method[] declaredMethods = Class.forName("android.widget.RemoteViews").getDeclaredMethods();
|
||||
final int len = declaredMethods.length;
|
||||
if (len > 0) {
|
||||
for (int m = 0; m < len; m++) {
|
||||
final Method method = declaredMethods[m];
|
||||
if (method.getName().equals("setDrawableParameters")) {
|
||||
method.setAccessible(true);
|
||||
method.invoke(views, R.id.widgetTimetableListView, true, -1, (int) colorFilter, mode, -1);
|
||||
method.invoke(views, R.id.widgetTimetableHeader, true, -1, (int) colorFilter, mode, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Intent refreshIntent = new Intent(context, WidgetTimetable.class);
|
||||
refreshIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
|
||||
PendingIntent pendingRefreshIntent = PendingIntent.getBroadcast(context,
|
||||
0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableRefresh, pendingRefreshIntent);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableSync, WidgetTimetable.getPendingSelfIntent(context, ACTION_SYNC_DATA));
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableRefresh, new IconicsDrawable(context, CommunityMaterial.Icon2.cmd_refresh)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableSync, new IconicsDrawable(context, CommunityMaterial.Icon2.cmd_sync)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
|
||||
|
||||
boolean unified = widgetConfig.profileId == -1;
|
||||
|
||||
List<Profile> profileList = new ArrayList<>();
|
||||
if (unified) {
|
||||
profileList = app.db.profileDao().getAllNow();
|
||||
filterOutArchived(profileList);
|
||||
}
|
||||
else {
|
||||
Profile profile = app.db.profileDao().getFullByIdNow(widgetConfig.profileId);
|
||||
if (profile != null) {
|
||||
profileList.add(profile);
|
||||
}
|
||||
}
|
||||
|
||||
//d(TAG, "Profiles: "+ Arrays.toString(profileList.toArray()));
|
||||
|
||||
if (profileList == null || profileList.size() == 0) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE);
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_profile_doesnt_exist));
|
||||
}
|
||||
else {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.GONE);
|
||||
//Register profile;
|
||||
|
||||
long bellSyncDiffMillis = 0;
|
||||
if (app.appConfig.bellSyncDiff != null) {
|
||||
bellSyncDiffMillis = app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000;
|
||||
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier;
|
||||
bellSyncDiffMillis *= -1;
|
||||
}
|
||||
|
||||
List<ItemWidgetTimetableModel> lessonList = new ArrayList<>();
|
||||
|
||||
Time syncedNow = Time.fromMillis(Time.getNow().getInMillis() + bellSyncDiffMillis);
|
||||
|
||||
Date today = Date.getToday();
|
||||
|
||||
int openProfileId = -1;
|
||||
Date displayingDate = null;
|
||||
int displayingWeekDay = 0;
|
||||
if (unified) {
|
||||
views.setTextViewText(R.id.widgetTimetableSubtitle, app.getString(R.string.widget_timetable_title_unified));
|
||||
}
|
||||
else {
|
||||
views.setTextViewText(R.id.widgetTimetableSubtitle, profileList.get(0).getName());
|
||||
openProfileId = profileList.get(0).getId();
|
||||
}
|
||||
|
||||
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(unified ? -1 : openProfileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today);
|
||||
|
||||
int scrollPos = 0;
|
||||
|
||||
for (Profile profile: profileList) {
|
||||
Date profileDisplayingDate = HomeFragment.findDateWithLessons(profile.getId(), lessons, syncedNow, 1);
|
||||
int profileDisplayingWeekDay = profileDisplayingDate.getWeekDay();
|
||||
int dayDiff = Date.diffDays(profileDisplayingDate, Date.getToday());
|
||||
|
||||
//d(TAG, "For profile "+profile.name+" displayingDate is "+profileDisplayingDate.getStringY_m_d());
|
||||
if (displayingDate == null || profileDisplayingDate.getValue() < displayingDate.getValue()) {
|
||||
displayingDate = profileDisplayingDate;
|
||||
displayingWeekDay = profileDisplayingWeekDay;
|
||||
//d(TAG, "Setting as global dd");
|
||||
if (dayDiff == 0) {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, app.getString(R.string.day_today_format, Week.getFullDayName(displayingWeekDay)));
|
||||
} else if (dayDiff == 1) {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, app.getString(R.string.day_tomorrow_format, Week.getFullDayName(displayingWeekDay)));
|
||||
} else {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, Week.getFullDayName(displayingWeekDay) + " " + profileDisplayingDate.getStringDm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Profile profile: profileList) {
|
||||
int pos = 0;
|
||||
|
||||
List<EventFull> events = app.db.eventDao().getAllByDateNow(profile.getId(), displayingDate);
|
||||
if (events == null)
|
||||
events = new ArrayList<>();
|
||||
|
||||
if (unified) {
|
||||
ItemWidgetTimetableModel separator = new ItemWidgetTimetableModel();
|
||||
separator.profileId = profile.getId();
|
||||
separator.bigStyle = widgetConfig.bigStyle;
|
||||
separator.darkTheme = widgetConfig.darkTheme;
|
||||
separator.separatorProfileName = profile.getName();
|
||||
lessonList.add(separator);
|
||||
}
|
||||
|
||||
for (LessonFull lesson : lessons) {
|
||||
//d(TAG, "Profile "+profile.id+" Lesson profileId "+lesson.profileId+" weekDay "+lesson.weekDay+", "+lesson);
|
||||
if (profile.getId() != lesson.profileId || displayingWeekDay != lesson.weekDay)
|
||||
continue;
|
||||
//d(TAG, "Not skipped");
|
||||
ItemWidgetTimetableModel model = new ItemWidgetTimetableModel();
|
||||
|
||||
model.bigStyle = widgetConfig.bigStyle;
|
||||
model.darkTheme = widgetConfig.darkTheme;
|
||||
|
||||
model.profileId = profile.getId();
|
||||
|
||||
model.lessonDate = displayingDate;
|
||||
model.startTime = lesson.startTime;
|
||||
model.endTime = lesson.endTime;
|
||||
|
||||
model.lessonPassed = (syncedNow.getValue() > lesson.endTime.getValue()) && displayingWeekDay == Week.getTodayWeekDay();
|
||||
model.lessonCurrent = (Time.inRange(lesson.startTime, lesson.endTime, syncedNow)) && displayingWeekDay == Week.getTodayWeekDay();
|
||||
|
||||
if (model.lessonCurrent) {
|
||||
scrollPos = pos;
|
||||
} else if (model.lessonPassed) {
|
||||
scrollPos = pos + 1;
|
||||
}
|
||||
pos++;
|
||||
|
||||
model.subjectName = bs(lesson.subjectLongName);
|
||||
model.classroomName = lesson.classroomName;
|
||||
|
||||
model.bellSyncDiffMillis = bellSyncDiffMillis;
|
||||
|
||||
if (lesson.changeId != 0) {
|
||||
if (lesson.changeType == LessonChange.TYPE_CHANGE) {
|
||||
model.lessonChange = true;
|
||||
if (lesson.changedClassroomName()) {
|
||||
model.newClassroomName = lesson.changeClassroomName;
|
||||
}
|
||||
|
||||
if (lesson.changedSubjectLongName()) {
|
||||
model.newSubjectName = lesson.changeSubjectLongName;
|
||||
}
|
||||
}
|
||||
if (lesson.changeType == LessonChange.TYPE_CANCELLED) {
|
||||
model.lessonCancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (EventFull event : events) {
|
||||
if (event.startTime == null)
|
||||
continue;
|
||||
if (event.eventDate.getValue() == displayingDate.getValue()
|
||||
&& event.startTime.getValue() == lesson.startTime.getValue()) {
|
||||
model.eventColors.add(event.type == TYPE_HOMEWORK ? ItemWidgetTimetableModel.EVENT_COLOR_HOMEWORK : event.getColor());
|
||||
}
|
||||
}
|
||||
|
||||
lessonList.add(model);
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonList.size() == 0) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE);
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, new Intent());
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_no_lessons));
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
else {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.GONE);
|
||||
|
||||
timetables.put(appWidgetId, lessonList);
|
||||
//WidgetTimetableListProvider.widgetsLessons.put(appWidgetId, lessons);
|
||||
//views.setRemoteAdapter(R.id.widgetTimetableListView, new Intent());
|
||||
Intent listIntent = new Intent(context, WidgetTimetableService.class);
|
||||
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
listIntent.setData(Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, listIntent);
|
||||
|
||||
// template to handle the click listener for each item
|
||||
Intent intentTemplate = new Intent(context, LessonDetailsActivity.class);
|
||||
// Old activities shouldn't be in the history stack
|
||||
intentTemplate.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntentTimetable = PendingIntent.getActivity(context,
|
||||
0,
|
||||
intentTemplate,
|
||||
0);
|
||||
views.setPendingIntentTemplate(R.id.widgetTimetableListView, pendingIntentTimetable);
|
||||
|
||||
Intent openIntent = new Intent(context, MainActivity.class);
|
||||
openIntent.setAction("android.intent.action.MAIN");
|
||||
if (!unified) {
|
||||
openIntent.putExtra("profileId", openProfileId);
|
||||
openIntent.putExtra("timetableDate", displayingDate.getValue());
|
||||
}
|
||||
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE);
|
||||
PendingIntent pendingOpenIntent = PendingIntent.getActivity(context,
|
||||
appWidgetId, openIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableHeader, pendingOpenIntent);
|
||||
|
||||
if (!unified)
|
||||
views.setScrollPosition(R.id.widgetTimetableListView, scrollPos);
|
||||
}
|
||||
}
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetTimetableListView);
|
||||
}
|
||||
//modeInt++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
for (int appWidgetId: appWidgetIds) {
|
||||
app.appConfig.widgetTimetableConfigs.remove(appWidgetId);
|
||||
}
|
||||
app.saveConfig("widgetTimetableConfigs");
|
||||
}
|
||||
}
|
||||
|
371
app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.kt
Normal file
371
app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.kt
Normal file
@ -0,0 +1,371 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
import pl.szczodrzynski.edziennik.widgets.WidgetConfig
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.LessonDialogActivity
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.WidgetTimetableService
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
|
||||
class WidgetTimetable : AppWidgetProvider() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (ACTION_SYNC_DATA == intent.action) {
|
||||
EdziennikTask.sync().enqueue(context)
|
||||
}
|
||||
super.onReceive(context, intent)
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
val thisWidget = ComponentName(context, WidgetTimetable::class.java)
|
||||
|
||||
timetables = SparseArray()
|
||||
//timetables.clear();
|
||||
|
||||
val app = context.applicationContext as App
|
||||
|
||||
var bellSyncDiffMillis: Long = 0
|
||||
if (app.appConfig.bellSyncDiff != null) {
|
||||
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong()
|
||||
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong()
|
||||
bellSyncDiffMillis *= -1
|
||||
}
|
||||
|
||||
val allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
|
||||
|
||||
allWidgetIds?.forEach { appWidgetId ->
|
||||
var widgetConfig = app.appConfig.widgetTimetableConfigs[appWidgetId]
|
||||
if (widgetConfig == null) {
|
||||
widgetConfig = WidgetConfig(app.profileFirstId())
|
||||
app.appConfig.widgetTimetableConfigs[appWidgetId] = widgetConfig
|
||||
app.appConfig.savePending = true
|
||||
}
|
||||
|
||||
val views = if (widgetConfig.bigStyle) {
|
||||
RemoteViews(context.packageName, if (widgetConfig.darkTheme) R.layout.widget_timetable_dark_big else R.layout.widget_timetable_big)
|
||||
} else {
|
||||
RemoteViews(context.packageName, if (widgetConfig.darkTheme) R.layout.widget_timetable_dark else R.layout.widget_timetable)
|
||||
}
|
||||
|
||||
val refreshIntent = Intent(app, WidgetTimetable::class.java)
|
||||
refreshIntent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
|
||||
val pendingRefreshIntent = PendingIntent.getBroadcast(context,
|
||||
0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableRefresh, pendingRefreshIntent)
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableSync, getPendingSelfIntent(context, ACTION_SYNC_DATA))
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableRefresh, IconicsDrawable(context, CommunityMaterial.Icon2.cmd_refresh)
|
||||
.colorInt(Color.WHITE)
|
||||
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
|
||||
.colorInt(Color.WHITE)
|
||||
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
|
||||
|
||||
prepareAppWidget(app, appWidgetId, views, widgetConfig, bellSyncDiffMillis)
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetTimetableListView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareAppWidget(
|
||||
app: App,
|
||||
appWidgetId: Int,
|
||||
views: RemoteViews,
|
||||
widgetConfig: WidgetConfig,
|
||||
bellSyncDiffMillis: Long
|
||||
) {
|
||||
// get the current bell-synced time
|
||||
val now = Time.fromMillis(Time.getNow().inMillis + bellSyncDiffMillis)
|
||||
|
||||
// set the widget transparency
|
||||
val mode = PorterDuff.Mode.DST_IN
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
// this code seems to crash the launcher on >= P
|
||||
val transparency = widgetConfig.opacity //0...1
|
||||
val colorFilter = 0x01000000L * (255f * transparency).toLong()
|
||||
try {
|
||||
val declaredMethods = Class.forName("android.widget.RemoteViews").declaredMethods
|
||||
val len = declaredMethods.size
|
||||
if (len > 0) {
|
||||
for (m in 0 until len) {
|
||||
val method = declaredMethods[m]
|
||||
if (method.name == "setDrawableParameters") {
|
||||
method.isAccessible = true
|
||||
method.invoke(views, R.id.widgetTimetableListView, true, -1, colorFilter.toInt(), mode, -1)
|
||||
method.invoke(views, R.id.widgetTimetableHeader, true, -1, colorFilter.toInt(), mode, -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvocationTargetException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
val unified = widgetConfig.profileId == -1
|
||||
|
||||
// get all profiles or one profile with the specified id
|
||||
val profileList = if (unified)
|
||||
app.db.profileDao().allNow.filterOutArchived()
|
||||
else
|
||||
listOfNotNull(app.db.profileDao().getByIdNow(widgetConfig.profileId))
|
||||
|
||||
// no profile was found
|
||||
if (profileList.isEmpty()) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE)
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_profile_doesnt_exist))
|
||||
return
|
||||
}
|
||||
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.GONE)
|
||||
|
||||
// set lesson search bounds
|
||||
val today = Date.getToday()
|
||||
val searchEnd = today.clone().stepForward(0, 0, 7)
|
||||
|
||||
var scrollPos = 0
|
||||
|
||||
var profileId: Int? = null
|
||||
var displayingDate: Date? = null
|
||||
|
||||
val models = mutableListOf<ItemWidgetTimetableModel>()
|
||||
|
||||
// get all lessons within the search bounds
|
||||
val lessonList = app.db.timetableDao().getBetweenDatesNow(today, searchEnd)
|
||||
|
||||
for (profile in profileList) {
|
||||
|
||||
// add a profile separator with its name
|
||||
if (unified) {
|
||||
val separator = ItemWidgetTimetableModel()
|
||||
separator.profileId = profile.id
|
||||
separator.bigStyle = widgetConfig.bigStyle
|
||||
separator.darkTheme = widgetConfig.darkTheme
|
||||
separator.separatorProfileName = profile.name
|
||||
models.add(separator)
|
||||
}
|
||||
|
||||
// search for lessons to display
|
||||
val timetableDate = Date.getToday()
|
||||
var checkedDays = 0
|
||||
var lessons = lessonList.filter { it.profileId == profile.id && it.displayDate == timetableDate && it.type != Lesson.TYPE_NO_LESSONS }
|
||||
while ((lessons.isEmpty() || lessons.none {
|
||||
it.displayDate != today || (it.displayDate == today && it.displayEndTime != null && it.displayEndTime!! >= now)
|
||||
}) && checkedDays < 7) {
|
||||
timetableDate.stepForward(0, 0, 1)
|
||||
lessons = lessonList.filter { it.profileId == profile.id && it.displayDate == timetableDate && it.type != Lesson.TYPE_NO_LESSONS }
|
||||
checkedDays++
|
||||
}
|
||||
|
||||
// set the displayingDate to show in the header
|
||||
if (!unified) {
|
||||
if (lessons.isNotEmpty())
|
||||
displayingDate = timetableDate
|
||||
profileId = profile.id
|
||||
}
|
||||
|
||||
// get all events for the current date
|
||||
val events = app.db.eventDao().getAllByDateNow(profile.id, timetableDate)?.filterNotNull() ?: emptyList()
|
||||
|
||||
lessons.forEachIndexed { pos, lesson ->
|
||||
val model = ItemWidgetTimetableModel()
|
||||
|
||||
model.bigStyle = widgetConfig.bigStyle
|
||||
model.darkTheme = widgetConfig.darkTheme
|
||||
|
||||
model.profileId = profile.id
|
||||
|
||||
model.lessonId = lesson.id
|
||||
model.lessonDate = timetableDate
|
||||
model.startTime = lesson.displayStartTime
|
||||
model.endTime = lesson.displayEndTime
|
||||
|
||||
// check if the lesson has already passed or it's currently in progress
|
||||
if (lesson.displayDate == today) {
|
||||
lesson.displayEndTime?.let { endTime ->
|
||||
model.lessonPassed = now > endTime
|
||||
lesson.displayStartTime?.let { startTime ->
|
||||
model.lessonCurrent = now in startTime..endTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set where should the list view scroll to
|
||||
if (model.lessonCurrent) {
|
||||
scrollPos = pos
|
||||
} else if (model.lessonPassed) {
|
||||
scrollPos = pos + 1
|
||||
}
|
||||
|
||||
// set the subject and classroom name
|
||||
model.subjectName = lesson.displaySubjectName
|
||||
model.classroomName = lesson.displayClassroom
|
||||
|
||||
// set the bell sync to calculate progress in ListProvider
|
||||
model.bellSyncDiffMillis = bellSyncDiffMillis
|
||||
|
||||
// make the model aware of the lesson type
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_CANCELLED -> {
|
||||
model.lessonCancelled = true
|
||||
}
|
||||
Lesson.TYPE_CHANGE,
|
||||
Lesson.TYPE_SHIFTED_SOURCE,
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
model.lessonChange = true
|
||||
}
|
||||
}
|
||||
|
||||
// add every event on this lesson
|
||||
for (event in events) {
|
||||
if (event.startTime != null && event.startTime != lesson.displayStartTime)
|
||||
continue
|
||||
model.eventColors.add(if (event.type == TYPE_HOMEWORK) ItemWidgetTimetableModel.EVENT_COLOR_HOMEWORK else event.getColor())
|
||||
}
|
||||
|
||||
models += model
|
||||
}
|
||||
}
|
||||
|
||||
if (unified) {
|
||||
// set the title for an unified widget
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, app.getString(R.string.widget_timetable_title_unified))
|
||||
views.setViewVisibility(R.id.widgetTimetableSubtitle, View.GONE)
|
||||
} else {
|
||||
// set the title to present the widget's profile
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, profileList[0].name)
|
||||
views.setViewVisibility(R.id.widgetTimetableTitle, View.VISIBLE)
|
||||
// make the subtitle show current date for these lessons
|
||||
displayingDate?.let {
|
||||
when (Date.diffDays(it, Date.getToday())) {
|
||||
0 -> views.setTextViewText(R.id.widgetTimetableSubtitle, app.getString(R.string.day_today_format, Week.getFullDayName(it.weekDay)))
|
||||
1 -> views.setTextViewText(R.id.widgetTimetableSubtitle, app.getString(R.string.day_tomorrow_format, Week.getFullDayName(it.weekDay)))
|
||||
else -> views.setTextViewText(R.id.widgetTimetableSubtitle, Week.getFullDayName(it.weekDay) + " " + it.formattedString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// intent running when the header is clicked
|
||||
val openIntent = Intent(app, MainActivity::class.java)
|
||||
openIntent.action = "android.intent.action.MAIN"
|
||||
if (!unified) {
|
||||
// per-profile widget should redirect to it + correct day
|
||||
profileId?.let {
|
||||
openIntent.putExtra("profileId", it)
|
||||
}
|
||||
displayingDate?.let {
|
||||
openIntent.putExtra("timetableDate", it.value)
|
||||
}
|
||||
}
|
||||
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE)
|
||||
val pendingOpenIntent = PendingIntent.getActivity(app, appWidgetId, openIntent, 0)
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableHeader, pendingOpenIntent)
|
||||
|
||||
if (lessonList.isEmpty()) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE)
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, Intent())
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_no_lessons))
|
||||
return
|
||||
}
|
||||
|
||||
timetables!!.put(appWidgetId, models)
|
||||
|
||||
// apply the list service to the list view
|
||||
val listIntent = Intent(app, WidgetTimetableService::class.java)
|
||||
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
listIntent.data = Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME))
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, listIntent)
|
||||
|
||||
// create an intent used to display the lesson details dialog
|
||||
val intentTemplate = Intent(app, LessonDialogActivity::class.java)
|
||||
intentTemplate.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
val pendingIntentTimetable = PendingIntent.getActivity(app, appWidgetId, intentTemplate, 0)
|
||||
views.setPendingIntentTemplate(R.id.widgetTimetableListView, pendingIntentTimetable)
|
||||
|
||||
if (!unified)
|
||||
views.setScrollPosition(R.id.widgetTimetableListView, scrollPos)
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
val app = context.applicationContext as App
|
||||
for (appWidgetId in appWidgetIds) {
|
||||
app.appConfig.widgetTimetableConfigs.remove(appWidgetId)
|
||||
}
|
||||
app.saveConfig("widgetTimetableConfigs")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
|
||||
val ACTION_SYNC_DATA = "ACTION_SYNC_DATA"
|
||||
private val TAG = "WidgetTimetable"
|
||||
private val modeInt = 0
|
||||
|
||||
var timetables: SparseArray<List<ItemWidgetTimetableModel>>? = null
|
||||
|
||||
fun getPendingSelfIntent(context: Context, action: String): PendingIntent {
|
||||
val intent = Intent(context, WidgetTimetable::class.java)
|
||||
intent.action = action
|
||||
return getPendingSelfIntent(context, intent)
|
||||
}
|
||||
|
||||
fun getPendingSelfIntent(context: Context, intent: Intent): PendingIntent {
|
||||
return PendingIntent.getBroadcast(context, 0, intent, 0)
|
||||
}
|
||||
|
||||
fun drawableToBitmap(drawable: Drawable): Bitmap {
|
||||
|
||||
if (drawable is BitmapDrawable) {
|
||||
return drawable.bitmap
|
||||
}
|
||||
|
||||
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
|
||||
return bitmap
|
||||
}
|
||||
}
|
||||
}
|
@ -60,6 +60,9 @@ class ApiService : Service() {
|
||||
|
||||
private val notification by lazy { EdziennikNotification(this) }
|
||||
|
||||
private var lastEventTime = System.currentTimeMillis()
|
||||
private var taskCancelTries = 0
|
||||
|
||||
/* ______ _ _ _ _ _____ _ _ _ _
|
||||
| ____| | | (_) (_) | / ____| | | | | | |
|
||||
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
|
||||
@ -68,22 +71,17 @@ class ApiService : Service() {
|
||||
|______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/
|
||||
private val taskCallback = object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
|
||||
//if (!taskCancelled) {
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
//}
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
taskRunning = null
|
||||
taskProfileId = -1
|
||||
taskProgress = -1f
|
||||
taskProgressText = null
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
clearTask()
|
||||
|
||||
notification.setIdle().post()
|
||||
runTask()
|
||||
}
|
||||
|
||||
override fun onError(apiError: ApiError) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
||||
apiError.profileId = taskProfileId
|
||||
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
|
||||
@ -92,9 +90,7 @@ class ApiService : Service() {
|
||||
if (apiError.isCritical) {
|
||||
taskRunning?.cancel()
|
||||
notification.setCriticalError().post()
|
||||
taskRunning = null
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
clearTask()
|
||||
runTask()
|
||||
}
|
||||
else {
|
||||
@ -103,6 +99,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
if (step <= 0)
|
||||
return
|
||||
if (taskProgress < 0)
|
||||
@ -115,6 +112,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
taskProgressText = getString(stringRes)
|
||||
d(TAG, "Task $taskRunningId progress: $taskProgressText")
|
||||
EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText))
|
||||
@ -129,6 +127,7 @@ class ApiService : Service() {
|
||||
| | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | |
|
||||
|_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/
|
||||
private fun runTask() {
|
||||
checkIfTaskFrozen()
|
||||
if (taskIsRunning)
|
||||
return
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
|
||||
@ -137,6 +136,8 @@ class ApiService : Service() {
|
||||
return
|
||||
}
|
||||
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
|
||||
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
|
||||
task.taskId = ++taskMaximumId
|
||||
task.prepare(app)
|
||||
@ -166,6 +167,48 @@ class ApiService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is inactive for more than 30 seconds.
|
||||
* If the user tries to cancel a task with no success at least three times,
|
||||
* consider it frozen as well.
|
||||
*
|
||||
* This usually means it is broken and won't become active again.
|
||||
* This method cancels the task and removes any pointers to it.
|
||||
*/
|
||||
private fun checkIfTaskFrozen(): Boolean {
|
||||
if (System.currentTimeMillis() - lastEventTime > 30*1000
|
||||
|| taskCancelTries >= 3) {
|
||||
val time = System.currentTimeMillis() - lastEventTime
|
||||
d(TAG, "!!! Task $taskRunningId froze for $time ms. $taskRunning")
|
||||
clearTask()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the service if the current task is frozen/broken.
|
||||
*/
|
||||
private fun stopIfTaskFrozen() {
|
||||
if (checkIfTaskFrozen()) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any task descriptors or pointers from the service.
|
||||
*/
|
||||
private fun clearTask() {
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
taskRunning = null
|
||||
taskProfileId = -1
|
||||
taskProgress = -1f
|
||||
taskProgressText = null
|
||||
taskCancelled = false
|
||||
taskCancelTries = 0
|
||||
}
|
||||
|
||||
private fun allCompleted() {
|
||||
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
|
||||
stopSelf()
|
||||
@ -211,8 +254,10 @@ class ApiService : Service() {
|
||||
EventBus.getDefault().removeStickyEvent(request)
|
||||
d(TAG, request.toString())
|
||||
|
||||
taskCancelTries++
|
||||
taskCancelled = true
|
||||
taskRunning?.cancel()
|
||||
stopIfTaskFrozen()
|
||||
}
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.ASYNC)
|
||||
fun onServiceCloseRequest(request: ServiceCloseRequest) {
|
||||
|
@ -39,9 +39,11 @@ const val ERROR_REQUEST_HTTP_404 = 54
|
||||
const val ERROR_REQUEST_HTTP_405 = 55
|
||||
const val ERROR_REQUEST_HTTP_410 = 56
|
||||
const val ERROR_REQUEST_HTTP_500 = 57
|
||||
const val ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND = 60
|
||||
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
|
||||
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
|
||||
const val ERROR_RESPONSE_EMPTY = 100
|
||||
const val ERROR_LOGIN_DATA_MISSING = 101
|
||||
const val ERROR_LOGIN_DATA_INVALID = 102
|
||||
const val ERROR_PROFILE_MISSING = 105
|
||||
const val ERROR_INVALID_LOGIN_MODE = 110
|
||||
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
|
||||
@ -99,6 +101,13 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID = 172
|
||||
const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED = 173
|
||||
const val ERROR_LIBRUS_SYNERGIA_OTHER = 174
|
||||
const val ERROR_LIBRUS_SYNERGIA_MAINTENANCE = 175
|
||||
const val ERROR_LIBRUS_MESSAGES_MAINTENANCE = 176
|
||||
const val ERROR_LIBRUS_MESSAGES_ERROR = 177
|
||||
const val ERROR_LIBRUS_MESSAGES_OTHER = 178
|
||||
const val ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN = 179
|
||||
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
|
||||
const val ERROR_LIBRUS_API_MAINTENANCE = 181
|
||||
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
||||
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||
@ -109,7 +118,7 @@ const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS = 206
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OTHER = 210
|
||||
const val ERROR_MOBIDZIENNIK_WEB_ACCESS_DENIED = 211
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY = 212
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 212
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 216
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213
|
||||
const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
||||
@ -163,3 +172,5 @@ const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
|
||||
const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912
|
||||
const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
|
||||
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
|
||||
|
||||
const val LOGIN_NO_ARGUMENTS = 1201
|
||||
|
@ -7,39 +7,36 @@ package pl.szczodrzynski.edziennik.api.v2
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
|
||||
const val FEATURE_ALL = 0
|
||||
const val FEATURE_TIMETABLE = 1
|
||||
const val FEATURE_AGENDA = 2
|
||||
const val FEATURE_GRADES = 3
|
||||
const val FEATURE_HOMEWORK = 4
|
||||
const val FEATURE_BEHAVIOUR = 5
|
||||
const val FEATURE_ATTENDANCE = 6
|
||||
const val FEATURE_MESSAGES_INBOX = 7
|
||||
const val FEATURE_MESSAGES_SENT = 8
|
||||
const val FEATURE_ANNOUNCEMENTS = 9
|
||||
internal const val FEATURE_TIMETABLE = 1
|
||||
internal const val FEATURE_AGENDA = 2
|
||||
internal const val FEATURE_GRADES = 3
|
||||
internal const val FEATURE_HOMEWORK = 4
|
||||
internal const val FEATURE_BEHAVIOUR = 5
|
||||
internal const val FEATURE_ATTENDANCE = 6
|
||||
internal const val FEATURE_MESSAGES_INBOX = 7
|
||||
internal const val FEATURE_MESSAGES_SENT = 8
|
||||
internal const val FEATURE_ANNOUNCEMENTS = 9
|
||||
|
||||
const val FEATURE_ALWAYS_NEEDED = 100
|
||||
const val FEATURE_STUDENT_INFO = 101
|
||||
const val FEATURE_STUDENT_NUMBER = 109
|
||||
const val FEATURE_SCHOOL_INFO = 102
|
||||
const val FEATURE_CLASS_INFO = 103
|
||||
const val FEATURE_TEAM_INFO = 104
|
||||
const val FEATURE_LUCKY_NUMBER = 105
|
||||
const val FEATURE_TEACHERS = 106
|
||||
const val FEATURE_SUBJECTS = 107
|
||||
const val FEATURE_CLASSROOMS = 108
|
||||
const val FEATURE_PUSH_CONFIG = 120
|
||||
|
||||
const val FEATURE_MESSAGE_GET = 201
|
||||
internal const val FEATURE_ALWAYS_NEEDED = 100
|
||||
internal const val FEATURE_STUDENT_INFO = 101
|
||||
internal const val FEATURE_STUDENT_NUMBER = 109
|
||||
internal const val FEATURE_SCHOOL_INFO = 102
|
||||
internal const val FEATURE_CLASS_INFO = 103
|
||||
internal const val FEATURE_TEAM_INFO = 104
|
||||
internal const val FEATURE_LUCKY_NUMBER = 105
|
||||
internal const val FEATURE_TEACHERS = 106
|
||||
internal const val FEATURE_SUBJECTS = 107
|
||||
internal const val FEATURE_CLASSROOMS = 108
|
||||
internal const val FEATURE_PUSH_CONFIG = 120
|
||||
|
||||
object Features {
|
||||
private fun getAllNecessary(): List<Int> = listOf(
|
||||
|
@ -37,6 +37,16 @@ object Regexes {
|
||||
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
|
||||
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
|
||||
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
|
||||
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
|
||||
@ -60,4 +70,10 @@ object Regexes {
|
||||
val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
|
||||
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val VULCAN_SHITFT_ANNOTATION by lazy {
|
||||
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
|
||||
data class MessageGetEvent(val message: MessageFull)
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.template.Template
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
|
||||
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
|
||||
companion object {
|
||||
@ -21,7 +22,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
fun sync() = EdziennikTask(-1, SyncRequest())
|
||||
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 messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
||||
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
// get the requested profile and login store
|
||||
val profile = app.db.profileDao().getByIdNow(profileId)
|
||||
this.profile = profile
|
||||
if (profile == null || !profile.syncEnabled) {
|
||||
if (profile == null) {
|
||||
return
|
||||
}
|
||||
val loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) ?: return
|
||||
@ -69,7 +70,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(),
|
||||
viewId = request.viewIds?.get(0)?.first,
|
||||
arguments = request.arguments)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.messageId)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
||||
}
|
||||
@ -87,6 +88,6 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
class SyncRequest
|
||||
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)
|
||||
data class MessageGetRequest(val message: MessageFull)
|
||||
class AnnouncementsReadRequest
|
||||
}
|
||||
}
|
||||
|
@ -138,10 +138,12 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" }
|
||||
return validateTeacher(teacher, firstName, lastName)
|
||||
}
|
||||
|
||||
fun getTeacher(firstNameChar: Char, lastName: String): Teacher {
|
||||
val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" }
|
||||
return validateTeacher(teacher, firstNameChar.toString(), lastName)
|
||||
}
|
||||
|
||||
fun getTeacherByLastFirst(nameLastFirst: String): Teacher {
|
||||
val nameParts = nameLastFirst.split(" ")
|
||||
return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0])
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
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.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
@ -61,7 +62,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(messageId: Int) {
|
||||
override fun getMessage(message: MessageFull) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
|
||||
/*messageId*/ messageId
|
||||
)
|
||||
|
||||
data.messageList.add(message)
|
||||
data.messageIgnoreList.add(message)
|
||||
data.messageRecipientList.add(messageRecipient)
|
||||
data.messageMetadataList.add(Metadata(
|
||||
profileId,
|
||||
|
@ -74,7 +74,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
|
||||
data.messageRecipientIgnoreList.add(messageRecipient)
|
||||
}
|
||||
|
||||
data.messageList.add(message)
|
||||
data.messageIgnoreList.add(message)
|
||||
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate))
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-22
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
|
||||
@ -12,33 +12,38 @@ import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
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.lessons.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CANCELLED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CHANGE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
|
||||
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
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebTimetable"
|
||||
}
|
||||
|
||||
init {
|
||||
val weekStart = Week.getWeekStart()
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
weekStart.stepForward(0, 0, 7)
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
webApiGet(TAG, IDZIENNIK_WEB_TIMETABLE, mapOf(
|
||||
"idPozDziennika" to data.registerId,
|
||||
"pidRokSzkolny" to data.schoolYearId,
|
||||
"data" to weekStart.stringY_m_d+"T10:00:00.000Z"
|
||||
"data" to "${weekStart.stringY_m_d}T10:00:00.000Z"
|
||||
)) { result ->
|
||||
val json = result.getJsonObject("d") ?: run {
|
||||
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
|
||||
@ -56,64 +61,103 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
|
||||
}
|
||||
|
||||
val dates = mutableSetOf<Int>()
|
||||
val lessons = mutableListOf<Lesson>()
|
||||
|
||||
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { lesson ->
|
||||
val subject = data.getSubject(
|
||||
lesson.getString("Nazwa") ?: return@forEach,
|
||||
lesson.getLong("Id"),
|
||||
lesson.getString("Skrot") ?: ""
|
||||
)
|
||||
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel") ?: return@forEach)
|
||||
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
|
||||
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1) ?: return@forEach]
|
||||
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel")
|
||||
?: return@forEach)
|
||||
|
||||
val lessonObject = Lesson(
|
||||
profileId,
|
||||
weekDay,
|
||||
lessonRange.startTime,
|
||||
lessonRange.endTime
|
||||
).apply {
|
||||
subjectId = subject.id
|
||||
teacherId = teacher.id
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
classroomName = lesson.getString("NazwaSali") ?: ""
|
||||
val newSubjectName = lesson.getString("PrzedmiotZastepujacy")
|
||||
val newSubject = when (newSubjectName.isNullOrBlank()) {
|
||||
true -> null
|
||||
else -> data.getSubject(newSubjectName, null, newSubjectName)
|
||||
}
|
||||
|
||||
data.lessonList.add(lessonObject)
|
||||
val newTeacherName = lesson.getString("NauZastepujacy")
|
||||
val newTeacher = when (newTeacherName.isNullOrBlank()) {
|
||||
true -> null
|
||||
else -> data.getTeacherByFDotLast(newTeacherName)
|
||||
}
|
||||
|
||||
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
|
||||
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1)
|
||||
?: return@forEach]
|
||||
val lessonDate = weekStart.clone().stepForward(0, 0, weekDay)
|
||||
val classroom = lesson.getString("NazwaSali")
|
||||
|
||||
val id = lessonDate.combineWith(lessonRange.startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
val type = lesson.getInt("TypZastepstwa") ?: -1
|
||||
if (type != -1) {
|
||||
// we have a lesson change to process
|
||||
val lessonChangeObject = LessonChange(
|
||||
profileId,
|
||||
weekStart.clone().stepForward(0, 0, weekDay),
|
||||
lessonObject.startTime,
|
||||
lessonObject.endTime
|
||||
)
|
||||
|
||||
lessonChangeObject.teamId = lessonObject.teamId
|
||||
lessonChangeObject.teacherId = lessonObject.teacherId
|
||||
lessonChangeObject.subjectId = lessonObject.subjectId
|
||||
lessonChangeObject.classroomName = lessonObject.classroomName
|
||||
when (type) {
|
||||
0 -> lessonChangeObject.type = TYPE_CANCELLED
|
||||
1, 2, 3, 4, 5 -> {
|
||||
lessonChangeObject.type = TYPE_CHANGE
|
||||
val newTeacher = lesson.getString("NauZastepujacy")
|
||||
val newSubject = lesson.getString("PrzedmiotZastepujacy")
|
||||
if (newTeacher != null) {
|
||||
lessonChangeObject.teacherId = data.getTeacherByFDotLast(newTeacher).id
|
||||
}
|
||||
if (newSubject != null) {
|
||||
lessonChangeObject.subjectId = data.getSubject(newSubject, null, "").id
|
||||
}
|
||||
val lessonObject = Lesson(profileId, id)
|
||||
|
||||
when (type) {
|
||||
1, 2, 3, 4, 5 -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_CHANGE
|
||||
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonRange.lessonNumber
|
||||
this.startTime = lessonRange.startTime
|
||||
this.endTime = lessonRange.endTime
|
||||
this.subjectId = newSubject?.id
|
||||
this.teacherId = newTeacher?.id
|
||||
this.teamId = data.teamClass?.id
|
||||
this.classroom = classroom
|
||||
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonRange.lessonNumber
|
||||
this.oldStartTime = lessonRange.startTime
|
||||
this.oldEndTime = lessonRange.endTime
|
||||
this.oldSubjectId = subject.id
|
||||
this.oldTeacherId = teacher.id
|
||||
this.oldTeamId = data.teamClass?.id
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
0 -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_CANCELLED
|
||||
|
||||
data.lessonChangeList.add(lessonChangeObject)
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonRange.lessonNumber
|
||||
this.oldStartTime = lessonRange.startTime
|
||||
this.oldEndTime = lessonRange.endTime
|
||||
this.oldSubjectId = subject.id
|
||||
this.oldTeacherId = teacher.id
|
||||
this.oldTeamId = data.teamClass?.id
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_NORMAL
|
||||
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonRange.lessonNumber
|
||||
this.startTime = lessonRange.startTime
|
||||
this.endTime = lessonRange.endTime
|
||||
this.subjectId = subject.id
|
||||
this.teacherId = teacher.id
|
||||
this.teamId = data.teamClass?.id
|
||||
this.classroom = classroom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dates.add(lessonDate.value)
|
||||
lessons.add(lessonObject)
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonChangeObject.id,
|
||||
lessonObject.id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
@ -121,6 +165,23 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
}
|
||||
}
|
||||
|
||||
val date: Date = weekStart.clone()
|
||||
while (date <= weekEnd) {
|
||||
if (!dates.contains(date.value)) {
|
||||
lessons.add(Lesson(profileId, date.value.toLong()).apply {
|
||||
this.type = Lesson.TYPE_NO_LESSONS
|
||||
this.date = date.clone()
|
||||
})
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.lessonNewList.addAll(lessons)
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
|
||||
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -5,10 +5,11 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.interfaces
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
|
||||
interface EdziennikInterface {
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||
fun getMessage(messageId: Int)
|
||||
fun getMessage(message: MessageFull)
|
||||
fun markAllAnnouncementsAsRead()
|
||||
fun firstLogin()
|
||||
fun cancel()
|
||||
|
@ -10,13 +10,16 @@ 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
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetMessage
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
|
||||
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.LibrusLoginMessages
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
@ -78,8 +81,16 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(messageId: Int) {
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
@ -24,6 +24,7 @@ const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 1023
|
||||
const val ENDPOINT_LIBRUS_API_TEXT_GC = 1024
|
||||
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 1025
|
||||
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 1026
|
||||
const val ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS = 1030
|
||||
const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 1031
|
||||
const val ENDPOINT_LIBRUS_API_POINT_GRADES = 1032
|
||||
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033
|
||||
@ -97,6 +98,7 @@ val LibrusFeatures = listOf(
|
||||
ENDPOINT_LIBRUS_API_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
|
@ -32,6 +32,13 @@ open class LibrusApi(open val data: DataLibrus) {
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (response?.code() == HTTP_UNAVAILABLE) {
|
||||
data.error(ApiError(tag, ERROR_LIBRUS_API_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (json == null && response?.parserErrorBody == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
@ -104,6 +111,7 @@ open class LibrusApi(open val data: DataLibrus) {
|
||||
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HTTP_FORBIDDEN)
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HTTP_UNAVAILABLE)
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -85,6 +85,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
LibrusApiGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
|
||||
LibrusApiGradeComments(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GC -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
|
||||
LibrusApiGradeCategories(data, onSuccess)
|
||||
|
@ -15,7 +15,6 @@ import org.jsoup.parser.Parser
|
||||
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
|
||||
@ -43,19 +42,19 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
if (text.isNullOrEmpty()) {
|
||||
data.error(ApiError(LibrusSynergia.TAG, ERROR_RESPONSE_EMPTY)
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Finish error handling
|
||||
|
||||
if ("error" in text) {
|
||||
when ("<type>(.*)</type>".toRegex().find(text)?.get(1)) {
|
||||
"eAccessDeny" -> data.error(ApiError(tag, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED)
|
||||
.withResponse(response)
|
||||
.withApiResponse(text))
|
||||
}
|
||||
when {
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
open class LibrusSynergia(open val data: DataLibrus) {
|
||||
companion object {
|
||||
const val TAG = "LibrusSynergia"
|
||||
private const val TAG = "LibrusSynergia"
|
||||
}
|
||||
|
||||
val profileId
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-20
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.asJsonObjectList
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
import pl.szczodrzynski.edziennik.getLong
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
|
||||
class LibrusApiGradeComments(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApiGradeComments"
|
||||
}
|
||||
|
||||
init {
|
||||
apiGet(TAG, "Grades/Comments") { json ->
|
||||
|
||||
json.getJsonArray("Comments")?.asJsonObjectList()?.forEach { comment ->
|
||||
val id = comment.getLong("Id") ?: return@forEach
|
||||
val text = comment.getString("Text")
|
||||
|
||||
val gradeCategoryObject = GradeCategory(
|
||||
profileId,
|
||||
id,
|
||||
-1f,
|
||||
-1,
|
||||
text
|
||||
).apply {
|
||||
type = GradeCategory.TYPE_COMMENT
|
||||
}
|
||||
|
||||
data.gradeCategories.put(id, gradeCategoryObject)
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE
|
||||
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.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -42,12 +43,21 @@ class LibrusApiGrades(override val data: DataLibrus,
|
||||
weight = 0f
|
||||
}
|
||||
|
||||
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
|
||||
if (comments.isNotEmpty()) {
|
||||
data.gradeCategories.singleOrNull {
|
||||
it.type == GradeCategory.TYPE_COMMENT
|
||||
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
|
||||
}?.text
|
||||
} else null
|
||||
} ?: ""
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
categoryName,
|
||||
color,
|
||||
"",
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
weight,
|
||||
|
@ -17,6 +17,7 @@ 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
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class LibrusApiTimetables(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
@ -29,9 +30,18 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id }
|
||||
}
|
||||
|
||||
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) }
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
apiGet(TAG, "Timetables?weekStart=$getDate") { json ->
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
apiGet(TAG, "Timetables?weekStart=${weekStart.stringY_m_d}") { json ->
|
||||
val days = json.getJsonObject("Timetable")
|
||||
|
||||
days?.entrySet()?.forEach { (dateString, dayEl) ->
|
||||
@ -44,21 +54,19 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
val lessonRange = lessonRangeEl?.asJsonArray?.asJsonObjectList()
|
||||
if (lessonRange?.isNullOrEmpty() == false)
|
||||
lessonsFound = true
|
||||
lessonRange?.forEachIndexed { index, lesson ->
|
||||
lessonRange?.forEach { lesson ->
|
||||
parseLesson(lessonDate, lesson)
|
||||
}
|
||||
}
|
||||
|
||||
if (day.isNullOrEmpty() || !lessonsFound) {
|
||||
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
|
||||
data.lessonNewList.add(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))
|
||||
@ -176,7 +184,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL) {
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
@ -187,6 +195,6 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList += lessonObject
|
||||
data.lessonNewList.add(lessonObject)
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ class LibrusApiUsers(override val data: DataLibrus,
|
||||
|
||||
users?.forEach { user ->
|
||||
val id = user.getLong("Id") ?: return@forEach
|
||||
val firstName = user.getString("FirstName")?.fixWhiteSpaces() ?: ""
|
||||
val lastName = user.getString("LastName")?.fixWhiteSpaces() ?: ""
|
||||
val firstName = user.getString("FirstName")?.fixName() ?: ""
|
||||
val lastName = user.getString("LastName")?.fixName() ?: ""
|
||||
|
||||
data.teacherList.put(id, Teacher(profileId, id, firstName, lastName))
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_MESSAGES_SENT
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages
|
||||
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.data.db.modules.teachers.Teacher
|
||||
@ -20,7 +21,7 @@ import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int = Message.TYPE_RECEIVED,
|
||||
class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int = TYPE_RECEIVED,
|
||||
archived: Boolean = false, val onSuccess: () -> Unit) : LibrusMessages(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusMessagesGetList"
|
||||
@ -28,7 +29,7 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int
|
||||
|
||||
init {
|
||||
val endpoint = when (type) {
|
||||
Message.TYPE_RECEIVED -> "Inbox/action/GetList"
|
||||
TYPE_RECEIVED -> "Inbox/action/GetList"
|
||||
Message.TYPE_SENT -> "Outbox/action/GetList"
|
||||
else -> null
|
||||
}
|
||||
@ -46,34 +47,38 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int
|
||||
else -> 0
|
||||
}
|
||||
val sentDate = Date.fromIso(element.select("sendDate").text().trim())
|
||||
var senderId: Long = -1
|
||||
var receiverId: Long = -1
|
||||
|
||||
when (type) {
|
||||
Message.TYPE_RECEIVED -> {
|
||||
val senderFirstName = element.select("senderFirstName").text().trim()
|
||||
val senderLastName = element.select("senderLastName").text().trim()
|
||||
senderId = data.teacherList.singleOrNull {
|
||||
it.name == senderFirstName && it.surname == senderLastName
|
||||
}?.id ?: -1
|
||||
}
|
||||
val recipientFirstName = element.select(when (type) {
|
||||
TYPE_RECEIVED -> "senderFirstName"
|
||||
else -> "receiverFirstName"
|
||||
}).text().trim()
|
||||
|
||||
Message.TYPE_SENT -> {
|
||||
val receiverFirstName = element.select("receiverFirstName").text().trim()
|
||||
val receiverLastName = element.select("receiverLastName").text().trim()
|
||||
receiverId = data.teacherList.singleOrNull {
|
||||
it.name == receiverFirstName && it.surname == receiverLastName
|
||||
}?.id ?: {
|
||||
val teacherObject = Teacher(
|
||||
profileId,
|
||||
-1 * Utils.crc16("$receiverFirstName $receiverLastName".toByteArray()).toLong(),
|
||||
receiverFirstName,
|
||||
receiverLastName
|
||||
)
|
||||
data.teacherList.put(teacherObject.id, teacherObject)
|
||||
teacherObject.id
|
||||
}.invoke()
|
||||
}
|
||||
val recipientLastName = element.select(when (type) {
|
||||
TYPE_RECEIVED -> "senderLastName"
|
||||
else -> "receiverLastName"
|
||||
}).text().trim()
|
||||
|
||||
val recipientId = data.teacherList.singleOrNull {
|
||||
it.name == recipientFirstName && it.surname == recipientLastName
|
||||
}?.id ?: {
|
||||
val teacherObject = Teacher(
|
||||
profileId,
|
||||
-1 * Utils.crc16("$recipientFirstName $recipientLastName".toByteArray()).toLong(),
|
||||
recipientFirstName,
|
||||
recipientLastName
|
||||
)
|
||||
data.teacherList.put(teacherObject.id, teacherObject)
|
||||
teacherObject.id
|
||||
}.invoke()
|
||||
|
||||
val senderId = when (type) {
|
||||
TYPE_RECEIVED -> recipientId
|
||||
else -> -1
|
||||
}
|
||||
|
||||
val receiverId = when (type) {
|
||||
TYPE_RECEIVED -> -1
|
||||
else -> recipientId
|
||||
}
|
||||
|
||||
val notified = when (type) {
|
||||
@ -99,7 +104,7 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int
|
||||
id
|
||||
)
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
@ -112,7 +117,7 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int
|
||||
}
|
||||
|
||||
when (type) {
|
||||
Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, SYNC_ALWAYS)
|
||||
TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, SYNC_ALWAYS)
|
||||
Message.TYPE_SENT -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES)
|
||||
}
|
||||
onSuccess()
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-11
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.messages
|
||||
|
||||
import android.util.Base64
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class LibrusMessagesGetMessage(
|
||||
override val data: DataLibrus,
|
||||
private val messageObject: MessageFull,
|
||||
val onSuccess: () -> Unit
|
||||
) : LibrusMessages(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusMessagesGetMessage"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
messagesGet(TAG, "GetMessage", parameters = mapOf(
|
||||
"messageId" to messageObject.id,
|
||||
"archive" to 0
|
||||
)) { doc ->
|
||||
val message = doc.select("response GetMessage data").first()
|
||||
|
||||
val body = Base64.decode(message.select("Message").text(), Base64.DEFAULT)
|
||||
.toString(Charset.defaultCharset())
|
||||
.replace("\n", "<br>")
|
||||
.replace("<!\\[CDATA\\[", "")
|
||||
.replace("]]>", "")
|
||||
|
||||
messageObject.apply {
|
||||
this.body = body
|
||||
|
||||
clearAttachments()
|
||||
message.select("attachments ArrayItem").forEach {
|
||||
val attachmentId = it.select("id").text().toLong()
|
||||
val attachmentName = it.select("filename").text()
|
||||
addAttachment(attachmentId, attachmentName, -1)
|
||||
}
|
||||
}
|
||||
|
||||
val messageRecipientList = mutableListOf<MessageRecipientFull>()
|
||||
|
||||
when (messageObject.type) {
|
||||
TYPE_RECEIVED -> {
|
||||
val senderLoginId = message.select("senderId").text()
|
||||
data.teacherList.singleOrNull { it.id == messageObject.senderId }?.loginId = senderLoginId
|
||||
|
||||
val readDateText = message.select("readDate").text()
|
||||
val readDate = when (readDateText.isNotEmpty()) {
|
||||
true -> Date.fromIso(readDateText)
|
||||
else -> 0
|
||||
}
|
||||
|
||||
val messageRecipientObject = MessageRecipientFull(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
readDate,
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
messageRecipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong
|
||||
|
||||
messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
TYPE_SENT -> {
|
||||
|
||||
message.select("receivers ArrayItem").forEach { receiver ->
|
||||
val receiverFirstName = receiver.select("firstName").text().fixName()
|
||||
val receiverLastName = receiver.select("lastName").text().fixName()
|
||||
val receiverLoginId = receiver.select("receiverId").text()
|
||||
|
||||
val teacher = data.teacherList.singleOrNull { it.name == receiverFirstName && it.surname == receiverLastName }
|
||||
val receiverId = teacher?.id ?: -1
|
||||
teacher?.loginId = receiverLoginId
|
||||
|
||||
val readDateText = message.select("readed").text()
|
||||
val readDate = when (readDateText.isNotEmpty()) {
|
||||
true -> Date.fromIso(readDateText)
|
||||
else -> 0
|
||||
}
|
||||
|
||||
val messageRecipientObject = MessageRecipientFull(
|
||||
profileId,
|
||||
receiverId,
|
||||
-1,
|
||||
readDate,
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
messageRecipientObject.fullName = "$receiverFirstName $receiverLastName"
|
||||
|
||||
messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!messageObject.seen) {
|
||||
data.messageMetadataList.add(Metadata(
|
||||
messageObject.profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
messageObject.id,
|
||||
true,
|
||||
true,
|
||||
messageObject.addedDate
|
||||
))
|
||||
}
|
||||
|
||||
messageObject.recipients = messageRecipientList
|
||||
data.messageRecipientList.addAll(messageRecipientList)
|
||||
data.messageList.add(messageObject)
|
||||
|
||||
EventBus.getDefault().postSticky(MessageGetEvent(messageObject))
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess()}
|
||||
}
|
@ -16,8 +16,7 @@ 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_BAD_REQUEST
|
||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||
import java.net.HttpURLConnection.*
|
||||
|
||||
class LibrusLoginApi {
|
||||
companion object {
|
||||
@ -117,6 +116,13 @@ class LibrusLoginApi {
|
||||
|
||||
private val tokenCallback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (response?.code() == HTTP_UNAVAILABLE) {
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_API_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
@ -176,6 +182,7 @@ class LibrusLoginApi {
|
||||
.post()
|
||||
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HTTP_UNAVAILABLE)
|
||||
.callback(tokenCallback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -6,20 +6,57 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
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.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
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
|
||||
|
||||
class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "LoginLibrusMessages"
|
||||
}
|
||||
|
||||
private val callback by lazy { object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
val location = response?.headers()?.get("Location")
|
||||
when {
|
||||
location?.contains("MultiDomainLogon") == true -> loginWithSynergia(location)
|
||||
location?.contains("AutoLogon") == true -> {
|
||||
saveSessionId(response, text)
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
text?.contains("<status>ok</status>") == true -> {
|
||||
saveSessionId(response, text)
|
||||
onSuccess()
|
||||
}
|
||||
text?.contains("<message>Niepoprawny login i/lub hasło.</message>") == true -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
||||
text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text?.contains("OffLine") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
||||
text?.contains("<status>error</status>") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
||||
text?.contains("<type>eVarWhitThisNameNotExists</type>") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text?.contains("<error>") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}}
|
||||
|
||||
init { run {
|
||||
if (data.profile == null) {
|
||||
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
|
||||
@ -41,7 +78,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) {
|
||||
loginWithSynergia()
|
||||
}
|
||||
else if (data.apiLogin != null && data.apiPassword != null && false) {
|
||||
else if (data.apiLogin != null && data.apiPassword != null) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
else {
|
||||
@ -54,7 +91,44 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
* XML (Flash messages website) login method. Uses a Synergia login and password.
|
||||
*/
|
||||
private fun loginWithCredentials() {
|
||||
d(TAG, "Request: Librus/Login/Messages - $LIBRUS_MESSAGES_URL/Login")
|
||||
|
||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = docBuilder.newDocument()
|
||||
val serviceElement = doc.createElement("service")
|
||||
val headerElement = doc.createElement("header")
|
||||
val dataElement = doc.createElement("data")
|
||||
val loginElement = doc.createElement("login")
|
||||
loginElement.appendChild(doc.createTextNode(data.apiLogin))
|
||||
dataElement.appendChild(loginElement)
|
||||
val passwordElement = doc.createElement("login")
|
||||
passwordElement.appendChild(doc.createTextNode(data.apiPassword))
|
||||
dataElement.appendChild(passwordElement)
|
||||
val keyStrokeElement = doc.createElement("KeyStroke")
|
||||
val keysElement = doc.createElement("Keys")
|
||||
val upElement = doc.createElement("Up")
|
||||
keysElement.appendChild(upElement)
|
||||
val downElement = doc.createElement("Down")
|
||||
keysElement.appendChild(downElement)
|
||||
keyStrokeElement.appendChild(keysElement)
|
||||
dataElement.appendChild(keyStrokeElement)
|
||||
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()
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_MESSAGES_URL/Login")
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,37 +137,6 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
private fun loginWithSynergia(url: String = "https://synergia.librus.pl/wiadomosci2") {
|
||||
d(TAG, "Request: Librus/Login/Messages - $url")
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
val location = response?.headers()?.get("Location")
|
||||
when {
|
||||
location?.contains("MultiDomainLogon") == true -> loginWithSynergia(location)
|
||||
location?.contains("AutoLogon") == true -> {
|
||||
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||
sessionId = sessionId?.replace("-MAINT", "")
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID)
|
||||
.withResponse(response)
|
||||
.withApiResponse(text))
|
||||
return
|
||||
}
|
||||
data.messagesSessionId = sessionId
|
||||
data.messagesSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
@ -103,4 +146,17 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
private fun saveSessionId(response: Response?, text: String?) {
|
||||
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID)
|
||||
.withResponse(response)
|
||||
.withApiResponse(text))
|
||||
return
|
||||
}
|
||||
data.messagesSessionId = sessionId
|
||||
data.messagesSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
|
||||
}
|
||||
}
|
@ -99,6 +99,14 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
.post()
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response) {
|
||||
val location = response.headers()?.get("Location")
|
||||
if (location == "http://localhost/bar?command=close") {
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_PORTAL_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED)
|
||||
@ -120,7 +128,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
if (response.code() == 403 || response.code() == 401) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_DATA_INVALID)
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
return
|
||||
|
@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
@ -16,7 +15,6 @@ import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
@ -86,6 +84,13 @@ class LibrusLoginSynergia(override val data: DataLibrus, val onSuccess: () -> Un
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(json: String?, response: Response?) {
|
||||
val location = response?.headers()?.get("Location")
|
||||
if (location?.endsWith("przerwa_techniczna") == true) {
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_SYNERGIA_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (location?.endsWith("centrum_powiadomien") == true) {
|
||||
val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID")
|
||||
if (sessionId == null) {
|
||||
|
@ -10,12 +10,15 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
|
||||
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.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
@ -61,8 +64,12 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(messageId: Int) {
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
MobidziennikLoginWeb(data) {
|
||||
MobidziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
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.teams.Team
|
||||
import pl.szczodrzynski.edziennik.getById
|
||||
@ -25,7 +24,7 @@ class MobidziennikApiTeams(val data: DataMobidziennik, tableTeams: List<String>?
|
||||
val teacherId = cols[4].toLongOrNull() ?: -1
|
||||
|
||||
val teamObject = Team(
|
||||
profileId,
|
||||
data.profileId,
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
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.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
@ -76,7 +75,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL) {
|
||||
if (it.type != Lesson.TYPE_NORMAL && date >= Date.getToday()) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
@ -93,7 +92,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
|
||||
for (day in dataDays) {
|
||||
val lessonDate = Date.fromValue(day)
|
||||
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
|
||||
data.lessonNewList += Lesson(data.profileId, lessonDate.value.toLong()).apply {
|
||||
type = Lesson.TYPE_NO_LESSONS
|
||||
date = lessonDate
|
||||
}
|
||||
@ -196,4 +195,4 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-18.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb
|
||||
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.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.monthFromName
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikWebGetMessage(
|
||||
override val data: DataMobidziennik,
|
||||
private val message: MessageFull,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebGetMessage"
|
||||
}
|
||||
|
||||
init {
|
||||
val typeUrl = if (message.type == Message.TYPE_SENT)
|
||||
"wiadwyslana"
|
||||
else
|
||||
"wiadodebrana"
|
||||
webGet(TAG, "/dziennik/$typeUrl/?id=${message.id}") { text ->
|
||||
MobidziennikLuckyNumberExtractor(data, text)
|
||||
|
||||
val messageRecipientList = mutableListOf<MessageRecipientFull>()
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
||||
val content = doc.select("#content").first()
|
||||
|
||||
val body = content.select(".wiadomosc_tresc").first()
|
||||
|
||||
if (message.type == TYPE_RECEIVED) {
|
||||
var readDate = System.currentTimeMillis()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_READ_DATE.find(body.html())?.let {
|
||||
val date = Date(
|
||||
it[3].toIntOrNull() ?: 2019,
|
||||
monthFromName(it[2]),
|
||||
it[1].toIntOrNull() ?: 1
|
||||
)
|
||||
val time = Time.fromH_m_s(
|
||||
it[4] // TODO blank string safety
|
||||
)
|
||||
readDate = date.combineWith(time)
|
||||
}
|
||||
|
||||
val recipient = MessageRecipientFull(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
readDate,
|
||||
message.id
|
||||
)
|
||||
|
||||
recipient.fullName = profile?.accountNameLong ?: profile?.studentNameLong
|
||||
|
||||
messageRecipientList.add(recipient)
|
||||
} else {
|
||||
message.senderId = -1
|
||||
message.senderReplyId = -1
|
||||
|
||||
content.select("table.spis tr:has(td)")?.forEach { recipientEl ->
|
||||
val senderEl = recipientEl.select("td:eq(0)").first()
|
||||
val senderName = senderEl.text()
|
||||
|
||||
val teacher = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }
|
||||
val receiverId = teacher?.id ?: -1
|
||||
|
||||
var readDate = 0L
|
||||
val isReadEl = recipientEl.select("td:eq(2)").first()
|
||||
if (isReadEl.ownText() != "NIE") {
|
||||
val readDateEl = recipientEl.select("td:eq(3) small").first()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_SENT_READ_DATE.find(readDateEl.ownText())?.let {
|
||||
val date = Date(
|
||||
it[3].toIntOrNull() ?: 2019,
|
||||
monthFromName(it[2]),
|
||||
it[1].toIntOrNull() ?: 1
|
||||
)
|
||||
val time = Time.fromH_m_s(
|
||||
it[4] // TODO blank string safety
|
||||
)
|
||||
readDate = date.combineWith(time)
|
||||
}
|
||||
}
|
||||
|
||||
val recipient = MessageRecipientFull(
|
||||
profileId,
|
||||
receiverId,
|
||||
-1,
|
||||
readDate,
|
||||
message.id
|
||||
)
|
||||
|
||||
recipient.fullName = teacher?.fullName ?: "?"
|
||||
|
||||
messageRecipientList.add(recipient)
|
||||
}
|
||||
}
|
||||
|
||||
// this line removes the sender and read date details
|
||||
body.select("div").remove()
|
||||
|
||||
// this needs to be at the end
|
||||
message.apply {
|
||||
this.body = body.html()
|
||||
|
||||
clearAttachments()
|
||||
content.select("ul li").map { it.select("a").first() }.forEach {
|
||||
val attachmentName = it.ownText()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_ATTACHMENT.find(it.outerHtml())?.let { match ->
|
||||
val attachmentId = match[1].toLong()
|
||||
var size = match[2].toFloatOrNull() ?: 0f
|
||||
when (match[3]) {
|
||||
"K" -> size *= 1024f
|
||||
"M" -> size *= 1024f * 1024f
|
||||
"G" -> size *= 1024f * 1024f * 1024f
|
||||
}
|
||||
message.addAttachment(attachmentId, attachmentName, size.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.seen) { // TODO discover why this monstrosity instead of MetadataDao.setSeen
|
||||
data.messageMetadataList.add(Metadata(
|
||||
message.profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
message.id,
|
||||
true,
|
||||
true,
|
||||
message.addedDate
|
||||
))
|
||||
}
|
||||
|
||||
message.recipients = messageRecipientList
|
||||
data.messageRecipientList.addAll(messageRecipientList)
|
||||
data.messageList.add(message)
|
||||
|
||||
EventBus.getDefault().postSticky(MessageGetEvent(message))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -8,9 +8,7 @@ import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.DAY
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb
|
||||
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.Message.TYPE_SENT
|
||||
@ -79,7 +77,7 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
-1
|
||||
)
|
||||
|
||||
data.messageList.add(message)
|
||||
data.messageIgnoreList.add(message)
|
||||
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate))
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
|
||||
if (hasAttachments)
|
||||
message.setHasAttachments()
|
||||
|
||||
data.messageList.add(message)
|
||||
data.messageIgnoreList.add(message)
|
||||
data.messageMetadataList.add(
|
||||
Metadata(
|
||||
profileId,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
@ -52,6 +53,15 @@ class ApiError(val tag: String, val errorCode: Int) {
|
||||
)
|
||||
}
|
||||
|
||||
fun getStringReason(context: Context): String {
|
||||
return context.resources.getIdentifier("error_${errorCode}_reason", "string", context.packageName).let {
|
||||
if (it != 0)
|
||||
context.getString(it)
|
||||
else
|
||||
"?"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ApiError(tag='$tag', errorCode=$errorCode, profileId=$profileId, throwable=$throwable, apiResponse=$apiResponse, request=$request, response=$response, isCritical=$isCritical)"
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
val teacherAbsenceList = mutableListOf<TeacherAbsence>()
|
||||
|
||||
val messageList = mutableListOf<Message>()
|
||||
val messageIgnoreList = mutableListOf<Message>()
|
||||
val messageRecipientList = mutableListOf<MessageRecipient>()
|
||||
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
|
||||
|
||||
@ -205,7 +206,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
announcementList.clear()
|
||||
luckyNumberList.clear()
|
||||
teacherAbsenceList.clear()
|
||||
messageList.clear()
|
||||
messageIgnoreList.clear()
|
||||
messageRecipientList.clear()
|
||||
messageRecipientIgnoreList.clear()
|
||||
metadataList.clear()
|
||||
@ -285,6 +286,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
}
|
||||
}
|
||||
|
||||
if (metadataList.isNotEmpty())
|
||||
db.metadataDao().addAllIgnore(metadataList)
|
||||
if (messageMetadataList.isNotEmpty())
|
||||
db.metadataDao().setSeen(messageMetadataList)
|
||||
|
||||
// not extracted from DB - always new data
|
||||
if (lessonList.isNotEmpty()) {
|
||||
db.lessonDao().clear(profile.id)
|
||||
@ -316,15 +322,13 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
db.teacherAbsenceDao().addAll(teacherAbsenceList)
|
||||
|
||||
if (messageList.isNotEmpty())
|
||||
db.messageDao().addAllIgnore(messageList)
|
||||
db.messageDao().addAll(messageList)
|
||||
if (messageIgnoreList.isNotEmpty())
|
||||
db.messageDao().addAllIgnore(messageIgnoreList)
|
||||
if (messageRecipientList.isNotEmpty())
|
||||
db.messageRecipientDao().addAll(messageRecipientList)
|
||||
if (messageRecipientIgnoreList.isNotEmpty())
|
||||
db.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList)
|
||||
if (metadataList.isNotEmpty())
|
||||
db.metadataDao().addAllIgnore(metadataList)
|
||||
if (messageMetadataList.isNotEmpty())
|
||||
db.metadataDao().setSeen(messageMetadataList)
|
||||
}
|
||||
|
||||
fun notifyAndSyncEvents(onSuccess: () -> Unit) {
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.template.firstlogin.TemplateFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
@ -61,7 +62,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(messageId: Int) {
|
||||
override fun getMessage(message: MessageFull) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -7,15 +7,14 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_VULCAN_API
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Data
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
|
||||
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
fun isApiLoginValid() = apiCertificateExpiryTime-30 > currentTimeUnix()
|
||||
&& apiCertificateKey.isNotNullNorEmpty()
|
||||
fun isApiLoginValid() = /*apiCertificateExpiryTime-30 > currentTimeUnix()
|
||||
&&*/ apiCertificateKey.isNotNullNorEmpty()
|
||||
&& apiCertificatePrivate.isNotNullNorEmpty()
|
||||
&& symbol.isNotNullNorEmpty()
|
||||
|
||||
@ -165,6 +164,8 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl"
|
||||
"KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu"
|
||||
"KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu"
|
||||
"LU1" -> "https://uonetplus-komunikacja.edu.lublin.eu"
|
||||
"LU2" -> "https://test-uonetplus-komunikacja.edu.lublin.eu"
|
||||
"P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl"
|
||||
"P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl"
|
||||
"P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl"
|
||||
@ -173,7 +174,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"SZ9" -> "http://vulcan.szkolny.eu"
|
||||
else -> null
|
||||
}
|
||||
return if (url != null) "$url/$symbol" else null
|
||||
return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
|
||||
}
|
||||
|
||||
val fullApiUrl: String?
|
||||
|
@ -12,10 +12,13 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiMessagesChangeStatus
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.firstlogin.VulcanFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
@ -61,8 +64,12 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(messageId: Int) {
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
VulcanLoginApi(data) {
|
||||
VulcanApiMessagesChangeStatus(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
@ -64,6 +64,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
VulcanApiAttendance(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_TIMETABLE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
VulcanApiTimetable(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_VULCAN_API_MESSAGES_INBOX -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
VulcanApiMessagesInbox(data, onSuccess)
|
||||
|
@ -55,7 +55,7 @@ class VulcanApiAttendance(override val data: DataVulcan, val onSuccess: () -> Un
|
||||
lessonSemester,
|
||||
attendance.getString("PrzedmiotNazwa") + attendanceCategory.name.let { " - $it" },
|
||||
lessonDate,
|
||||
data.lessonRanges.get(attendance.getInt("IdPoraLekcji") ?: 0)?.startTime,
|
||||
data.lessonRanges.get(attendance.getInt("Numer") ?: 0)?.startTime,
|
||||
type)
|
||||
|
||||
data.attendanceList.add(attendanceObject)
|
||||
|
@ -35,7 +35,7 @@ class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () ->
|
||||
elements?.getJsonArray("KategorieUwag")?.forEach { saveNoticeType(it.asJsonObject) }
|
||||
elements?.getJsonArray("KategorieFrekwencji")?.forEach { saveAttendanceType(it.asJsonObject) }
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_DICTIONARIES, 4*DAY)
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_DICTIONARIES, 4 * DAY)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,7 @@ class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () ->
|
||||
}
|
||||
|
||||
private fun saveLessonRange(lessonRange: JsonObject) {
|
||||
val lessonNumber = lessonRange.getInt("Id") ?: return
|
||||
val lessonNumber = lessonRange.getInt("Numer") ?: return
|
||||
val startTime = lessonRange.getString("PoczatekTekst")?.let { Time.fromH_m(it) } ?: return
|
||||
val endTime = lessonRange.getString("KoniecTekst")?.let { Time.fromH_m(it) } ?: return
|
||||
|
||||
@ -126,8 +126,7 @@ class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () ->
|
||||
Attendance.TYPE_ABSENT_EXCUSED
|
||||
else
|
||||
Attendance.TYPE_ABSENT
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
val belated = attendanceType.getBoolean("Spoznienie") ?: false
|
||||
val released = attendanceType.getBoolean("Zwolnienie") ?: false
|
||||
val present = attendanceType.getBoolean("Obecnosc") ?: true
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-12
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
|
||||
class VulcanApiMessagesChangeStatus(
|
||||
override val data: DataVulcan,
|
||||
private val messageObject: MessageFull,
|
||||
val onSuccess: () -> Unit
|
||||
) : VulcanApi(data) {
|
||||
companion object {
|
||||
const val TAG = "VulcanApiMessagesChangeStatus"
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS, parameters = mapOf(
|
||||
"WiadomoscId" to messageObject.id,
|
||||
"FolderWiadomosci" to "Odebrane",
|
||||
"Status" to "Widoczna",
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { _, _ ->
|
||||
|
||||
if (!messageObject.seen) {
|
||||
data.messageMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
messageObject.id,
|
||||
true,
|
||||
true,
|
||||
messageObject.addedDate
|
||||
))
|
||||
}
|
||||
|
||||
if (messageObject.type != TYPE_SENT) {
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
System.currentTimeMillis(),
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@ 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.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
@ -43,7 +45,22 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
|
||||
val senderLoginId = message.getString("NadawcaId") ?: return@forEach
|
||||
val senderId = data.teacherList
|
||||
.singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach
|
||||
.singleOrNull { it.loginId == senderLoginId }?.id ?: {
|
||||
|
||||
val senderName = message.getString("Nadawca") ?: ""
|
||||
|
||||
senderName.getLastFirstName()?.let { (senderLastName, senderFirstName) ->
|
||||
val teacherObject = Teacher(
|
||||
profileId,
|
||||
-1 * Utils.crc16(senderName.toByteArray()).toLong(),
|
||||
senderFirstName,
|
||||
senderLastName,
|
||||
senderLoginId
|
||||
)
|
||||
data.teacherList.put(teacherObject.id, teacherObject)
|
||||
teacherObject.id
|
||||
}
|
||||
}.invoke() ?: -1
|
||||
|
||||
val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 }
|
||||
?: -1
|
||||
@ -68,7 +85,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
id
|
||||
)
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
|
@ -14,6 +14,8 @@ 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.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
@ -23,11 +25,11 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
|
||||
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 startDate: Long = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).inUnix
|
||||
else -> Date.getToday().stepForward(0, -1, 0).inUnix
|
||||
}
|
||||
val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
|
||||
val endDate: Long = profile.getSemesterEnd(profile.currentSemester).inUnix
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_SENT, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
@ -43,23 +45,27 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
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 ->
|
||||
?.onEach { receiver ->
|
||||
|
||||
val recipientLoginId = recipient.getString("LoginId")
|
||||
?: return@forEachIndexed
|
||||
val recipientId = data.teacherList.singleOrNull { it.loginId == recipientLoginId }?.id
|
||||
?: return@forEachIndexed
|
||||
val receiverLoginId = receiver.getString("LoginId")
|
||||
?: return@onEach
|
||||
val receiverId = data.teacherList.singleOrNull { it.loginId == receiverLoginId }?.id
|
||||
?: {
|
||||
val receiverName = receiver.getString("Nazwa") ?: ""
|
||||
|
||||
receiverName.getLastFirstName()?.let { (receiverLastName, receiverFirstName) ->
|
||||
val teacherObject = Teacher(
|
||||
profileId,
|
||||
-1 * Utils.crc16(receiverName.toByteArray()).toLong(),
|
||||
receiverFirstName,
|
||||
receiverLastName,
|
||||
receiverLoginId
|
||||
)
|
||||
data.teacherList.put(teacherObject.id, teacherObject)
|
||||
teacherObject.id
|
||||
}
|
||||
}.invoke() ?: -1
|
||||
|
||||
val readDate: Long = when (readBy) {
|
||||
0 -> 0
|
||||
@ -71,7 +77,7 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
recipientId,
|
||||
receiverId,
|
||||
-1,
|
||||
readDate,
|
||||
id
|
||||
@ -80,7 +86,17 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
data.messageList.add(messageObject)
|
||||
val messageObject = Message(
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
TYPE_SENT,
|
||||
-1,
|
||||
-1
|
||||
)
|
||||
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
|
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-13
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import androidx.core.util.set
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_TIMETABLE
|
||||
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.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.crc16
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
const val TAG = "VulcanApiTimetable"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
|
||||
if (Date.getToday().weekDay > 4 && weekStart == currentWeekStart) {
|
||||
weekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_TIMETABLE, parameters = mapOf(
|
||||
"DataPoczatkowa" to weekStart.stringY_m_d,
|
||||
"DataKoncowa" to weekEnd.stringY_m_d,
|
||||
"IdUczen" to data.studentId,
|
||||
"IdOddzial" to data.studentClassId,
|
||||
"IdOkresKlasyfikacyjny" to data.studentSemesterId
|
||||
)) { json, _ ->
|
||||
val dates = mutableSetOf<Int>()
|
||||
val lessons = mutableListOf<Lesson>()
|
||||
|
||||
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson ->
|
||||
if (lesson.getBoolean("PlanUcznia") != true)
|
||||
return@forEach
|
||||
val lessonDate = Date.fromY_m_d(lesson.getString("DzienTekst"))
|
||||
val lessonNumber = lesson.getInt("NumerLekcji")
|
||||
val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }
|
||||
val startTime = lessonRange?.startTime
|
||||
val endTime = lessonRange?.endTime
|
||||
val teacherId = lesson.getLong("IdPracownik")
|
||||
val classroom = lesson.getString("Sala")
|
||||
|
||||
val oldTeacherId = lesson.getLong("IdPracownikOld")
|
||||
|
||||
val changeAnnotation = lesson.getString("AdnotacjaOZmianie") ?: ""
|
||||
val type = when {
|
||||
changeAnnotation.startsWith("(przeniesiona z") -> Lesson.TYPE_SHIFTED_TARGET
|
||||
changeAnnotation.startsWith("(przeniesiona na") -> Lesson.TYPE_SHIFTED_SOURCE
|
||||
changeAnnotation.startsWith("(zastępstwo") -> Lesson.TYPE_CHANGE
|
||||
lesson.getBoolean("PrzekreslonaNazwa") == true -> Lesson.TYPE_CANCELLED
|
||||
else -> Lesson.TYPE_NORMAL
|
||||
}
|
||||
|
||||
val teamId = lesson.getString("PodzialSkrot")?.let { teamName ->
|
||||
val name = "${data.teamClass?.name} $teamName"
|
||||
val id = name.crc16().toLong()
|
||||
var team = data.teamList.singleOrNull { it.name == name }
|
||||
if (team == null) {
|
||||
team = Team(
|
||||
profileId,
|
||||
id,
|
||||
name,
|
||||
Team.TYPE_VIRTUAL,
|
||||
"${data.schoolName}:$name",
|
||||
teacherId ?: oldTeacherId ?: -1
|
||||
)
|
||||
data.teamList[id] = team
|
||||
}
|
||||
team.id
|
||||
} ?: data.studentClassId.toLong()
|
||||
|
||||
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
|
||||
when (it) {
|
||||
0L -> {
|
||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||
|
||||
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
|
||||
?: {
|
||||
/**
|
||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||
*/
|
||||
|
||||
val subjectObject = Subject(
|
||||
profileId,
|
||||
-1 * crc16(subjectName.toByteArray()).toLong(),
|
||||
subjectName,
|
||||
subjectName
|
||||
)
|
||||
data.subjectList.put(subjectObject.id, subjectObject)
|
||||
subjectObject.id
|
||||
}.invoke()
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
|
||||
val lessonObject = Lesson(profileId, id).apply {
|
||||
this.type = type
|
||||
|
||||
when (type) {
|
||||
Lesson.TYPE_NORMAL, Lesson.TYPE_CHANGE, Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonNumber
|
||||
this.startTime = startTime
|
||||
this.endTime = endTime
|
||||
this.subjectId = subjectId
|
||||
this.teacherId = teacherId
|
||||
this.teamId = teamId
|
||||
this.classroom = classroom
|
||||
|
||||
this.oldTeacherId = oldTeacherId
|
||||
}
|
||||
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonNumber
|
||||
this.oldStartTime = startTime
|
||||
this.oldEndTime = endTime
|
||||
this.oldSubjectId = subjectId
|
||||
this.oldTeacherId = teacherId
|
||||
this.oldTeamId = teamId
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Lesson.TYPE_SHIFTED_SOURCE || type == Lesson.TYPE_SHIFTED_TARGET) {
|
||||
val shift = Regexes.VULCAN_SHITFT_ANNOTATION.find(changeAnnotation)
|
||||
val oldLessonNumber = shift?.get(2)?.toInt()
|
||||
val oldLessonDate = shift?.get(3)?.let { Date.fromd_m_Y(it) }
|
||||
|
||||
val oldLessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == oldLessonNumber }
|
||||
val oldStartTime = oldLessonRange?.startTime
|
||||
val oldEndTime = oldLessonRange?.endTime
|
||||
|
||||
when (type) {
|
||||
Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.lessonNumber = oldLessonNumber
|
||||
this.date = oldLessonDate
|
||||
this.startTime = oldStartTime
|
||||
this.endTime = oldEndTime
|
||||
}
|
||||
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.oldLessonNumber = oldLessonNumber
|
||||
this.oldDate = oldLessonDate
|
||||
this.oldStartTime = oldStartTime
|
||||
this.oldEndTime = oldEndTime
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
dates.add(lessonDate.value)
|
||||
lessons.add(lessonObject)
|
||||
}
|
||||
|
||||
val date: Date = weekStart.clone()
|
||||
while (date <= weekEnd) {
|
||||
if (!dates.contains(date.value)) {
|
||||
lessons.add(Lesson(profileId, date.value.toLong()).apply {
|
||||
this.type = Lesson.TYPE_NO_LESSONS
|
||||
this.date = date.clone()
|
||||
})
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.lessonNewList.addAll(lessons)
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}}
|
||||
}
|
@ -38,10 +38,12 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
|
||||
data.apiCertificatePfx ?: ""
|
||||
)
|
||||
onSuccess()
|
||||
return@run
|
||||
data.loginStore.removeLoginData("certificatePfx")
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
onSuccess()
|
||||
return@run
|
||||
}
|
||||
}
|
||||
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,11 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import im.wangchao.mhttp.Request;
|
||||
|
||||
/**
|
||||
* Callback containing a {@link Request.Builder} which has correct headers and body to download a corresponding message attachment when ran.
|
||||
* {@code onSuccess} has to be ran on the UI thread.
|
||||
*/
|
||||
public interface AttachmentGetCallback {
|
||||
void onSuccess(Request.Builder builder);
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeInfo;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Endpoint;
|
||||
|
||||
public interface EdziennikInterface {
|
||||
|
||||
/**
|
||||
* Sync all Edziennik data.
|
||||
* Ran always on worker thread.
|
||||
*
|
||||
* @param activityContext a {@link Context}, used for resource extractions, passed back to {@link SyncCallback}
|
||||
* @param callback ran on worker thread.
|
||||
* @param profileId
|
||||
* @param profile
|
||||
* @param loginStore
|
||||
*/
|
||||
void sync(@NonNull Context activityContext, @NonNull SyncCallback callback, int profileId, @Nullable Profile profile, @NonNull LoginStore loginStore);
|
||||
void syncMessages(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile);
|
||||
void syncFeature(@NonNull Context activityContext, @NonNull SyncCallback callback, @NonNull ProfileFull profile, int ... featureList);
|
||||
|
||||
int FEATURE_ALL = 0;
|
||||
int FEATURE_TIMETABLE = 1;
|
||||
int FEATURE_AGENDA = 2;
|
||||
int FEATURE_GRADES = 3;
|
||||
int FEATURE_HOMEWORK = 4;
|
||||
int FEATURE_NOTICES = 5;
|
||||
int FEATURE_ATTENDANCE = 6;
|
||||
int FEATURE_MESSAGES_INBOX = 7;
|
||||
int FEATURE_MESSAGES_OUTBOX = 8;
|
||||
int FEATURE_ANNOUNCEMENTS = 9;
|
||||
|
||||
/**
|
||||
* Download a single message or get its recipient list if it's already downloaded.
|
||||
*
|
||||
* May be executed on any thread.
|
||||
*
|
||||
* @param activityContext
|
||||
* @param errorCallback used for error reporting. Ran on a background thread.
|
||||
* @param profile
|
||||
* @param message a message of which body and recipient list should be downloaded.
|
||||
* @param messageCallback always executed on UI thread.
|
||||
*/
|
||||
void getMessage(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull MessageFull message, @NonNull MessageGetCallback messageCallback);
|
||||
void getAttachment(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull MessageFull message, long attachmentId, @NonNull AttachmentGetCallback attachmentCallback);
|
||||
//void getMessageList(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, int type, @NonNull MessageListCallback messageCallback);
|
||||
/**
|
||||
* Download a list of available message recipients.
|
||||
*
|
||||
* Updates a database-saved {@code teacherList} with {@code loginId}s.
|
||||
*
|
||||
* A {@link Teacher} is considered as a recipient when its {@code loginId} is not null.
|
||||
*
|
||||
* May be executed on any thread.
|
||||
*
|
||||
* @param activityContext
|
||||
* @param errorCallback used for error reporting. Ran on a background thread.
|
||||
* @param profile
|
||||
* @param recipientListGetCallback always executed on UI thread.
|
||||
*/
|
||||
void getRecipientList(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull RecipientListGetCallback recipientListGetCallback);
|
||||
MessagesComposeInfo getComposeInfo(@NonNull ProfileFull profile);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param profile a {@link Profile} containing already changed endpoints
|
||||
* @return a map of configurable {@link Endpoint}s along with their names, {@code null} when unsupported
|
||||
*/
|
||||
Map<String, Endpoint> getConfigurableEndpoints(Profile profile);
|
||||
|
||||
/**
|
||||
* Check if the specified endpoint is enabled for the current profile.
|
||||
*
|
||||
* @param profile a {@link Profile} containing already changed endpoints
|
||||
* @param defaultActive if the endpoint is enabled by default.
|
||||
* @param name the endpoint's name
|
||||
* @return {@code true} if the endpoint is enabled, {@code false} when it's not. Return {@code defaultActive} if unsupported.
|
||||
*/
|
||||
boolean isEndpointEnabled(Profile profile, boolean defaultActive, String name);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError;
|
||||
|
||||
public interface ErrorCallback {
|
||||
void onError(Context activityContext, @NonNull AppError error);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
public interface LoginCallback {
|
||||
void onSuccess();
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull;
|
||||
|
||||
/**
|
||||
* Callback containing a {@link MessageFull} which already has its {@code body} and {@code recipients}.
|
||||
* {@code onSuccess} is always ran on the UI thread.
|
||||
*/
|
||||
public interface MessageGetCallback {
|
||||
void onSuccess(MessageFull message);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull;
|
||||
|
||||
public interface MessageListCallback {
|
||||
void onSuccess(List<MessageFull> messageList);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
public interface ProgressCallback extends ErrorCallback {
|
||||
void onProgress(int progressStep);
|
||||
void onActionStarted(@StringRes int stringResId);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher;
|
||||
|
||||
public interface RecipientListGetCallback {
|
||||
void onSuccess(List<Teacher> teacherList);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
|
||||
|
||||
/**
|
||||
* A callback used for error reporting, progress information.
|
||||
* All the methods are always ran on a worker thread.
|
||||
*/
|
||||
public interface SyncCallback extends ProgressCallback {
|
||||
void onLoginFirst(List<Profile> profileList, LoginStore loginStore);
|
||||
void onSuccess(Context activityContext, ProfileFull profileFull);
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.v2
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
|
||||
data class ApiLoginResult(val loginStore: LoginStore, val error: AppError?)
|
@ -1,6 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.v2
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
data class FirstLoginResult(val profileList: ArrayList<Profile>, val error: AppError?)
|
@ -1,12 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.v2.librus.firstlogin
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class FirstLoginLibrus(val app: App, val loginStore: LoginStore, val progressCallback: ProgressCallback, val onSuccess: (profileList: List<Profile>) -> Unit) {
|
||||
init {
|
||||
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.v2.librus.firstlogin
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class FirstLoginSynergia(val app: App, val loginStore: LoginStore, val progressCallback: ProgressCallback, val onSuccess: (profileList: List<Profile>) -> Unit) {
|
||||
init {
|
||||
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
@ -85,6 +86,23 @@ public class Event {
|
||||
this.teamId = teamId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event clone() throws CloneNotSupportedException {
|
||||
return new Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate.clone(),
|
||||
startTime == null ? null : startTime.clone(),
|
||||
topic,
|
||||
color,
|
||||
type,
|
||||
addedManually,
|
||||
subjectId,
|
||||
teacherId,
|
||||
teamId
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{" +
|
||||
|
@ -1,10 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.grades;
|
||||
|
||||
import androidx.room.Entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.Entity;
|
||||
|
||||
@Entity(tableName = "gradeCategories",
|
||||
primaryKeys = {"profileId", "categoryId"})
|
||||
public class GradeCategory {
|
||||
@ -25,6 +25,9 @@ public class GradeCategory {
|
||||
*/
|
||||
public int type = 0;
|
||||
|
||||
public static final int TYPE_NORMAL = 0;
|
||||
public static final int TYPE_COMMENT = 1;
|
||||
|
||||
public GradeCategory(int profileId, long categoryId, float weight, int color, String text) {
|
||||
this.profileId = profileId;
|
||||
this.categoryId = categoryId;
|
||||
|
@ -1,19 +1,19 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity(tableName = "messages",
|
||||
primaryKeys = {"profileId", "messageId"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Message {
|
||||
int profileId;
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "messageId")
|
||||
public long id;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.messages;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
@ -11,6 +10,8 @@ import androidx.room.RawQuery;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_DELETED;
|
||||
@ -23,6 +24,9 @@ public abstract class MessageDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Message message);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Message> messageList);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
public abstract void addAllIgnore(List<Message> messageList);
|
||||
|
||||
@ -56,6 +60,7 @@ public abstract class MessageDao {
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MessageFull getById(int profileId, long messageId) {
|
||||
return getOneNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
|
@ -1,13 +1,15 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.messages;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.Ignore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.Ignore;
|
||||
|
||||
public class MessageFull extends Message {
|
||||
public String senderFullName = null;
|
||||
@Ignore
|
||||
@Nullable
|
||||
public List<MessageRecipientFull> recipients = null;
|
||||
|
||||
public MessageFull addRecipient(MessageRecipientFull recipient) {
|
||||
|
@ -21,7 +21,10 @@ interface NotificationDao {
|
||||
@Query("DELETE FROM notifications WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("SELECT * FROM notifications")
|
||||
@Query("DELETE FROM notifications")
|
||||
fun clearAll()
|
||||
|
||||
@Query("SELECT * FROM notifications ORDER BY addedDate DESC")
|
||||
fun getAll(): LiveData<List<Notification>>
|
||||
|
||||
@Query("SELECT * FROM notifications")
|
||||
|
@ -62,6 +62,25 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
return classroom ?: oldClassroom
|
||||
}
|
||||
|
||||
val displayTeamId: Long?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeamId
|
||||
return teamId ?: oldTeamId
|
||||
}
|
||||
val displaySubjectId: Long?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldSubjectId
|
||||
return subjectId ?: oldSubjectId
|
||||
}
|
||||
val displayTeacherId: Long?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeacherId
|
||||
return teacherId ?: oldTeacherId
|
||||
}
|
||||
|
||||
// metadata
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
|
@ -10,6 +10,27 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@Dao
|
||||
interface TimetableDao {
|
||||
companion object {
|
||||
private const val 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
|
||||
"""
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
operator fun plusAssign(lessonList: List<Lesson>)
|
||||
@ -17,33 +38,47 @@ interface TimetableDao {
|
||||
@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)")
|
||||
@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)")
|
||||
@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)")
|
||||
@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
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId
|
||||
ORDER BY id, type
|
||||
LIMIT 1
|
||||
""")
|
||||
fun getNextWithSubject(profileId: Int, today: Date, subjectId: Long) : LiveData<LessonFull?>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId AND timetable.teamId = :teamId
|
||||
ORDER BY id, type
|
||||
LIMIT 1
|
||||
""")
|
||||
fun getNextWithSubjectAndTeam(profileId: Int, today: Date, subjectId: Long, teamId: Long): LiveData<LessonFull?>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)
|
||||
ORDER BY profileId, id, type
|
||||
""")
|
||||
fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) : List<LessonFull>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND timetable.id = :lessonId
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getByIdNow(profileId: Int, lessonId: Long) : LessonFull?
|
||||
}
|
||||
|
@ -125,19 +125,22 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
String updateUrl = result.get("update_url").getAsString();
|
||||
String updateFilename = result.get("update_filename").getAsString();
|
||||
boolean updateMandatory = result.get("update_mandatory").getAsBoolean();
|
||||
boolean updateDirect = result.get("update_direct").getAsBoolean();
|
||||
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
|
||||
app.appConfig.updateVersion = updateVersion;
|
||||
app.appConfig.updateUrl = updateUrl;
|
||||
app.appConfig.updateFilename = updateFilename;
|
||||
app.appConfig.updateMandatory = updateMandatory;
|
||||
app.appConfig.updateDirect = updateDirect;
|
||||
app.saveConfig();
|
||||
}
|
||||
if (!UPDATES_ON_PLAY_STORE || intent.getBooleanExtra("UserChecked", false)) {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
updateVersion,
|
||||
updateUrl,
|
||||
updateFilename);
|
||||
updateFilename,
|
||||
updateDirect);
|
||||
}
|
||||
} else {
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
|
||||
@ -259,7 +262,7 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (UPDATES_ON_PLAY_STORE) {
|
||||
if (UPDATES_ON_PLAY_STORE && !intent.getBooleanExtra("update_direct", false)) {
|
||||
Utils.openGooglePlay(this, "pl.szczodrzynski.edziennik");
|
||||
return;
|
||||
}
|
||||
@ -270,7 +273,8 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
intent.getStringExtra("update_version"),
|
||||
intent.getStringExtra("update_url"),
|
||||
intent.getStringExtra("update_filename"));
|
||||
intent.getStringExtra("update_filename"),
|
||||
intent.getBooleanExtra("update_direct", false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -154,19 +154,22 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||
String updateUrl = remoteMessage.getData().get("update_url");
|
||||
String updateFilename = remoteMessage.getData().get("update_filename");
|
||||
boolean updateMandatory = Boolean.parseBoolean(remoteMessage.getData().get("update_mandatory"));
|
||||
boolean updateDirect = Boolean.parseBoolean(remoteMessage.getData().get("update_direct"));
|
||||
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
|
||||
app.appConfig.updateVersion = updateVersion;
|
||||
app.appConfig.updateUrl = updateUrl;
|
||||
app.appConfig.updateFilename = updateFilename;
|
||||
app.appConfig.updateMandatory = updateMandatory;
|
||||
app.appConfig.updateDirect = updateDirect;
|
||||
app.saveConfig("updateVersion", "updateUrl", "updateFilename", "updateMandatory");
|
||||
}
|
||||
if (!remoteMessage.getData().containsKey("update_silent")) {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
updateVersion,
|
||||
updateUrl,
|
||||
updateFilename);
|
||||
updateFilename,
|
||||
updateDirect);
|
||||
}
|
||||
} else {
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class EventAddTypeDialog(
|
||||
val activity: Activity,
|
||||
val profileId: Int,
|
||||
val date: Date? = null,
|
||||
val time: Time? = null
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "EventAddTypeDialog"
|
||||
}
|
||||
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setItems(R.array.main_menu_add_options) { dialog, which ->
|
||||
dialog.dismiss()
|
||||
EventManualDialog(activity, profileId)
|
||||
.show(
|
||||
activity.application as App,
|
||||
null,
|
||||
date,
|
||||
time,
|
||||
when (which) {
|
||||
1 -> EventManualDialog.DIALOG_HOMEWORK
|
||||
else -> EventManualDialog.DIALOG_EVENT
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
.show()
|
||||
}}
|
||||
}
|
@ -36,7 +36,6 @@ import java.util.List;
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.api.AppError;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType;
|
||||
@ -54,7 +53,6 @@ import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.App.APP_URL;
|
||||
import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_OTHER;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_DEFAULT;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_UNDEFINED;
|
||||
@ -374,7 +372,8 @@ public class EventManualDialog {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
activity.runOnUiThread(() -> {
|
||||
app.apiEdziennik.guiShowErrorDialog(activity, new AppError(TAG, 379, CODE_OTHER, null, e), R.string.error_occured);
|
||||
// TODO show error in EventManualDialog
|
||||
//app.apiEdziennik.guiShowErrorDialog(activity, new AppError(TAG, 379, CODE_OTHER, null, e), R.string.error_occured);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -518,7 +517,7 @@ public class EventManualDialog {
|
||||
|
||||
registerEventManualDateLayout = dialogView.findViewById(R.id.registerEventManualDateLayout);
|
||||
registerEventManualDate = dialogView.findViewById(R.id.registerEventManualDate);
|
||||
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
|
||||
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar_outline).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
|
||||
//registerEventManualDate.setCompoundDrawablePadding(Utils.dpToPx(6));
|
||||
registerEventManualLessonLayout = dialogView.findViewById(R.id.registerEventManualLessonLayout);
|
||||
registerEventManualLesson = dialogView.findViewById(R.id.registerEventManualLesson);
|
||||
|
@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class EventManualV2Dialog(
|
||||
val activity: AppCompatActivity,
|
||||
val profileId: Int,
|
||||
val defaultLesson: LessonFull? = null,
|
||||
val defaultDate: Date? = null,
|
||||
val defaultTime: Time? = null,
|
||||
val defaultType: Int? = null,
|
||||
val editingEvent: Event? = null,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "EventManualDialog"
|
||||
}
|
||||
|
||||
private lateinit var job: Job
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogEventManualV2Binding
|
||||
private lateinit var dialog: AlertDialog
|
||||
private var defaultLoaded = false
|
||||
|
||||
private lateinit var event: Event
|
||||
private var customColor: Int? = null
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
job = Job()
|
||||
onShowListener?.invoke(TAG)
|
||||
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.dialog_event_manual_title)
|
||||
.setView(b.root)
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
.setPositiveButton(R.string.save) { _, _ -> saveEvent() }
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
}
|
||||
.show()
|
||||
|
||||
event = editingEvent?.clone() ?: Event().also { event ->
|
||||
event.profileId = profileId
|
||||
/*defaultDate?.let {
|
||||
event.eventDate = it
|
||||
b.date = it
|
||||
}
|
||||
defaultTime?.let {
|
||||
event.startTime = it
|
||||
b.time = it
|
||||
}
|
||||
defaultType?.let {
|
||||
event.type = it
|
||||
}*/
|
||||
b.shareSwitch.isChecked = event.sharedBy != null
|
||||
}
|
||||
|
||||
b.showMore.onClick { // TODO iconics is broken
|
||||
it.apply {
|
||||
refreshDrawableState()
|
||||
|
||||
if (isChecked)
|
||||
Anim.expand(b.moreLayout, 200, null)
|
||||
else
|
||||
Anim.collapse(b.moreLayout, 200, null)
|
||||
}
|
||||
}
|
||||
|
||||
updateShareText()
|
||||
b.shareSwitch.onChange { _, isChecked ->
|
||||
updateShareText(isChecked)
|
||||
}
|
||||
|
||||
loadLists()
|
||||
}}
|
||||
|
||||
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {
|
||||
val editingShared = editingEvent?.sharedBy != null
|
||||
val editingOwn = editingEvent?.sharedBy == "self"
|
||||
|
||||
b.shareDetails.visibility = if (checked || editingShared)
|
||||
View.VISIBLE
|
||||
else View.GONE
|
||||
|
||||
val text = when {
|
||||
checked && editingShared && editingOwn -> R.string.dialog_event_manual_share_will_change
|
||||
checked && editingShared -> R.string.dialog_event_manual_share_will_request
|
||||
!checked && editingShared -> R.string.dialog_event_manual_share_will_remove
|
||||
else -> R.string.dialog_event_manual_share_first_notice
|
||||
}
|
||||
|
||||
b.shareDetails.setText(text)
|
||||
}
|
||||
|
||||
private fun loadLists() { launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get the team list
|
||||
val teams = app.db.teamDao().getAllNow(profileId)
|
||||
b.teamDropdown.clear()
|
||||
b.teamDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_team),
|
||||
""
|
||||
)
|
||||
b.teamDropdown += teams.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
|
||||
// get the subject list
|
||||
val subjects = app.db.subjectDao().getAllNow(profileId)
|
||||
b.subjectDropdown.clear()
|
||||
b.subjectDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_subject),
|
||||
""
|
||||
)
|
||||
b.subjectDropdown += subjects.map { TextInputDropDown.Item(it.id, it.longName, tag = it) }
|
||||
|
||||
// get the teacher list
|
||||
val teachers = app.db.teacherDao().getAllNow(profileId)
|
||||
b.teacherDropdown.clear()
|
||||
b.teacherDropdown += TextInputDropDown.Item(
|
||||
-1,
|
||||
activity.getString(R.string.dialog_event_manual_no_teacher),
|
||||
""
|
||||
)
|
||||
b.teacherDropdown += teachers.map { TextInputDropDown.Item(it.id, it.fullName, tag = it) }
|
||||
|
||||
// get the event type list
|
||||
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
b.typeDropdown.clear()
|
||||
b.typeDropdown += eventTypes.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
}
|
||||
deferred.await()
|
||||
|
||||
b.teamDropdown.isEnabled = true
|
||||
b.subjectDropdown.isEnabled = true
|
||||
b.teacherDropdown.isEnabled = true
|
||||
b.typeDropdown.isEnabled = true
|
||||
|
||||
b.typeDropdown.selected?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
|
||||
// copy IDs from event being edited
|
||||
editingEvent?.let {
|
||||
b.teamDropdown.select(it.teamId)
|
||||
b.subjectDropdown.select(it.subjectId)
|
||||
b.teacherDropdown.select(it.teacherId)
|
||||
b.typeDropdown.select(it.type)?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
if (it.color != -1)
|
||||
customColor = it.color
|
||||
}
|
||||
|
||||
// copy IDs from the LessonFull
|
||||
defaultLesson?.let {
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
}
|
||||
|
||||
b.typeDropdown.setOnChangeListener {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter((it.tag as EventType).color, PorterDuff.Mode.SRC_ATOP)
|
||||
customColor = null
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
(customColor ?: Event.COLOR_DEFAULT).let {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP)
|
||||
}
|
||||
b.typeColor.onClick {
|
||||
val currentColor = (b.typeDropdown?.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
||||
.setColor(currentColor)
|
||||
.create()
|
||||
colorPickerDialog.setColorPickerDialogListener(
|
||||
object : ColorPickerDialogListener {
|
||||
override fun onDialogDismissed(dialogId: Int) {}
|
||||
override fun onColorSelected(dialogId: Int, color: Int) {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||
customColor = color
|
||||
}
|
||||
})
|
||||
colorPickerDialog.show(activity.fragmentManager, "color-picker-dialog")
|
||||
}
|
||||
|
||||
loadDates()
|
||||
}}
|
||||
|
||||
private fun loadDates() { launch {
|
||||
val date = Date.getToday()
|
||||
val today = date.value
|
||||
var weekDay = date.weekDay
|
||||
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
val dates = mutableListOf<TextInputDropDown.Item>()
|
||||
// item choosing the next lesson of specific subject
|
||||
b.subjectDropdown.selected?.let {
|
||||
if (it.tag is Subject) {
|
||||
dates += TextInputDropDown.Item(
|
||||
-it.id,
|
||||
activity.getString(R.string.dialog_event_manual_date_next_lesson, it.tag.longName)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODAY
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_today, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
|
||||
// TOMORROW
|
||||
if (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1)
|
||||
weekDay++
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_tomorrow, date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// REMAINING SCHOOL DAYS OF THE CURRENT WEEK
|
||||
while (weekDay < 4) {
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_this_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
}
|
||||
// go to next week Monday
|
||||
date.stepForward(0, 0, -weekDay + 7)
|
||||
weekDay = 0
|
||||
// ALL SCHOOL DAYS OF THE NEXT WEEK
|
||||
while (weekDay < 4) {
|
||||
dates += TextInputDropDown.Item(
|
||||
date.value.toLong(),
|
||||
activity.getString(R.string.dialog_event_manual_date_next_week, Week.getFullDayName(weekDay), date.formattedString),
|
||||
tag = date.clone()
|
||||
)
|
||||
date.stepForward(0, 0, 1) // step one day forward
|
||||
weekDay++
|
||||
}
|
||||
dates += TextInputDropDown.Item(
|
||||
-1L,
|
||||
activity.getString(R.string.dialog_event_manual_date_other)
|
||||
)
|
||||
dates
|
||||
}
|
||||
|
||||
val dates = deferred.await()
|
||||
b.dateDropdown.clear().append(dates)
|
||||
|
||||
editingEvent?.eventDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
defaultLesson?.displayDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
if (b.dateDropdown.selected == null) {
|
||||
b.dateDropdown.select(today.toLong())
|
||||
}
|
||||
|
||||
b.dateDropdown.isEnabled = true
|
||||
|
||||
b.dateDropdown.setOnChangeListener { item ->
|
||||
when {
|
||||
// next lesson with specified subject
|
||||
item.id < -1 -> {
|
||||
val teamId = defaultLesson?.teamId ?: -1
|
||||
val selectedLessonDate = defaultLesson?.date ?: Date.getToday()
|
||||
|
||||
when (teamId) {
|
||||
-1L -> app.db.timetableDao().getNextWithSubject(profileId, selectedLessonDate, -item.id)
|
||||
else -> app.db.timetableDao().getNextWithSubjectAndTeam(profileId, selectedLessonDate, -item.id, teamId)
|
||||
}.observeOnce(activity, Observer {
|
||||
val lessonDate = it?.displayDate ?: return@Observer
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
lessonDate.value.toLong(),
|
||||
lessonDate.formattedString,
|
||||
tag = lessonDate
|
||||
))
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
defaultLoaded = false
|
||||
loadHours(it.displayStartTime)
|
||||
})
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// custom date
|
||||
item.id == -1L -> {
|
||||
MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection((b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) }
|
||||
?: Date.getToday()).inMillis)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
val dateSelected = Date.fromMillis(it)
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
dateSelected.value.toLong(),
|
||||
dateSelected.formattedString,
|
||||
tag = dateSelected
|
||||
))
|
||||
loadHours()
|
||||
}
|
||||
show(this@EventManualV2Dialog.activity.supportFragmentManager, "MaterialDatePicker")
|
||||
}
|
||||
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// a specific date
|
||||
else -> {
|
||||
b.dateDropdown.select(item)
|
||||
loadHours()
|
||||
}
|
||||
}
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
|
||||
loadHours()
|
||||
}}
|
||||
|
||||
private fun loadHours(defaultHour: Time? = null) {
|
||||
b.timeDropdown.isEnabled = false
|
||||
// get the selected date
|
||||
val date = b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) } ?: return
|
||||
// get all lessons for selected date
|
||||
app.db.timetableDao().getForDate(profileId, date).observeOnce(activity, Observer { lessons ->
|
||||
val hours = mutableListOf<TextInputDropDown.Item>()
|
||||
// add All day time choice
|
||||
hours += TextInputDropDown.Item(
|
||||
0L,
|
||||
activity.getString(R.string.dialog_event_manual_all_day)
|
||||
)
|
||||
lessons.forEach { lesson ->
|
||||
if (lesson.type == Lesson.TYPE_NO_LESSONS) {
|
||||
// indicate there are no lessons this day
|
||||
hours += TextInputDropDown.Item(
|
||||
-2L,
|
||||
activity.getString(R.string.dialog_event_manual_no_lessons)
|
||||
)
|
||||
return@forEach
|
||||
}
|
||||
// create the lesson caption
|
||||
val text = listOfNotEmpty(
|
||||
lesson.displayStartTime?.stringHM ?: "",
|
||||
lesson.displaySubjectName?.let {
|
||||
when {
|
||||
lesson.type == Lesson.TYPE_CANCELLED
|
||||
|| lesson.type == Lesson.TYPE_SHIFTED_SOURCE -> it.asStrikethroughSpannable()
|
||||
lesson.type != Lesson.TYPE_NORMAL -> it.asItalicSpannable()
|
||||
else -> it
|
||||
}
|
||||
} ?: ""
|
||||
)
|
||||
// add an item with LessonFull as the tag
|
||||
hours += TextInputDropDown.Item(
|
||||
lesson.displayStartTime?.value?.toLong() ?: -1,
|
||||
text.concat(" "),
|
||||
tag = lesson
|
||||
)
|
||||
}
|
||||
b.timeDropdown.clear().append(hours)
|
||||
|
||||
if (defaultLoaded) {
|
||||
b.timeDropdown.deselect()
|
||||
// select the TEAM_CLASS if possible
|
||||
b.teamDropdown.items.singleOrNull {
|
||||
it.tag is Team && it.tag.type == Team.TYPE_CLASS
|
||||
}?.let {
|
||||
b.teamDropdown.select(it)
|
||||
} ?: b.teamDropdown.deselect()
|
||||
|
||||
// clear subject, teacher selection
|
||||
b.subjectDropdown.deselect()
|
||||
b.teacherDropdown.deselect()
|
||||
}
|
||||
else {
|
||||
editingEvent?.let {
|
||||
b.timeDropdown.select(it.startTime?.value?.toLong())
|
||||
}
|
||||
|
||||
defaultLesson?.let {
|
||||
b.timeDropdown.select(it.displayStartTime?.value?.toLong())
|
||||
}
|
||||
|
||||
defaultHour?.let {
|
||||
b.timeDropdown.select(it.value.toLong())
|
||||
}
|
||||
}
|
||||
defaultLoaded = true
|
||||
b.timeDropdown.isEnabled = true
|
||||
|
||||
// attach a listener to time dropdown
|
||||
b.timeDropdown.setOnChangeListener { item ->
|
||||
when {
|
||||
// no lessons this day
|
||||
item.id == -2L -> {
|
||||
b.timeDropdown.deselect()
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// custom start hour
|
||||
item.id == -1L -> {
|
||||
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// selected a specific lesson
|
||||
else -> {
|
||||
if (item.tag is LessonFull) {
|
||||
// update team, subject, teacher dropdowns,
|
||||
// using the LessonFull from item tag
|
||||
b.teamDropdown.deselect()
|
||||
b.subjectDropdown.deselect()
|
||||
b.teacherDropdown.deselect()
|
||||
item.tag.displayTeamId?.let {
|
||||
b.teamDropdown.select(it)
|
||||
}
|
||||
item.tag.displaySubjectId?.let {
|
||||
b.subjectDropdown.select(it)
|
||||
}
|
||||
item.tag.displayTeacherId?.let {
|
||||
b.teacherDropdown.select(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun saveEvent() {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.settings
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.utils.models.Notification
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ProfileRemoveDialog(
|
||||
val activity: MainActivity,
|
||||
val profileId: Int,
|
||||
val profileName: String
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "ProfileRemoveDialog"
|
||||
}
|
||||
|
||||
private lateinit var job: Job
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogLessonDetailsBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
job = Job()
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.profile_menu_remove_confirm)
|
||||
.setMessage(activity.getString(R.string.profile_menu_remove_confirm_text_format, profileName, profileName))
|
||||
.setPositiveButton(R.string.remove) { _, _ ->
|
||||
removeProfile()
|
||||
}
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}}
|
||||
|
||||
private fun removeProfile() { launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
val profileObject = app.db.profileDao().getByIdNow(profileId) ?: return@async
|
||||
app.db.announcementDao().clear(profileId)
|
||||
app.db.attendanceDao().clear(profileId)
|
||||
app.db.eventDao().clear(profileId)
|
||||
app.db.eventTypeDao().clear(profileId)
|
||||
app.db.gradeDao().clear(profileId)
|
||||
app.db.gradeCategoryDao().clear(profileId)
|
||||
app.db.lessonDao().clear(profileId)
|
||||
app.db.lessonChangeDao().clear(profileId)
|
||||
app.db.luckyNumberDao().clear(profileId)
|
||||
app.db.noticeDao().clear(profileId)
|
||||
app.db.subjectDao().clear(profileId)
|
||||
app.db.teacherDao().clear(profileId)
|
||||
app.db.teamDao().clear(profileId)
|
||||
app.db.messageRecipientDao().clear(profileId)
|
||||
app.db.messageDao().clear(profileId)
|
||||
app.db.endpointTimerDao().clear(profileId)
|
||||
app.db.attendanceTypeDao().clear(profileId)
|
||||
app.db.classroomDao().clear(profileId)
|
||||
app.db.lessonRangeDao().clear(profileId)
|
||||
app.db.noticeTypeDao().clear(profileId)
|
||||
app.db.teacherAbsenceDao().clear(profileId)
|
||||
app.db.teacherAbsenceTypeDao().clear(profileId)
|
||||
app.db.timetableDao().clear(profileId)
|
||||
|
||||
val loginStoreId = profileObject.loginStoreId
|
||||
val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId)
|
||||
if (profilesUsingLoginStore.size == 1) {
|
||||
app.db.loginStoreDao().remove(loginStoreId)
|
||||
}
|
||||
app.db.profileDao().remove(profileId)
|
||||
app.db.metadataDao().deleteAll(profileId)
|
||||
|
||||
val toRemove = ArrayList<Notification>()
|
||||
for (notification in app.appConfig.notifications) {
|
||||
if (notification.profileId == profileId) {
|
||||
toRemove.add(notification)
|
||||
}
|
||||
}
|
||||
app.appConfig.notifications.removeAll(toRemove)
|
||||
|
||||
app.profile = null
|
||||
App.profileId = -1
|
||||
|
||||
app.profileLoadById(app.profileLastId())
|
||||
}
|
||||
deferred.await()
|
||||
dialog.dismiss()
|
||||
activity.reloadTarget()
|
||||
Toast.makeText(activity, R.string.dialog_profile_remove_success, Toast.LENGTH_LONG).show()
|
||||
}}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.sync
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class SyncViewListDialog(
|
||||
val activity: MainActivity,
|
||||
val currentViewId: Int? = null
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "SyncViewListDialog"
|
||||
}
|
||||
|
||||
private lateinit var job: Job
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogLessonDetailsBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
job = Job()
|
||||
|
||||
val viewIds = arrayOf(
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE,
|
||||
MainActivity.DRAWER_ITEM_AGENDA,
|
||||
MainActivity.DRAWER_ITEM_GRADES,
|
||||
MainActivity.DRAWER_ITEM_HOMEWORK,
|
||||
MainActivity.DRAWER_ITEM_BEHAVIOUR,
|
||||
MainActivity.DRAWER_ITEM_ATTENDANCE,
|
||||
MainActivity.DRAWER_ITEM_MESSAGES,
|
||||
MainActivity.DRAWER_ITEM_MESSAGES,
|
||||
MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
)
|
||||
|
||||
val items = arrayOf<String>(
|
||||
app.getString(R.string.menu_timetable),
|
||||
app.getString(R.string.menu_agenda),
|
||||
app.getString(R.string.menu_grades),
|
||||
app.getString(R.string.menu_homework),
|
||||
app.getString(R.string.menu_notices),
|
||||
app.getString(R.string.menu_attendance),
|
||||
app.getString(R.string.title_messages_inbox_single),
|
||||
app.getString(R.string.title_messages_sent_single),
|
||||
app.getString(R.string.menu_announcements)
|
||||
)
|
||||
|
||||
val everything = currentViewId == MainActivity.DRAWER_ITEM_HOME
|
||||
val checkedItems = booleanArrayOf(
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_TIMETABLE,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_AGENDA,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_GRADES,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_HOMEWORK,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_BEHAVIOUR,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_ATTENDANCE,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_MESSAGES && MessagesFragment.pageSelection != 1,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_MESSAGES && MessagesFragment.pageSelection == 1,
|
||||
everything || currentViewId == MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
)
|
||||
val userChooses = checkedItems.toMutableList()
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.dialog_sync_view_list_title)
|
||||
.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
|
||||
userChooses[which] = isChecked
|
||||
}
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
dialog.dismiss()
|
||||
|
||||
val selectedViewIds = userChooses.mapIndexed { index, it ->
|
||||
if (it)
|
||||
viewIds[index] to when (index) {
|
||||
7 -> 1
|
||||
else -> 0
|
||||
}
|
||||
else
|
||||
null
|
||||
}.let {
|
||||
listOfNotNull(*it.toTypedArray())
|
||||
}
|
||||
|
||||
activity.swipeRefreshLayout.isRefreshing = true
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
selectedViewIds
|
||||
).enqueue(activity)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.timetable
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.setText
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualV2Dialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class LessonDetailsDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val lesson: LessonFull,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "LessonDetailsDialog"
|
||||
}
|
||||
|
||||
private lateinit var b: DialogLessonDetailsBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
onShowListener?.invoke(TAG)
|
||||
b = DialogLessonDetailsBinding.inflate(activity.layoutInflater)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.close) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNeutralButton(R.string.add) { dialog, _ ->
|
||||
EventManualV2Dialog(
|
||||
activity,
|
||||
lesson.profileId,
|
||||
lesson,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener
|
||||
)
|
||||
/*MaterialAlertDialogBuilder(activity)
|
||||
.setItems(R.array.main_menu_add_options) { dialog2, which ->
|
||||
dialog2.dismiss()
|
||||
EventManualDialog(activity, lesson.profileId)
|
||||
.show(
|
||||
activity.application as App,
|
||||
null,
|
||||
lesson.displayDate,
|
||||
lesson.displayStartTime,
|
||||
when (which) {
|
||||
1 -> EventManualDialog.DIALOG_HOMEWORK
|
||||
else -> EventManualDialog.DIALOG_EVENT
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { dialog2, _ -> dialog2.dismiss() }
|
||||
.show()*/
|
||||
}
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
}
|
||||
.show()
|
||||
update()
|
||||
}}
|
||||
|
||||
private fun update() {
|
||||
b.lesson = lesson
|
||||
val lessonDate = lesson.displayDate ?: return
|
||||
b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString
|
||||
|
||||
if (lesson.type >= Lesson.TYPE_SHIFTED_SOURCE) {
|
||||
b.shiftedLayout.visibility = View.VISIBLE
|
||||
var otherLessonDate: Date? = null
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
otherLessonDate = lesson.date
|
||||
when {
|
||||
lesson.date != lesson.oldDate -> b.shiftedText.setText(
|
||||
R.string.timetable_lesson_shifted_other_day,
|
||||
lesson.date?.stringY_m_d ?: "?",
|
||||
lesson.startTime?.stringHM ?: "?"
|
||||
)
|
||||
lesson.startTime != lesson.oldStartTime -> b.shiftedText.setText(
|
||||
R.string.timetable_lesson_shifted_same_day,
|
||||
lesson.startTime?.stringHM ?: "?"
|
||||
)
|
||||
else -> b.shiftedText.setText(R.string.timetable_lesson_shifted)
|
||||
}
|
||||
}
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
otherLessonDate = lesson.oldDate
|
||||
when {
|
||||
lesson.date != lesson.oldDate -> b.shiftedText.setText(
|
||||
R.string.timetable_lesson_shifted_from_other_day,
|
||||
lesson.oldDate?.stringY_m_d ?: "?",
|
||||
lesson.oldStartTime?.stringHM ?: "?"
|
||||
)
|
||||
lesson.startTime != lesson.oldStartTime -> b.shiftedText.setText(
|
||||
R.string.timetable_lesson_shifted_from_same_day,
|
||||
lesson.oldStartTime?.stringHM ?: "?"
|
||||
)
|
||||
else -> b.shiftedText.setText(R.string.timetable_lesson_shifted_from)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.shiftedGoTo.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
val dateStr = otherLessonDate?.stringY_m_d ?: return@setOnClickListener
|
||||
val intent = Intent(TimetableFragment.ACTION_SCROLL_TO_DATE).apply {
|
||||
putExtra("date", dateStr)
|
||||
}
|
||||
activity.sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
b.shiftedLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldSubjectId != null && lesson.subjectId != lesson.oldSubjectId) {
|
||||
b.oldSubjectName = lesson.oldSubjectName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displaySubjectId != null) {
|
||||
b.subjectName = lesson.subjectName
|
||||
}
|
||||
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeacherId != null && lesson.teacherId != lesson.oldTeacherId) {
|
||||
b.oldTeacherName = lesson.oldTeacherName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayTeacherId != null) {
|
||||
b.teacherName = lesson.teacherName
|
||||
}
|
||||
|
||||
if (lesson.oldClassroom != null && lesson.classroom != lesson.oldClassroom) {
|
||||
b.oldClassroom = lesson.oldClassroom
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayClassroom != null) {
|
||||
b.classroom = lesson.classroom
|
||||
}
|
||||
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeamId != null && lesson.teamId != lesson.oldTeamId) {
|
||||
b.oldTeamName = lesson.oldTeamName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayTeamId != null) {
|
||||
b.teamName = lesson.teamName
|
||||
}
|
||||
}
|
||||
}
|
@ -6,15 +6,16 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.applandeo.materialcalendarview.CalendarView;
|
||||
import com.applandeo.materialcalendarview.EventDay;
|
||||
@ -27,6 +28,7 @@ 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 com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
@ -34,13 +36,13 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog;
|
||||
@ -51,11 +53,11 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEve
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceCounter;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
|
||||
|
||||
@ -102,7 +104,7 @@ public class AgendaFragment extends Fragment {
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_add_event)
|
||||
.withDescription(R.string.menu_add_event_desc)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
new MaterialDialog.Builder(activity)
|
||||
@ -122,7 +124,7 @@ public class AgendaFragment extends Fragment {
|
||||
}),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_agenda_change_view)
|
||||
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar : CommunityMaterial.Icon2.cmd_view_list)
|
||||
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar_outline : CommunityMaterial.Icon.cmd_format_list_bulleted_square)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
|
||||
@ -133,7 +135,7 @@ public class AgendaFragment extends Fragment {
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_EVENT, true));
|
||||
|
@ -58,7 +58,7 @@ public class AnnouncementsFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ANNOUNCEMENT, true));
|
||||
|
@ -95,7 +95,7 @@ public class AttendanceFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true));
|
||||
@ -133,7 +133,7 @@ public class AttendanceFragment extends Fragment {
|
||||
CafeBar.builder(activity)
|
||||
.to(activity.getNavView().getCoordinator())
|
||||
.content(R.string.sync_old_data_info)
|
||||
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_sync).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
|
||||
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_download_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
|
||||
.positiveText(R.string.refresh)
|
||||
.positiveColor(0xff4caf50)
|
||||
.negativeText(R.string.ok)
|
||||
|
@ -69,7 +69,7 @@ public class BehaviourFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_NOTICE, true));
|
||||
|
@ -9,21 +9,20 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import com.mikepenz.iconics.utils.colorRes
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeFull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.bs
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NoticesAdapter//getting the context and product list with constructor
|
||||
(private val context: Context, var noticeList: List<NoticeFull>) : RecyclerView.Adapter<NoticesAdapter.ViewHolder>() {
|
||||
@ -50,15 +49,15 @@ class NoticesAdapter//getting the context and product list with constructor
|
||||
holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString
|
||||
|
||||
if (notice.type == Notice.TYPE_POSITIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle_outline)
|
||||
.colorRes(R.color.md_green_600)
|
||||
.sizeDp(36))
|
||||
} else if (notice.type == Notice.TYPE_NEGATIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram_outline)
|
||||
.colorRes(R.color.md_red_600)
|
||||
.sizeDp(36))
|
||||
} else {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_message_outline)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, SzkolnyFont.Icon.szf_message_processing_outline)
|
||||
.colorRes(R.color.md_blue_500)
|
||||
.sizeDp(36))
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.error
|
||||
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ErrorDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val exception: Exception
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "ErrorDialog"
|
||||
}
|
||||
|
||||
private lateinit var job: Job
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogLessonDetailsBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
job = Job()
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error_occured)
|
||||
.setMessage(exception.message + "\n" + Log.getStackTraceString(exception))
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.error
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
|
||||
class ErrorSnackbar(val activity: AppCompatActivity) {
|
||||
companion object {
|
||||
private const val TAG = "ErrorSnackbar"
|
||||
}
|
||||
|
||||
private var snackbar: Snackbar? = null
|
||||
private lateinit var coordinator: CoordinatorLayout
|
||||
private val errors = mutableListOf<ApiError>()
|
||||
|
||||
fun setCoordinator(coordinatorLayout: CoordinatorLayout, showAbove: View? = null) {
|
||||
this.coordinator = coordinatorLayout
|
||||
snackbar = Snackbar.make(coordinator, R.string.snackbar_error_text, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbar?.setAction(R.string.more) {
|
||||
|
||||
}
|
||||
val bgColor = ColorUtils.compositeColors(
|
||||
getColorFromAttr(activity, R.attr.colorOnSurface) and 0xcfffffff.toInt(),
|
||||
getColorFromAttr(activity, R.attr.colorSurface)
|
||||
)
|
||||
snackbar?.setBackgroundTint(bgColor)
|
||||
showAbove?.let { snackbar?.anchorView = it }
|
||||
}
|
||||
|
||||
fun addError(apiError: ApiError): ErrorSnackbar {
|
||||
errors += apiError
|
||||
snackbar?.setText(apiError.getStringReason(activity))
|
||||
return this
|
||||
}
|
||||
|
||||
fun show() = snackbar?.show()
|
||||
fun dismiss() = snackbar?.dismiss()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user