Compare commits

...

15 Commits

Author SHA1 Message Date
c8c1fe5367 [4.0-beta.8] Update build.gradle and signing. 2020-02-14 22:39:13 +01:00
71128e0244 [Messages/Compose] Fix text layout jumping and scrolling off-screen when typing a long message. 2020-02-14 22:28:58 +01:00
453bcaa1f6 [Dialog/Day] Show lesson changes and teacher absences in the day dialog. 2020-02-13 23:04:29 +01:00
48898ab1d4 [Widgets] Fix profile separator text color. 2020-02-13 13:44:20 +01:00
a095520d0d [UI/Agenda] Fix subject, teacher and time display in all day events. 2020-02-13 13:33:04 +01:00
2e0c6fa6a5 [Errors] Add request body in error reporting. 2020-02-13 09:57:59 +01:00
bfbc0861df [Notifications] Disable notifications about past events & timetable changes. 2020-02-12 23:05:03 +01:00
3a500f3f28 [API/Librus] Fix student name not normalized, short name not having a trailing dot (remove legacy code). 2020-02-12 19:20:39 +01:00
df8094c39c [Dialog/LessonChanges] Add a new lesson changes dialog. 2020-02-11 16:34:40 +01:00
448fd0e884 [API/Librus] Fix marking removed announcements as read. 2020-02-10 23:53:04 +01:00
4717b4549e [Feedback] Fix crashing when null message is received. 2020-02-09 23:03:37 +01:00
57a8d72f1c [Feedback] Fix received messages not displaying for user. 2020-02-09 23:00:01 +01:00
7e57617e04 [Feedback] Update proguard rules for feedback message entity. 2020-02-09 22:03:41 +01:00
37ddd643ac [Feedback] Hide notification when feedback is open. Fix mixing messages when a thread is open. 2020-02-09 15:10:03 +01:00
bcf3fef303 [Widget/Timetable] Fix no lessons text not legible on dark background. 2020-02-09 14:34:38 +01:00
28 changed files with 418 additions and 143 deletions

View File

@ -24,6 +24,7 @@
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0x21, 0xd7, 0x64, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0x89, 0x62, 0x5d, 0x4d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -116,6 +116,7 @@ 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_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202

View File

