diff --git a/app/build.gradle b/app/build.gradle index 2923e5f58..4cda75fac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,8 +72,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - implementation('com.github.wulkanowy:api:0a4317f651') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:46f09bdf34') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" @@ -125,6 +124,8 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'org.mockito:mockito-android:2.23.4' androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + + implementation "com.hootsuite.android:nachos:1.1.1" } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt index 552c2213f..03a8c0953 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/AttendanceLocalTest.kt @@ -41,7 +41,7 @@ class AttendanceLocalTest { )) val attendance = attendanceLocal - .getAttendance(Semester(1, 2, "", 1, 3, true), + .getAttendance(Semester(1, 2, "", 1, 3, true, 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/CompletedLessonsLocalTest.kt index a7b6c302c..62335b057 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/CompletedLessonsLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/CompletedLessonsLocalTest.kt @@ -42,7 +42,7 @@ class CompletedLessonsLocalTest { )) val completed = completedLessonsLocal - .getCompletedLessons(Semester(1, 2, "", 1, 3, true), + .getCompletedLessons(Semester(1, 2, "", 1, 3, true, 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt index c5cdca906..f383a41c7 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/ExamLocalTest.kt @@ -41,7 +41,7 @@ class ExamLocalTest { )) val exams = examLocal - .getExams(Semester(1, 2, "", 1, 3, true), + .getExams(Semester(1, 2, "", 1, 3, true, 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/LuckyNumberLocalTest.kt index 22b38cdc4..b4076bec5 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/LuckyNumberLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/LuckyNumberLocalTest.kt @@ -37,7 +37,7 @@ class LuckyNumberLocalTest { fun saveAndReadTest() { luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) - val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, true), + val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, true, 1, 1), LocalDate.of(2019, 1, 20) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/RecipientLocalTest.kt new file mode 100644 index 000000000..4932d8148 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/RecipientLocalTest.kt @@ -0,0 +1,61 @@ +package io.github.wulkanowy.data.repositories.local + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.recipient.RecipientLocal +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.threeten.bp.LocalDateTime +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class RecipientLocalTest { + + private lateinit var recipientLocal: RecipientLocal + + private lateinit var testDb: AppDatabase + + @Before + fun createDb() { + testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) + .build() + recipientLocal = RecipientLocal(testDb.recipientDao) + } + + @After + fun closeDb() { + testDb.close() + } + + @Test + fun saveAndReadTest() { + recipientLocal.saveRecipients(listOf( + Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), + Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), + Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") + )) + + val recipients = recipientLocal.getRecipients( + Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", true, LocalDateTime.now()), + 2, + ReportingUnit(1, 4, "", 0, "", emptyList()) + ).blockingGet() + + assertEquals(2, recipients.size) + assertEquals(1, recipients[0].studentId) + assertEquals("3rPracownik", recipients[1].realId) + assertEquals("Kowalski Jan", recipients[0].name) + assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName) + assertEquals(3, recipients[0].loginId) + assertEquals(4, recipients[1].unitId) + assertEquals(2, recipients[0].role) + assertEquals("hash", recipients[1].hash) + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt index 2cf9625cd..c90c73330 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/local/TimetableLocalTest.kt @@ -45,7 +45,7 @@ class TimetableLocalTest { )) val exams = timetableDb.getTimetable( - Semester(1, 2, "", 1, 1, true), + Semester(1, 2, "", 1, 1, true, 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ).blockingGet() diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index 27bab352a..29b0d2218 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -121,4 +121,12 @@ internal class RepositoryModule { @Singleton @Provides fun provideCompletedLessonsDao(database: AppDatabase) = database.completedLessonsDao + + @Singleton + @Provides + fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao + + @Singleton + @Provides + fun provideRecipientDao(database: AppDatabase) = database.recipientDao } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index de3429dac..50619a5f1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -16,6 +16,8 @@ import io.github.wulkanowy.data.db.dao.HomeworkDao import io.github.wulkanowy.data.db.dao.LuckyNumberDao import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.dao.ReportingUnitDao import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.dao.SubjectDao @@ -30,6 +32,8 @@ import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Subject @@ -38,6 +42,7 @@ import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration5 +import io.github.wulkanowy.data.db.migrations.Migration6 import javax.inject.Singleton @Singleton @@ -56,7 +61,9 @@ import javax.inject.Singleton Homework::class, Subject::class, LuckyNumber::class, - CompletedLesson::class + CompletedLesson::class, + ReportingUnit::class, + Recipient::class ], version = AppDatabase.VERSION_SCHEMA, exportSchema = false @@ -65,7 +72,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 5 + const val VERSION_SCHEMA = 6 fun newInstance(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") @@ -76,7 +83,8 @@ abstract class AppDatabase : RoomDatabase() { Migration2(), Migration3(), Migration4(), - Migration5() + Migration5(), + Migration6() ) .build() } @@ -109,4 +117,8 @@ abstract class AppDatabase : RoomDatabase() { abstract val luckyNumberDao: LuckyNumberDao abstract val completedLessonsDao: CompletedLessonsDao + + abstract val reportingUnitDao: ReportingUnitDao + + abstract val recipientDao: RecipientDao } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt index a550df893..f0d11b264 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -8,6 +8,8 @@ import org.threeten.bp.LocalDateTime import org.threeten.bp.Month import org.threeten.bp.ZoneOffset import java.util.Date +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken class Converters { @@ -36,4 +38,14 @@ class Converters { @TypeConverter fun intToMonth(value: Int?) = value?.let { Month.of(it) } + + @TypeConverter + fun intListToGson(list: List): String { + return Gson().toJson(list) + } + + @TypeConverter + fun gsonToIntList(value: String): List { + return Gson().fromJson(value, object : TypeToken>() {}.type) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt new file mode 100644 index 000000000..7c5fd6ca6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Recipient +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface RecipientDao { + + @Insert + fun insertAll(messages: List) + + @Delete + fun deleteAll(messages: List) + + @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") + fun load(studentId: Int, role: Int, unitId: Int): Maybe> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt new file mode 100644 index 000000000..1898390a9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Maybe +import javax.inject.Singleton + +@Singleton +@Dao +interface ReportingUnitDao { + + @Insert + fun insertAll(reportingUnits: List) + + @Delete + fun deleteAll(reportingUnits: List) + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") + fun load(studentId: Int): Maybe> + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") + fun loadOne(studentId: Int, unitId: Int): Maybe +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt new file mode 100644 index 000000000..3021da72d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "Recipients") +data class Recipient( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: String, + + val name: String, + + @ColumnInfo(name = "real_name") + val realName: String, + + @ColumnInfo(name = "login_id") + val loginId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int, + + val role: Int, + + val hash: String + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + + override fun toString() = name +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt new file mode 100644 index 000000000..601d8aac7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "ReportingUnits") +data class ReportingUnit( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "short") + val shortName: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "sender_name") + val senderName: String, + + val roles: List + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt index 462967922..0f44fa2d9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -24,7 +24,13 @@ data class Semester( val semesterName: Int, @ColumnInfo(name = "is_current") - val isCurrent: Boolean + val isCurrent: Boolean, + + @ColumnInfo(name = "class_id") + val classId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int ) { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt new file mode 100644 index 000000000..fec1eadab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration6 : Migration(5, 6) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE ReportingUnits (" + + "id INTEGER NOT NULL PRIMARY KEY," + + "student_id INTEGER NOT NULL," + + "real_id INTEGER NOT NULL," + + "short TEXT NOT NULL," + + "sender_id INTEGER NOT NULL," + + "sender_name TEXT NOT NULL," + + "roles TEXT NOT NULL)") + + database.execSQL("CREATE TABLE Recipients (" + + "id INTEGER NOT NULL PRIMARY KEY," + + "student_id INTEGER NOT NULL," + + "real_id TEXT NOT NULL," + + "name TEXT NOT NULL," + + "real_name TEXT NOT NULL," + + "login_id INTEGER NOT NULL," + + "unit_id INTEGER NOT NULL," + + "role INTEGER NOT NULL," + + "hash TEXT NOT NULL)") + + database.execSQL("DELETE FROM Semesters WHERE 1") + database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index 2fc2c45a6..b751f8c5a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -2,13 +2,16 @@ package io.github.wulkanowy.data.repositories.message import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.messages.Folder +import io.github.wulkanowy.api.messages.SentMessage import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.utils.toLocalDateTime import io.reactivex.Single import org.threeten.bp.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton import io.github.wulkanowy.api.messages.Message as ApiMessage +import io.github.wulkanowy.api.messages.Recipient as ApiRecipient @Singleton class MessageRemote @Inject constructor(private val api: Api) { @@ -39,4 +42,21 @@ class MessageRemote @Inject constructor(private val api: Api) { fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) } + + fun sendMessage(subject: String, content: String, recipients: List): Single { + return api.sendMessage( + subject = subject, + content = content, + recipients = recipients.map { + ApiRecipient( + id = it.realId, + realName = it.realName, + loginId = it.loginId, + reportingUnitId = it.unitId, + role = it.role, + hash = it.hash + ) + } + ) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index 24e8c6bd1..2d78c4cb3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -2,8 +2,10 @@ package io.github.wulkanowy.data.repositories.message import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.api.messages.SentMessage import io.github.wulkanowy.data.ApiHelper import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Student import io.reactivex.Completable import io.reactivex.Single @@ -82,4 +84,8 @@ class MessageRepository @Inject constructor( fun updateMessages(messages: List): Completable { return Completable.fromCallable { local.updateMessages(messages) } } + + fun sendMessage(subject: String, content: String, recipients: List): Single { + return remote.sendMessage(subject, content, recipients) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt new file mode 100644 index 000000000..6b8328ec2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.data.repositories.recipient + +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { + + fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe> { + return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } + } + + fun saveRecipients(recipients: List) { + return recipientDb.insertAll(recipients) + } + + fun deleteRecipients(recipients: List) { + recipientDb.deleteAll(recipients) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt new file mode 100644 index 000000000..f3ef3ee2d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data.repositories.recipient + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientRemote @Inject constructor(private val api: Api) { + + fun getRecipients(role: Int, unit: ReportingUnit): Single> { + return api.getRecipients(role, unit.realId) + .map { recipients -> + recipients.map { + Recipient( + studentId = api.studentId, + name = it.name, + realName = it.realName, + realId = it.id, + hash = it.hash, + loginId = it.loginId, + role = it.role, + unitId = it.reportingUnitId + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt new file mode 100644 index 000000000..37b1631ef --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.repositories.recipient + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: RecipientLocal, + private val remote: RecipientRemote, + private val apiHelper: ApiHelper +) { + + fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getRecipients(student, role, unit).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getRecipients(role, unit) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getRecipients(student, role, unit).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteRecipients(old - new) + local.saveRecipients(new - old) + } + }.flatMap { + local.getRecipients(student, role, unit).toSingle(emptyList()) + } + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt new file mode 100644 index 000000000..b4281cbfd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt @@ -0,0 +1,28 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { + + fun getReportingUnits(student: Student): Maybe> { + return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() } + } + + fun getReportingUnit(student: Student, unitId: Int): Maybe { + return reportingUnitDb.loadOne(student.studentId, unitId) + } + + fun saveReportingUnits(reportingUnits: List) { + return reportingUnitDb.insertAll(reportingUnits) + } + + fun deleteReportingUnits(reportingUnits: List) { + reportingUnitDb.deleteAll(reportingUnits) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt new file mode 100644 index 000000000..feb4b0134 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRemote @Inject constructor(private val api: Api) { + + fun getReportingUnits(): Single> { + return api.getReportingUnits().map { + it.map { unit -> + ReportingUnit( + studentId = api.studentId, + realId = unit.id, + roles = unit.roles, + senderId = unit.senderId, + senderName = unit.senderName, + shortName = unit.short + ) + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt new file mode 100644 index 000000000..9184b4bb1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt @@ -0,0 +1,55 @@ +package io.github.wulkanowy.data.repositories.reportingunit + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.reactivex.Maybe +import io.reactivex.Single +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRepository @Inject constructor( + private val settings: InternetObservingSettings, + private val local: ReportingUnitLocal, + private val remote: ReportingUnitRemote, + private val apiHelper: ApiHelper +) { + + fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> { + return Single.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getReportingUnits(student).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getReportingUnits() + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getReportingUnits(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteReportingUnits(old - new) + local.saveReportingUnits(new - old) + } + }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } + ) + } + } + + fun getReportingUnit(student: Student, unitId: Int): Maybe { + return Maybe.just(apiHelper.initApi(student)) + .flatMap { _ -> + local.getReportingUnit(student, unitId) + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) getReportingUnits(student, true) + else Single.error(UnknownHostException()) + }.flatMapMaybe { + local.getReportingUnit(student, unitId) + } + ) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt index a62db433b..b25c8881b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt @@ -19,7 +19,9 @@ class SemesterRemote @Inject constructor(private val api: Api) { diaryName = semester.diaryName, semesterId = semester.semesterId, semesterName = semester.semesterNumber, - isCurrent = semester.current + isCurrent = semester.current, + classId = semester.classId, + unitId = semester.unitId ) } diff --git a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt index cb994a03a..1d0d98b49 100644 --- a/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/job/SyncWorker.kt @@ -14,6 +14,8 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.note.NoteRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.recipient.RecipientRepository +import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository @@ -26,6 +28,7 @@ import io.github.wulkanowy.utils.isHolidays import io.github.wulkanowy.utils.monday import io.reactivex.Completable import io.reactivex.Maybe +import io.reactivex.Single import io.reactivex.disposables.CompositeDisposable import org.threeten.bp.LocalDate import timber.log.Timber @@ -69,6 +72,12 @@ class SyncWorker : SimpleJobService() { @Inject lateinit var completedLessons: CompletedLessonsRepository + @Inject + lateinit var reportingUnitRepository: ReportingUnitRepository + + @Inject + lateinit var recipientRepository: RecipientRepository + @Inject lateinit var prefRepository: PreferencesRepository @@ -98,21 +107,24 @@ class SyncWorker : SimpleJobService() { disposable.add(student.isStudentSaved() .flatMapMaybe { if (it) student.getCurrentStudent().toMaybe() else Maybe.empty() } .flatMap { semester.getCurrentSemester(it, true).map { semester -> semester to it }.toMaybe() } - .flatMapCompletable { + .flatMapCompletable { c -> Completable.merge( listOf( - gradesDetails.getGrades(it.second, it.first, true, notify).ignoreElement(), - gradesSummary.getGradesSummary(it.first, true).ignoreElement(), - attendance.getAttendance(it.first, start, end, true).ignoreElement(), - exam.getExams(it.first, start, end, true).ignoreElement(), - timetable.getTimetable(it.first, start, end, true).ignoreElement(), - message.getMessages(it.second, RECEIVED, true, notify).ignoreElement(), - note.getNotes(it.second, it.first, true, notify).ignoreElement(), - homework.getHomework(it.first, LocalDate.now(), true).ignoreElement(), - homework.getHomework(it.first, LocalDate.now().plusDays(1), true).ignoreElement(), - luckyNumber.getLuckyNumber(it.first, true, notify).ignoreElement(), - completedLessons.getCompletedLessons(it.first, start, end, true).ignoreElement() - ) + gradesDetails.getGrades(c.second, c.first, true, notify).ignoreElement(), + gradesSummary.getGradesSummary(c.first, true).ignoreElement(), + attendance.getAttendance(c.first, start, end, true).ignoreElement(), + exam.getExams(c.first, start, end, true).ignoreElement(), + timetable.getTimetable(c.first, start, end, true).ignoreElement(), + message.getMessages(c.second, RECEIVED, true, notify).ignoreElement(), + note.getNotes(c.second, c.first, true, notify).ignoreElement(), + homework.getHomework(c.first, LocalDate.now(), true).ignoreElement(), + homework.getHomework(c.first, LocalDate.now().plusDays(1), true).ignoreElement(), + luckyNumber.getLuckyNumber(c.first, true, notify).ignoreElement(), + completedLessons.getCompletedLessons(c.first, start, end, true).ignoreElement() + ) + reportingUnitRepository.getReportingUnits(c.second, true) + .flatMapPublisher { reportingUnits -> + Single.merge(reportingUnits.map { recipientRepository.getRecipients(c.second, 2, it, true) }) + }.ignoreElements() ) } .subscribe({}, { error = it })) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index 766c6129c..625176aae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -20,6 +20,7 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageModule import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment +import io.github.wulkanowy.ui.modules.message.send.SendMessageFragment import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment @@ -94,9 +95,13 @@ abstract class MainModule { @PerFragment @ContributesAndroidInjector - abstract fun bindAccountDialog(): AccountDialog + abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment @PerFragment @ContributesAndroidInjector - abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment + abstract fun bindSendMessageFragment(): SendMessageFragment + + @PerFragment + @ContributesAndroidInjector + abstract fun bindAccountDialog(): AccountDialog } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt index 7905a3444..0de502993 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -12,7 +12,9 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository.MessageFo import io.github.wulkanowy.data.repositories.message.MessageRepository.MessageFolder.TRASHED import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.send.SendMessageFragment import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment import io.github.wulkanowy.utils.setOnSelectPageListener import kotlinx.android.synthetic.main.fragment_message.* @@ -61,6 +63,8 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { setOnSelectPageListener { presenter.onPageSelected(it) } } messageTabLayout.setupWithViewPager(messageViewPager) + + openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() } } override fun showContent(show: Boolean) { @@ -80,6 +84,10 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { (pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh) } + override fun openSendMessage() { + (activity as? MainActivity)?.pushView(SendMessageFragment.newInstance()) + } + override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt index 11ec94d8e..332d5b74f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -42,4 +42,8 @@ class MessagePresenter @Inject constructor( showProgress(false) } } + + fun onSendMessageButtonClicked() { + view?.openSendMessage() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt index b0bb8aa7c..41257ecc7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -14,6 +14,8 @@ interface MessageView : BaseView { fun notifyChildLoadData(index: Int, forceRefresh: Boolean) + fun openSendMessage() + interface MessageChildView { fun onParentLoadData(forceRefresh: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageFragment.kt new file mode 100644 index 000000000..c61691dbe --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageFragment.kt @@ -0,0 +1,154 @@ +package io.github.wulkanowy.ui.modules.message.send + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.core.content.ContextCompat +import com.hootsuite.nachos.ChipConfiguration +import com.hootsuite.nachos.chip.ChipSpan +import com.hootsuite.nachos.chip.ChipSpanChipCreator +import com.hootsuite.nachos.tokenizer.SpanChipTokenizer +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.ui.base.session.BaseSessionFragment +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.setOnTextChangedListener +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.fragment_send_message.* +import javax.inject.Inject + +class SendMessageFragment : BaseSessionFragment(), SendMessageView, MainView.TitledView { + + @Inject + lateinit var presenter: SendMessagePresenter + + private var recipients: List = emptyList() + + private lateinit var recipientsAdapter: ArrayAdapter + + companion object { + fun newInstance() = SendMessageFragment() + } + + override val titleStringId: Int + get() = R.string.send_message_title + + override val formRecipientsData: List + get() = sendMessageRecipientInput.allChips.map { it.data as Recipient } + + override val formSubjectValue: String + get() = sendMessageSubjectInput.text.toString() + + override val formContentValue: String + get() = sendMessageContentInput.text.toString() + + override val messageRequiredRecipients: String + get() = getString(R.string.send_message_required_recipients) + + override val messageContentMinLength: String + get() = getString(R.string.send_message_content_min_length) + + override val messageSuccess: String + get() = getString(R.string.send_message_successful) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_send_message, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + context?.let { + sendMessageRecipientInput.chipTokenizer = SpanChipTokenizer(it, object : ChipSpanChipCreator() { + override fun createChip(context: Context, text: CharSequence, data: Any?): ChipSpan { + return ChipSpan(context, text, ContextCompat.getDrawable(context, R.drawable.ic_all_account_24dp), data) + } + + override fun configureChip(chip: ChipSpan, chipConfiguration: ChipConfiguration) { + super.configureChip(chip, chipConfiguration) + chip.setShowIconOnLeft(true) + } + }, ChipSpan::class.java) + recipientsAdapter = ArrayAdapter(it, android.R.layout.simple_dropdown_item_1line) + } + + sendMessageRecipientInput.setAdapter(recipientsAdapter) + sendMessageRecipientInput.setOnTextChangedListener { presenter.onTypingRecipients() } + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + inflater?.inflate(R.menu.action_menu_send_message, menu) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + return if (item?.itemId == R.id.sendMessageMenuSend) presenter.onSend() + else false + } + + override fun setReportingUnit(unit: ReportingUnit) { + sendMessageFromTextView.setText(unit.senderName) + } + + override fun setRecipients(recipients: List) { + this.recipients = recipients + } + + override fun refreshRecipientsAdapter() { + recipientsAdapter.run { + clear() + addAll(recipients - sendMessageRecipientInput.allChips.map { it.data as Recipient }) + notifyDataSetChanged() + } + } + + override fun showProgress(show: Boolean) { + sendMessageProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showContent(show: Boolean) { + sendMessageContent.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showEmpty(show: Boolean) { + sendMessageEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun popView() { + (activity as? MainActivity)?.popView() + } + + override fun hideSoftInput() { + activity?.hideSoftInput() + } + + override fun showBottomNav(show: Boolean) { + (activity as? MainActivity)?.mainBottomNav?.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showMessage(text: String) { + Toast.makeText(context, text, Toast.LENGTH_LONG).show() + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt new file mode 100644 index 000000000..bb8070b43 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -0,0 +1,137 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.recipient.RecipientRepository +import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.session.BaseSessionPresenter +import io.github.wulkanowy.ui.base.session.SessionErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class SendMessagePresenter @Inject constructor( + private val errorHandler: SessionErrorHandler, + private val schedulers: SchedulersProvider, + private val studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val messageRepository: MessageRepository, + private val reportingUnitRepository: ReportingUnitRepository, + private val recipientRepository: RecipientRepository, + private val analytics: FirebaseAnalyticsHelper +) : BaseSessionPresenter(errorHandler) { + + private lateinit var reportingUnit: ReportingUnit + + override fun onAttachView(view: SendMessageView) { + Timber.i("Send message view is attached") + super.onAttachView(view) + view.run { + initView() + showBottomNav(false) + } + loadRecipients() + } + + private fun loadRecipients() { + Timber.i("Loading recipients started") + disposable.add(studentRepository.getCurrentStudent() + .flatMapMaybe { student -> + semesterRepository.getCurrentSemester(student) + .flatMapMaybe { reportingUnitRepository.getReportingUnit(student, it.unitId) } + .doOnSuccess { reportingUnit = it } + .flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showProgress(true) + showContent(false) + } + } + .doFinally { + view?.run { + showProgress(false) + } + } + .subscribe({ + view?.apply { + setReportingUnit(reportingUnit) + setRecipients(it) + refreshRecipientsAdapter() + showContent(true) + } + Timber.i("Loading recipients result: Success, fetched %s recipients", it.size.toString()) + }, { + Timber.i("Loading recipients result: An exception occurred") + view?.showContent(true) + errorHandler.dispatch(it) + }, { + Timber.i("Loading recipients result: Can't find the reporting unit") + view?.showEmpty(true) + }) + ) + } + + private fun sendMessage(subject: String, content: String, recipients: List) { + Timber.i("Sending message started") + disposable.add(messageRepository.sendMessage(subject, content, recipients) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + hideSoftInput() + showContent(false) + showProgress(true) + } + } + .doFinally { + view?.showProgress(false) + } + .subscribe({ + Timber.i("Sending message result: Success") + analytics.logEvent("send_message", "recipients" to recipients.size) + view?.run { + showMessage(messageSuccess) + popView() + } + }, { + Timber.i("Sending message result: An exception occurred") + view?.showContent(true) + errorHandler.dispatch(it) + }) + ) + } + + fun onTypingRecipients() { + view?.refreshRecipientsAdapter() + } + + fun onSend(): Boolean { + view?.run { + when { + formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients) + formContentValue.length < 3 -> showMessage(messageContentMinLength) + else -> { + sendMessage( + subject = formSubjectValue, + content = formContentValue, + recipients = formRecipientsData + ) + return true + } + } + } + return false + } + + override fun onDetachView() { + view?.showBottomNav(true) + super.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt new file mode 100644 index 000000000..ded96b071 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.ui.modules.message.send + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.ui.base.session.BaseSessionView + +interface SendMessageView : BaseSessionView { + + val formRecipientsData: List + + val formSubjectValue: String + + val formContentValue: String + + val messageRequiredRecipients: String + + val messageContentMinLength: String + + val messageSuccess: String + + fun initView() + + fun setReportingUnit(unit: ReportingUnit) + + fun setRecipients(recipients: List) + + fun refreshRecipientsAdapter() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun showEmpty(show: Boolean) + + fun popView() + + fun hideSoftInput() + + fun showBottomNav(show: Boolean) +} diff --git a/app/src/main/res/drawable/ic_menu_send_message_24dp.xml b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml new file mode 100644 index 000000000..fcc5acfb0 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_send_message_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_send_message_24dp.xml b/app/src/main/res/drawable/ic_send_message_24dp.xml new file mode 100644 index 000000000..0d82394be --- /dev/null +++ b/app/src/main/res/drawable/ic_send_message_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index 53920bb2b..78edbb037 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -24,10 +24,22 @@ android:layout_marginTop="48dp" android:visibility="invisible" /> + + + diff --git a/app/src/main/res/layout/fragment_send_message.xml b/app/src/main/res/layout/fragment_send_message.xml new file mode 100644 index 000000000..1919720fe --- /dev/null +++ b/app/src/main/res/layout/fragment_send_message.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/action_menu_send_message.xml b/app/src/main/res/menu/action_menu_send_message.xml new file mode 100644 index 000000000..d512fc763 --- /dev/null +++ b/app/src/main/res/menu/action_menu_send_message.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 0e082ecb4..bf2162c3c 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -7,6 +7,9 @@ @style/PreferenceThemeOverlay.v14.Material @color/bottom_nav_background @color/bottom_nav_background + @color/chip_material_background_inverse + @color/chip_default_text_color_inverse + @color/divider_inverse @color/about_libraries_window_background_dark diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e31752ee8..616c4b7fc 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -168,6 +168,16 @@ Dostałeś %1$d wiadomości + + Wyślij + Od: + Do: + Temat + Treść + Wiadomość wysłana pomyślnie + Musisz wybrać co najmniej 1 adresata + Treść wiadomości musi zawierać co najmniej 3 znaki + Kod źródłowy Zgłoś błąd diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 3593e29e6..eef7eab48 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,4 +1,5 @@ + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 6b79f622d..5b1dba26a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -16,4 +16,10 @@ #303030 #ffffff + + #595959 + #fefefe + + #cccccc + #777777 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22e28bafd..b8e3d0fb7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,6 +155,16 @@ You received %1$d messages + + Send + From: + To: + Subject + Content + Message sent successfully + You need to choose at least 1 recipient + The message content must be at least 3 characters + Source code Report a bug diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b79ef7b73..4735fd2f2 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -14,6 +14,9 @@ @android:color/primary_text_dark @android:color/white @color/bottom_nav_background_inverse + @color/chip_material_background + @color/chip_default_text_color + @color/divider @color/about_libraries_window_background