@ -63,6 +63,7 @@ open class LibrusApi(open val data: DataLibrus) {
"NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE
"InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
else -> ERROR_LIBRUS_API_OTHER
}.let { errorCode ->
if (errorCode !in ignoreErrors) {

View File

@ -6,12 +6,13 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
import pl.szczodrzynski.edziennik.data.api.POST
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
class LibrusApiAnnouncementMarkAsRead(
override val data: DataLibrus,
@ -24,7 +25,10 @@ class LibrusApiAnnouncementMarkAsRead(
init {
apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST,
ignoreErrors = listOf(ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS)) {
ignoreErrors = listOf(
ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS,
ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
)) {
announcement.seen = true
EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement))

View File

@ -58,11 +58,6 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () ->
data.apiAccessToken = accountToken
data.apiTokenExpiryTime = response.getUnixDate() + 6 * 60 * 60
// TODO remove this
data.profile?.studentNameLong = json.getString("studentName") ?: ""
val nameParts = json.getString("studentName")?.split(" ")
data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
onSuccess()
}
}

View File

@ -72,7 +72,7 @@ class ApiError(val tag: String, var errorCode: Int) {
fun toReportableError(context: Context): ErrorReportRequest.Error {
val requestString = request?.let {
it.method() + " " + it.url() + "\n" + it.headers()
it.method() + " " + it.url() + "\n" + it.headers() + "\n\n" + (it.jsonBody()?.toString() ?: "") + (it.textBody() ?: "")
}
val responseString = response?.let {
if (it.parserErrorBody == null) {

View File

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MDdK88yzVk===.$param2".sha256()
return "$param1.MTIzNDU2Nzg5MD4iBdQfyv===.$param2".sha256()
}
}

View File

@ -37,7 +37,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
}
private fun timetableNotifications() {
for (lesson in app.db.timetableDao().getNotNotifiedNow()) {
for (lesson in app.db.timetableDao().getNotNotifiedNow().filter { it.displayDate == null || it.displayDate!! >= today }) {
val text = app.getString(
R.string.notification_lesson_change_format,
lesson.getDisplayChangeType(app),
@ -58,7 +58,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
}
private fun eventNotifications() {
for (event in app.db.eventDao().notNotifiedNow) {
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today }) {
val text = if (event.type == Event.TYPE_HOMEWORK)
app.getString(
if (event.subjectLongName.isNullOrEmpty())
@ -93,7 +93,7 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
}
fun sharedEventNotifications() {
for (event in app.db.eventDao().notNotifiedNow.filter { it.sharedBy != null }) {
for (event in app.db.eventDao().notNotifiedNow.filter { it.eventDate >= today && it.sharedBy != null }) {
val text = app.getString(
R.string.notification_shared_event_format,
event.sharedByName,
@ -274,4 +274,4 @@ class Notifications(val app: App, val notifications: MutableList<Notification>,
)
}
}
}
}

View File

@ -41,6 +41,14 @@ interface TeacherAbsenceDao {
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
fun getAllByDateFull(profileId: Int, date: Date): LiveData<List<TeacherAbsenceFull>>
@Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " +
"metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " +
"LEFT JOIN teachers USING (profileId, teacherId) " +
"LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE +
" AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " +
"AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo")
fun getAllByDateNow(profileId: Int, date: Date): List<TeacherAbsenceFull>
@Query("DELETE FROM teacherAbsence WHERE profileId = :profileId")
fun clear(profileId: Int)
}

View File

@ -58,7 +58,14 @@ interface TimetableDao {
WHERE timetable.profileId = :profileId AND type != -1 AND type != 0
ORDER BY id, type
""")
fun getAllChangesNow(profileId: Int): List<Lesson>
fun getAllChangesNow(profileId: Int): List<LessonFull>
@Query("""
$QUERY
WHERE timetable.profileId = :profileId AND type != -1 AND type != 0 AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
ORDER BY id, type
""")
fun getChangesForDateNow(profileId: Int, date: Date): List<LessonFull>
@Query("""
$QUERY

View File

@ -47,7 +47,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
)
"appUpdate" -> launch { UpdateWorker.runNow(app, app.gson.fromJson(message.data.getString("update"), Update::class.java)) }
"feedbackMessage" -> launch {
val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java)
val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java) ?: return@launch
feedbackMessage(message)
}
}
@ -74,18 +74,20 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
}
withContext(Dispatchers.Default) {
app.db.feedbackMessageDao().add(message)
val notification = Notification(
id = System.currentTimeMillis(),
title = "Wiadomość od ${message.senderName}",
text = message.text,
type = Notification.TYPE_FEEDBACK_MESSAGE,
profileId = null,
profileName = "Wiadomość od ${message.senderName}"
).addExtra("action", "feedbackMessage").addExtra("feedbackMessageDeviceId", message.deviceId)
app.db.notificationDao().add(notification)
PostNotifications(app, listOf(notification))
if (!EventBus.getDefault().hasSubscriberForEvent(FeedbackMessageEvent::class.java)) {
val notification = Notification(
id = System.currentTimeMillis(),
title = "Wiadomość od ${message.senderName}",
text = message.text,
type = Notification.TYPE_FEEDBACK_MESSAGE,
profileId = null,
profileName = "Wiadomość od ${message.senderName}"
).addExtra("action", "feedbackMessage").addExtra("feedbackMessageDeviceId", message.deviceId)
app.db.notificationDao().add(notification)
PostNotifications(app, listOf(notification))
}
EventBus.getDefault().postSticky(FeedbackMessageEvent(message))
}
EventBus.getDefault().postSticky(FeedbackMessageEvent(message))
}
private fun sharedEvent(teamCode: String, jsonStr: String, message: String) {

View File

@ -10,17 +10,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import kotlinx.android.synthetic.main.row_lesson_change_item.view.*
import kotlinx.android.synthetic.main.row_teacher_absence_item.view.*
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.DialogDayBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.setText
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Week
@ -77,13 +76,51 @@ class DayDialog(
update()
}}
private fun update() {
private fun update() { launch {
b.dayDate.setText(
R.string.dialog_day_date_format,
Week.getFullDayName(date.weekDay),
date.formattedString
)
val lessonChanges = withContext(Dispatchers.Default) {
app.db.timetableDao().getChangesForDateNow(profileId, date)
}
lessonChanges.ifNotEmpty {
b.lessonChangeContainer.visibility = View.VISIBLE
b.lessonChangeContainer.lessonChangeCount.text = it.size.toString()
b.lessonChangeLayout.onClick {
LessonChangeDialog(
activity,
profileId,
date,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
}
val teacherAbsences = withContext(Dispatchers.Default) {
app.db.teacherAbsenceDao().getAllByDateNow(profileId, date)
}
teacherAbsences.ifNotEmpty {
b.teacherAbsenceContainer.visibility = View.VISIBLE
b.teacherAbsenceContainer.teacherAbsenceCount.text = it.size.toString()
b.teacherAbsenceLayout.onClick {
TeacherAbsenceDialog(
activity,
profileId,
date,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
}
adapter = EventListAdapter(
activity,
onItemClick = {
@ -126,5 +163,5 @@ class DayDialog(
b.eventsNoData.visibility = View.VISIBLE
}
})
}
}}
}

View File

@ -0,0 +1,189 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-19.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.lessonchange
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
import pl.szczodrzynski.navlib.getColorFromAttr
class LessonChangeAdapter(
val context: Context,
private val onItemClick: ((lesson: LessonFull) -> Unit)? = null
) : RecyclerView.Adapter<LessonChangeAdapter.ViewHolder>() {
var items = listOf<LessonFull>()
private val arrowRight = ""
private val bullet = ""
private val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(context)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = TimetableLessonBinding.inflate(inflater, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val lesson = items[position]
val b = holder.b
b.root.onClick {
onItemClick?.invoke(lesson)
}
val startTime = lesson.displayStartTime ?: return
val endTime = lesson.displayEndTime ?: return
val timeRange = "${startTime.stringHM} - ${endTime.stringHM}".asColoredSpannable(colorSecondary)
b.unread = false
b.root.background = null
b.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = 16.dp
}
// teacher
val teacherInfo = if (lesson.teacherId != null && lesson.teacherId == lesson.oldTeacherId)
lesson.teacherName ?: "?"
else
mutableListOf<CharSequence>().apply {
lesson.oldTeacherName?.let { add(it.asStrikethroughSpannable()) }
lesson.teacherName?.let { add(it) }
}.concat(arrowRight)
// team
val teamInfo = if (lesson.teamId != null && lesson.teamId == lesson.oldTeamId)
lesson.teamName ?: "?"
else
mutableListOf<CharSequence>().apply {
lesson.oldTeamName?.let { add(it.asStrikethroughSpannable()) }
lesson.teamName?.let { add(it) }
}.concat(arrowRight)
// classroom
val classroomInfo = if (lesson.classroom != null && lesson.classroom == lesson.oldClassroom)
lesson.classroom ?: "?"
else
mutableListOf<CharSequence>().apply {
lesson.oldClassroom?.let { add(it.asStrikethroughSpannable()) }
lesson.classroom?.let { add(it) }
}.concat(arrowRight)
b.lessonNumber = lesson.displayLessonNumber
b.subjectName.text = lesson.displaySubjectName?.let {
if (lesson.type == Lesson.TYPE_CANCELLED || lesson.type == Lesson.TYPE_SHIFTED_SOURCE)
it.asStrikethroughSpannable().asColoredSpannable(colorSecondary)
else
it
}
b.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
b.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
when (lesson.type) {
Lesson.TYPE_NORMAL -> {
b.annotationVisible = false
}
Lesson.TYPE_CANCELLED -> {
b.annotationVisible = true
b.annotation.setText(R.string.timetable_lesson_cancelled)
b.annotation.background.colorFilter = PorterDuffColorFilter(
getColorFromAttr(context, R.attr.timetable_lesson_cancelled_color),
PorterDuff.Mode.SRC_ATOP
)
//lb.subjectName.typeface = Typeface.DEFAULT
}
Lesson.TYPE_CHANGE -> {
b.annotationVisible = true
when {
lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId
&& lesson.oldSubjectName != null && lesson.oldTeacherName != null ->
b.annotation.setText(
R.string.timetable_lesson_change_format,
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
)
lesson.subjectId != lesson.oldSubjectId && lesson.oldSubjectName != null ->
b.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldSubjectName ?: "?"
)
lesson.teacherId != lesson.oldTeacherId && lesson.oldTeacherName != null ->
b.annotation.setText(
R.string.timetable_lesson_change_format,
lesson.oldTeacherName ?: "?"
)
else -> b.annotation.setText(R.string.timetable_lesson_change)
}
b.annotation.background.colorFilter = PorterDuffColorFilter(
getColorFromAttr(context, R.attr.timetable_lesson_change_color),
PorterDuff.Mode.SRC_ATOP
)
}
Lesson.TYPE_SHIFTED_SOURCE -> {
b.annotationVisible = true
when {
lesson.date != lesson.oldDate && lesson.date != null ->
b.annotation.setText(
R.string.timetable_lesson_shifted_other_day,
lesson.date?.stringY_m_d ?: "?",
lesson.startTime?.stringHM ?: ""
)
lesson.startTime != lesson.oldStartTime && lesson.startTime != null ->
b.annotation.setText(
R.string.timetable_lesson_shifted_same_day,
lesson.startTime?.stringHM ?: "?"
)
else -> b.annotation.setText(R.string.timetable_lesson_shifted)
}
b.annotation.background.setTintColor(R.attr.timetable_lesson_shifted_source_color.resolveAttr(context))
}
Lesson.TYPE_SHIFTED_TARGET -> {
b.annotationVisible = true
when {
lesson.date != lesson.oldDate && lesson.oldDate != null ->
b.annotation.setText(
R.string.timetable_lesson_shifted_from_other_day,
lesson.oldDate?.stringY_m_d ?: "?",
lesson.oldStartTime?.stringHM ?: ""
)
lesson.startTime != lesson.oldStartTime && lesson.oldStartTime != null ->
b.annotation.setText(
R.string.timetable_lesson_shifted_from_same_day,
lesson.oldStartTime?.stringHM ?: "?"
)
else -> b.annotation.setText(R.string.timetable_lesson_shifted_from)
}
b.annotation.background.colorFilter = PorterDuffColorFilter(
getColorFromAttr(context, R.attr.timetable_lesson_shifted_target_color),
PorterDuff.Mode.SRC_ATOP
)
}
}
}
override fun getItemCount() = items.size
class ViewHolder(val b: TimetableLessonBinding) : RecyclerView.ViewHolder(b.root)
}

View File

@ -1,69 +0,0 @@
package pl.szczodrzynski.edziennik.ui.dialogs.lessonchange;
import android.content.Context;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding;
import pl.szczodrzynski.edziennik.utils.models.Date;
public class LessonChangeDialog {
private App app;
private Context context;
private int profileId;
public LessonChangeDialog(Context context) {
this.context = context;
this.profileId = App.Companion.getProfileId();
}
public LessonChangeDialog(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
private MaterialDialog dialog;
private DialogLessonChangeListBinding b;
public void show(App _app, Date date)
{
this.app = _app;
dialog = new MaterialDialog.Builder(context)
.title(date.getFormattedString())
.customView(R.layout.dialog_lesson_change_list, false)
.positiveText(R.string.close)
.autoDismiss(false)
.onPositive((dialog, which) -> dialog.dismiss())
.show();
if (dialog.getCustomView() == null)
return;
b = DataBindingUtil.bind(dialog.getCustomView());
if (b == null)
return;
b.lessonChangeView.setHasFixedSize(true);
b.lessonChangeView.setLayoutManager(new LinearLayoutManager(context));
/*app.db.lessonDao().getAllByDate(profileId, date, Time.getNow()).observe((LifecycleOwner) context, lessons -> {
if (app == null || app.profile == null || b == null)
return;
List<LessonFull> changedLessons = new ArrayList<>();
for (LessonFull lesson: lessons) {
if (lesson.changeId != 0) {
changedLessons.add(lesson);
}
}
app.db.eventDao().getAllByDate(profileId, date).observe((LifecycleOwner) context, events -> {
TimetableAdapter adapter = new TimetableAdapter(context, date, changedLessons, events == null ? new ArrayList<>() : events);
b.lessonChangeView.setAdapter(adapter);
b.lessonChangeView.setVisibility(View.VISIBLE);
});
});*/
}
}

View File

@ -0,0 +1,76 @@
package pl.szczodrzynski.edziennik.ui.dialogs.lessonchange
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class LessonChangeDialog(
val activity: AppCompatActivity,
val profileId: Int,
private val defaultDate: Date,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
const val TAG = "LessonChangeDialog"
}
private val app by lazy { activity.application as App }
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var b: DialogLessonChangeListBinding
private lateinit var dialog: AlertDialog
init { run {
if (activity.isFinishing)
return@run
job = Job()
onShowListener?.invoke(TAG)
b = DialogLessonChangeListBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(defaultDate.formattedString)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.create()
loadLessonChanges()
}}
private fun loadLessonChanges() { launch {
val lessonChanges = withContext(Dispatchers.Default) {
app.db.timetableDao().getChangesForDateNow(profileId, defaultDate)
}
val adapter = LessonChangeAdapter(
activity,
onItemClick = {
LessonDetailsDialog(
activity,
it,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
).apply {
items = lessonChanges
}
b.lessonChangeView.adapter = adapter
b.lessonChangeView.layoutManager = LinearLayoutManager(activity)
dialog.show()
}}
}

View File

@ -32,6 +32,7 @@ import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeCounter
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEvent
@ -215,7 +216,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
eventList.add(BaseCalendarEvent(
"${event.typeName} - ${event.topic}",
"",
if (event.startTime == null) getString(R.string.agenda_event_all_day) else event.startTime!!.stringHM +
(if (event.startTime == null) getString(R.string.agenda_event_all_day) else event.startTime!!.stringHM) +
(event.subjectLongName?.let { ", $it" } ?: "") +
(event.teacherFullName?.let { ", $it" } ?: "") +
(event.teamName?.let { ", $it" } ?: ""),
@ -250,7 +251,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
when (event) {
is BaseCalendarEvent -> DayDialog(activity, app.profileId, date)
// is LessonChangeEvent -> todo
is LessonChangeEvent -> LessonChangeDialog(activity, app.profileId, date)
is TeacherAbsenceEvent -> TeacherAbsenceDialog(activity, app.profileId, date)
}
}

View File

@ -70,9 +70,14 @@ class FeedbackFragment : Fragment(), CoroutineScope {
fun onFeedbackMessageEvent(event: FeedbackMessageEvent) {
EventBus.getDefault().removeStickyEvent(event)
val message = event.message
val chatMessage = getChatMessage(message)
if (message.received) chatView.receive(chatMessage)
else chatView.send(chatMessage)
if (currentDeviceId == null || message.deviceId == currentDeviceId) {
val chatMessage = getChatMessage(message)
if (message.received) chatView.receive(chatMessage)
else chatView.send(chatMessage)
}
else {
Toast.makeText(context, "${message.senderName}: Nowa wiadomość w innym wątku.", Toast.LENGTH_LONG).show()
}
}
private val users = mutableMapOf(

View File

@ -155,7 +155,7 @@ public class WidgetTimetableFactory implements RemoteViewsService.RemoteViewsFac
views.setViewVisibility(R.id.widgetTimetableContent, View.GONE);
views.setTextViewText(R.id.widgetTimetableProfileName, lesson.separatorProfileName);
views.setTextViewTextSize(R.id.widgetTimetableProfileName, COMPLEX_UNIT_SP, lesson.bigStyle ? 30 : 20);
views.setTextColor(R.id.widgetTimetableProfileName, lesson.darkTheme ? 0xff000000 : 0xffffffff);
views.setTextColor(R.id.widgetTimetableProfileName, lesson.darkTheme ? 0xffffffff : 0xff000000);
Intent intent = new Intent();
intent.putExtra("profileId", lesson.profileId);

View File

@ -30,29 +30,43 @@
android:textIsSelectable="true"
tools:text="wtorek, 17 grudnia" />
<include
android:id="@+id/lessonChangeContainer"
layout="@layout/row_lesson_change_item"
<LinearLayout
android:id="@+id/lessonChangeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="5dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="5dp"
android:visibility="gone"
tools:visibility="visible" />
android:background="?selectableItemBackground">
<include
android:id="@+id/teacherAbsenceContainer"
layout="@layout/row_teacher_absence_item"
<include
android:id="@+id/lessonChangeContainer"
layout="@layout/row_lesson_change_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="5dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="5dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout
android:id="@+id/teacherAbsenceLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:visibility="gone"
tools:visibility="visible" />
android:background="?selectableItemBackground">
<include
android:id="@+id/teacherAbsenceContainer"
layout="@layout/row_teacher_absence_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout
android:id="@+id/eventsNoData"

View File

@ -11,5 +11,5 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:clipToPadding="false"
tools:listitem="@layout/row_timetable_item" />
</layout>
tools:listitem="@layout/timetable_lesson" />
</layout>

View File

@ -54,7 +54,8 @@
android:focusableInTouchMode="true"
android:hint="@string/messages_compose_subject_hint"
android:singleLine="true"
tools:text="kachoomba"/>
tools:text="kachoomba"
android:inputType="textCapSentences|textAutoCorrect|textShortMessage|textAutoComplete|textEmailSubject"/>
</com.google.android.material.textfield.TextInputLayout>
@ -66,7 +67,7 @@
app:boxBackgroundMode="filled"
app:counterEnabled="true"
tools:counterMaxLength="1983">
<com.google.android.material.textfield.TextInputEditText
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -77,7 +78,7 @@
android:ems="10"
android:gravity="start|top"
tools:text="Witam,\n\nchciałem przekazać bardzo ważną wiadomość.\nJest to cytat znanej osoby.\n\n&quot;To jest tak, ale nie. Pamiętaj żeby oczywiście&quot;\n\nCytat ma bardzo duże przesłanie i ogromny wpływ na dzisiejszą kulturę i rozwój współczesnej cywilizacji.\n\nJako zadanie domowe, należy wypisać 5 pierwszych liczb pierwszych. Uzasadnij decyzję, odwołując się do cytatu i 3 innych przykładów z literatury lub filmu.\n\nPozdrawiam,\nJa."
android:inputType="textMultiLine" />
android:inputType="textMultiLine|textAutoCorrect|textLongMessage|textAutoComplete|textCapSentences" />
</com.google.android.material.textfield.TextInputLayout>
<!--<com.google.android.material.button.MaterialButtonToggleGroup
@ -130,4 +131,4 @@
</LinearLayout>
</ScrollView>
</layout>
</layout>

View File

@ -1,7 +1,7 @@
<androidx.cardview.widget.CardView android:id="@+id/lesson_change_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:cardBackgroundColor="#78909c"
app:cardBackgroundColor="#78909c"
app:cardCornerRadius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

View File

@ -1,7 +1,7 @@
<androidx.cardview.widget.CardView android:id="@+id/teacherAbsenceCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:cardBackgroundColor="#ff1744"
app:cardBackgroundColor="#ff1744"
app:cardCornerRadius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"

View File

@ -17,7 +17,7 @@
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:foreground="@drawable/bg_rounded_ripple_4dp"
tools:padding="32dp">

View File

@ -93,7 +93,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:textColor="@color/primaryTextLight"
android:textColor="@color/primaryTextDark"
android:gravity="center"
android:textSize="16sp"
android:text="@string/widget_timetable_no_timetable"
@ -106,7 +106,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:textColor="@color/primaryTextLight"
android:textColor="@color/primaryTextDark"
android:gravity="center"
android:textSize="16sp"
android:text="@string/widget_timetable_no_lessons_found"
@ -115,4 +115,4 @@
</FrameLayout>
</LinearLayout>
</LinearLayout>

View File

@ -90,6 +90,7 @@
<string name="error_180" translatable="false">ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN</string>
<string name="error_181" translatable="false">ERROR_LIBRUS_API_MAINTENANCE</string>
<string name="error_182" translatable="false">ERROR_LIBRUS_PORTAL_MAINTENANCE</string>
<string name="error_183" translatable="false">ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM</string>
<string name="error_201" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN</string>
<string name="error_202" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD</string>
@ -256,6 +257,7 @@
<string name="error_180_reason">Librus Portal: nieprawidłowe dane logowania</string>
<string name="error_181_reason">Librus API: przerwa techniczna</string>
<string name="error_182_reason">Librus Portal: przerwa techniczna</string>
<string name="error_183_reason">Wystąpił problem z tablicą ogłoszeń</string>
<string name="error_201_reason">Nieprawidłowy login lub hasło</string>
<string name="error_202_reason">Podano stare hasło</string>

View File

@ -5,8 +5,8 @@ buildscript {
kotlin_version = '1.3.61'
release = [
versionName: "4.0-beta.7",
versionCode: 4000007
versionName: "4.0-beta.8",
versionCode: 4000008
]
setup = [