Add replying to messages (#263)

This commit is contained in:
Kacper Ziubryniewicz 2019-03-17 21:02:41 +01:00 committed by Mikołaj Pich
parent 824ed3f282
commit ba76453e45
50 changed files with 676 additions and 434 deletions

View File

@ -77,7 +77,7 @@ play {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('com.github.wulkanowy:api:29d24ca1ff') { exclude module: "threetenbp" } implementation('com.github.wulkanowy:api:a8d05df1ab') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2" implementation "androidx.appcompat:appcompat:1.0.2"
@ -107,7 +107,7 @@ dependencies {
implementation "com.aurelhubert:ahbottomnavigation:2.3.4" implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation 'com.ncapdevi:frag-nav:3.1.0' implementation 'com.ncapdevi:frag-nav:3.1.0'
implementation "com.hootsuite.android:nachos:1.1.1" implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
implementation 'com.github.PhilJay:MPAndroidChart:971640b29d' implementation 'com.github.PhilJay:MPAndroidChart:971640b29d'
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2' implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'

View File

@ -21,7 +21,8 @@ class GradeLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build()
gradeLocal = GradeLocal(testDb.gradeDao) gradeLocal = GradeLocal(testDb.gradeDao)
} }

View File

@ -7,9 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.toDate
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
@ -22,7 +20,6 @@ import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.of
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
import kotlin.test.assertFalse import kotlin.test.assertFalse

View File

@ -39,7 +39,7 @@ class GradeStatisticsLocalTest {
)) ))
val stats = gradeStatisticsLocal.getGradesStatistics( val stats = gradeStatisticsLocal.getGradesStatistics(
Semester(2, 2, "", 1, 2, true, 1 ,1), false, Semester(2, 2, "", 1, 2, true, 1, 1), false,
"Matematyka" "Matematyka"
).blockingGet() ).blockingGet()
assertEquals(1, stats.size) assertEquals(1, stats.size)

View File

@ -39,7 +39,18 @@
android:name=".ui.modules.main.MainActivity" android:name=".ui.modules.main.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/main_title" android:label="@string/main_title"
android:launchMode="singleTop"
android:theme="@style/WulkanowyTheme.NoActionBar" /> android:theme="@style/WulkanowyTheme.NoActionBar" />
<activity
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
android:parentActivityName=".ui.modules.main.MainActivity"
android:theme="@style/WulkanowyTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.main.MainActivity" />
</activity>
<service <service
android:name=".services.widgets.TimetableWidgetService" android:name=".services.widgets.TimetableWidgetService"

View File

@ -40,6 +40,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
@ -47,6 +48,7 @@ import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6 import io.github.wulkanowy.data.db.migrations.Migration6
import io.github.wulkanowy.data.db.migrations.Migration7 import io.github.wulkanowy.data.db.migrations.Migration7
import io.github.wulkanowy.data.db.migrations.Migration8 import io.github.wulkanowy.data.db.migrations.Migration8
import io.github.wulkanowy.data.db.migrations.Migration9
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -77,7 +79,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 8 const val VERSION_SCHEMA = 10
fun newInstance(context: Context): AppDatabase { fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -91,7 +93,9 @@ abstract class AppDatabase : RoomDatabase() {
Migration5(), Migration5(),
Migration6(), Migration6(),
Migration7(), Migration7(),
Migration8() Migration8(),
Migration9(),
Migration10()
) )
.build() .build()
} }

View File

@ -18,6 +18,6 @@ interface GradeSummaryDao {
@Delete @Delete
fun deleteAll(gradesSummary: List<GradeSummary>) fun deleteAll(gradesSummary: List<GradeSummary>)
@Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId") @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>> fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>
} }

View File

@ -20,7 +20,7 @@ interface MessagesDao {
@Update @Update
fun updateAll(messages: List<Message>) fun updateAll(messages: List<Message>)
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>> fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id") @Query("SELECT * FROM Messages WHERE student_id = :studentId AND real_id = :id")

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "Grades_Summary") @Entity(tableName = "GradesSummary")
data class GradeSummary( data class GradeSummary(
@ColumnInfo(name = "semester_id") @ColumnInfo(name = "semester_id")

View File

@ -24,9 +24,6 @@ data class Message(
@ColumnInfo(name = "sender_id") @ColumnInfo(name = "sender_id")
val senderId: Int, val senderId: Int,
@ColumnInfo(name = "recipient_id")
val recipientId: Int,
@ColumnInfo(name = "recipient_name") @ColumnInfo(name = "recipient_name")
val recipient: String, val recipient: String,
@ -39,8 +36,10 @@ data class Message(
var unread: Boolean, var unread: Boolean,
@ColumnInfo(name = "unread_by")
val unreadBy: Int, val unreadBy: Int,
@ColumnInfo(name = "read_by")
val readBy: Int, val readBy: Int,
val removed: Boolean val removed: Boolean

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration10 : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary")
}
}

View File

@ -6,11 +6,13 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration2 : Migration(1, 2) { class Migration2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE LuckyNumbers (" + database.execSQL("""
"id INTEGER NOT NULL PRIMARY KEY, " + CREATE TABLE IF NOT EXISTS LuckyNumbers (
"is_notified INTEGER NOT NULL, " + id INTEGER PRIMARY KEY NOT NULL,
"student_id INTEGER NOT NULL, " + is_notified INTEGER NOT NULL,
"date INTEGER NOT NULL, " + student_id INTEGER NOT NULL,
"lucky_number INTEGER NOT NULL)") date INTEGER NOT NULL,
lucky_number INTEGER NOT NULL)
""")
} }
} }

View File

@ -6,18 +6,20 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration3 : Migration(2, 3) { class Migration3 : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE CompletedLesson (" + database.execSQL("""
"id INTEGER NOT NULL PRIMARY KEY, " + CREATE TABLE IF NOT EXISTS CompletedLesson (
"student_id INTEGER NOT NULL, " + id INTEGER PRIMARY KEY NOT NULL,
"diary_id INTEGER NOT NULL, " + student_id INTEGER NOT NULL,
"date INTEGER NOT NULL, " + diary_id INTEGER NOT NULL,
"number INTEGER NOT NULL, " + date INTEGER NOT NULL,
"subject TEXT NOT NULL, " + number INTEGER NOT NULL,
"topic TEXT NOT NULL, " + subject TEXT NOT NULL,
"teacher TEXT NOT NULL, " + topic TEXT NOT NULL,
"teacher_symbol TEXT NOT NULL, " + teacher TEXT NOT NULL,
"substitution TEXT NOT NULL, " + teacher_symbol TEXT NOT NULL,
"absence TEXT NOT NULL, " + substitution TEXT NOT NULL,
"resources TEXT NOT NULL)") absence TEXT NOT NULL,
resources TEXT NOT NULL)
""")
} }
} }

View File

@ -6,24 +6,26 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration4 : Migration(3, 4) { class Migration4 : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS `Messages`") database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL("CREATE TABLE IF NOT EXISTS `Messages` (" + database.execSQL("""
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + CREATE TABLE IF NOT EXISTS Messages (
"`is_notified` INTEGER NOT NULL," + id INTEGER PRIMARY KEY NOT NULL,
"`content` TEXT," + is_notified INTEGER NOT NULL,
"`student_id` INTEGER NOT NULL," + content TEXT,
"`real_id` INTEGER NOT NULL," + student_id INTEGER NOT NULL,
"`message_id` INTEGER NOT NULL," + real_id INTEGER NOT NULL,
"`sender_name` TEXT NOT NULL," + message_id INTEGER NOT NULL,
"`sender_id` INTEGER NOT NULL," + sender_name TEXT NOT NULL,
"`recipient_id` INTEGER NOT NULL," + sender_id INTEGER NOT NULL,
"`recipient_name` TEXT NOT NULL," + recipient_id INTEGER NOT NULL,
"`subject` TEXT NOT NULL," + recipient_name TEXT NOT NULL,
"`date` INTEGER NOT NULL," + subject TEXT NOT NULL,
"`folder_id` INTEGER NOT NULL," + date INTEGER NOT NULL,
"`unread` INTEGER NOT NULL," + folder_id INTEGER NOT NULL,
"`unreadBy` INTEGER NOT NULL," + unread INTEGER NOT NULL,
"`readBy` INTEGER NOT NULL," + unreadBy INTEGER NOT NULL,
"`removed` INTEGER NOT NULL)") readBy INTEGER NOT NULL,
removed INTEGER NOT NULL)
""")
} }
} }

View File

@ -8,17 +8,19 @@ import org.threeten.bp.ZoneOffset
class Migration5 : Migration(4, 5) { class Migration5 : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN `registration_date` INTEGER NOT NULL DEFAULT 0") database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL")
database.execSQL("UPDATE Students SET `registration_date` = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'")
database.execSQL("DROP TABLE IF EXISTS `Notes`") database.execSQL("DROP TABLE IF EXISTS Notes")
database.execSQL("CREATE TABLE IF NOT EXISTS `Notes` (" + database.execSQL("""
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + CREATE TABLE IF NOT EXISTS Notes (
"`is_read` INTEGER NOT NULL," + id INTEGER PRIMARY KEY NOT NULL,
"`is_notified` INTEGER NOT NULL," + is_read INTEGER NOT NULL,
"`student_id` INTEGER NOT NULL," + is_notified INTEGER NOT NULL,
"`date` INTEGER NOT NULL," + student_id INTEGER NOT NULL,
"`teacher` TEXT NOT NULL," + date INTEGER NOT NULL,
"`category` TEXT NOT NULL," + teacher TEXT NOT NULL,
"`content` TEXT NOT NULL)") category TEXT NOT NULL,
content TEXT NOT NULL)
""")
} }
} }

View File

@ -6,25 +6,29 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration6 : Migration(5, 6) { class Migration6 : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE ReportingUnits (" + database.execSQL("""
"id INTEGER NOT NULL PRIMARY KEY," + CREATE TABLE IF NOT EXISTS ReportingUnits (
"student_id INTEGER NOT NULL," + id INTEGER PRIMARY KEY NOT NULL,
"real_id INTEGER NOT NULL," + student_id INTEGER NOT NULL,
"short TEXT NOT NULL," + real_id INTEGER NOT NULL,
"sender_id INTEGER NOT NULL," + short TEXT NOT NULL,
"sender_name TEXT NOT NULL," + sender_id INTEGER NOT NULL,
"roles TEXT NOT NULL)") sender_name TEXT NOT NULL,
roles TEXT NOT NULL)
""")
database.execSQL("CREATE TABLE Recipients (" + database.execSQL("""
"id INTEGER NOT NULL PRIMARY KEY," + CREATE TABLE IF NOT EXISTS Recipients (
"student_id INTEGER NOT NULL," + id INTEGER PRIMARY KEY NOT NULL,
"real_id TEXT NOT NULL," + student_id INTEGER NOT NULL,
"name TEXT NOT NULL," + real_id TEXT NOT NULL,
"real_name TEXT NOT NULL," + name TEXT NOT NULL,
"login_id INTEGER NOT NULL," + real_name TEXT NOT NULL,
"unit_id INTEGER NOT NULL," + login_id INTEGER NOT NULL,
"role INTEGER NOT NULL," + unit_id INTEGER NOT NULL,
"hash TEXT NOT NULL)") role INTEGER NOT NULL,
hash TEXT NOT NULL)
""")
database.execSQL("DELETE FROM Semesters WHERE 1") 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 class_id INTEGER DEFAULT 0 NOT NULL")

View File

@ -6,13 +6,15 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration7 : Migration(6, 7) { class Migration7 : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `GradesStatistics` (" + database.execSQL("""
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + CREATE TABLE IF NOT EXISTS GradesStatistics (
"`student_id` INTEGER NOT NULL," + id INTEGER PRIMARY KEY NOT NULL,
"`semester_id` INTEGER NOT NULL," + student_id INTEGER NOT NULL,
"`subject` TEXT NOT NULL," + semester_id INTEGER NOT NULL,
"`grade` INTEGER NOT NULL," + subject TEXT NOT NULL,
"`amount` INTEGER NOT NULL," + grade INTEGER NOT NULL,
"`is_semester` INTEGER NOT NULL)") amount INTEGER NOT NULL,
is_semester INTEGER NOT NULL)
""")
} }
} }

View File

@ -6,8 +6,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration8 : Migration(7, 8) { class Migration8 : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE `Timetable` ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL")
} }
} }

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration9 : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL("""
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,
real_id INTEGER NOT NULL,
message_id INTEGER NOT NULL,
sender_name TEXT NOT NULL,
sender_id INTEGER NOT NULL,
recipient_name TEXT NOT NULL,
subject TEXT NOT NULL,
date INTEGER NOT NULL,
folder_id INTEGER NOT NULL,
unread INTEGER NOT NULL,
unread_by INTEGER NOT NULL,
read_by INTEGER NOT NULL,
removed INTEGER NOT NULL,
is_notified INTEGER NOT NULL,
content TEXT)
""")
}
}

View File

@ -27,7 +27,8 @@ class GradeRepository @Inject constructor(
}.flatMap { newGrades -> }.flatMap { newGrades ->
local.getGrades(semester).toSingle(emptyList()) local.getGrades(semester).toSingle(emptyList())
.doOnSuccess { oldGrades -> .doOnSuccess { oldGrades ->
val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() val notifyBreakDate = oldGrades.maxBy { it.date }?.date
?: student.registrationDate.toLocalDate()
local.deleteGrades(oldGrades - newGrades) local.deleteGrades(oldGrades - newGrades)
local.saveGrades((newGrades - oldGrades) local.saveGrades((newGrades - oldGrades)
.onEach { .onEach {

View File

@ -20,6 +20,7 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
} }
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Homework>> { fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Homework>> {
return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate)
.filter { it.isNotEmpty() }
} }
} }

View File

@ -26,7 +26,6 @@ class MessageRemote @Inject constructor(private val api: Api) {
sender = it.sender.orEmpty(), sender = it.sender.orEmpty(),
senderId = it.senderId ?: 0, senderId = it.senderId ?: 0,
recipient = it.recipient.orEmpty(), recipient = it.recipient.orEmpty(),
recipientId = 0,
subject = it.subject.trim(), subject = it.subject.trim(),
date = it.date?.toLocalDateTime() ?: now(), date = it.date?.toLocalDateTime() ?: now(),
folderId = it.folderId, folderId = it.folderId,

View File

@ -83,6 +83,10 @@ class MessageRepository @Inject constructor(
} }
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> { fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
return remote.sendMessage(subject, content, recipients) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.sendMessage(subject, content, recipients)
else Single.error(UnknownHostException())
}
} }
} }

View File

@ -24,12 +24,6 @@ class PreferencesRepository @Inject constructor(
val currentTheme: Int val currentTheme: Int
get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1 get() = sharedPref.getString(currentThemeKey, "1")?.toIntOrNull() ?: 1
val gradePlusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDoubleOrNull() ?: 0.0
val gradeMinusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDoubleOrNull() ?: 0.0
val gradeColorTheme: String val gradeColorTheme: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan" get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
@ -51,4 +45,14 @@ class PreferencesRepository @Inject constructor(
val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean val isDebugNotificationEnable: Boolean
get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false) get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false)
val gradePlusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
val gradeMinusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble()
?: 0.0
val fillMessageContent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)
} }

View File

@ -1,30 +1,41 @@
package io.github.wulkanowy.data.repositories.recipient package io.github.wulkanowy.data.repositories.recipient
import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
@Singleton @Singleton
class RecipientRemote @Inject constructor(private val api: Api) { class RecipientRemote @Inject constructor(private val api: Api) {
fun getRecipients(role: Int, unit: ReportingUnit): Single<List<Recipient>> { fun getRecipients(role: Int, unit: ReportingUnit): Single<List<Recipient>> {
return api.getRecipients(role, unit.realId) return api.getRecipients(unit.realId, role)
.map { recipients -> .map { recipients ->
recipients.map { recipients.map { it.toRecipient() }
Recipient( }
}
fun getMessageRecipients(message: Message): Single<List<Recipient>> {
return api.getMessageRecipients(message.messageId, message.senderId)
.map { recipients ->
recipients.map { it.toRecipient() }
}
}
private fun ApiRecipient.toRecipient(): Recipient {
return Recipient(
studentId = api.studentId, studentId = api.studentId,
name = it.name, realId = id,
realName = it.name, realName = name,
realId = it.id, name = shortName,
hash = it.hash, hash = hash,
loginId = it.loginId, loginId = loginId,
role = it.role, role = role,
unitId = it.reportingUnitId ?: 0 unitId = reportingUnitId ?: 0
) )
} }
}
}
} }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.recipient
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper 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.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
@ -39,4 +40,13 @@ class RecipientRepository @Inject constructor(
) )
} }
} }
fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> {
return Single.just(apiHelper.initApi(student))
.flatMap { ReactiveNetwork.checkInternetConnectivity(settings) }
.flatMap {
if (it) remote.getMessageRecipients(message)
else Single.error(UnknownHostException())
}
}
} }

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule import io.github.wulkanowy.ui.modules.main.MainModule
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.splash.SplashActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
@ -15,7 +16,7 @@ import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
internal abstract class BuilderModule { internal abstract class BuilderModule {
@PerActivity @PerActivity
@ContributesAndroidInjector() @ContributesAndroidInjector
abstract fun bindSplashActivity(): SplashActivity abstract fun bindSplashActivity(): SplashActivity
@PerActivity @PerActivity
@ -26,6 +27,9 @@ internal abstract class BuilderModule {
@ContributesAndroidInjector(modules = [MainModule::class]) @ContributesAndroidInjector(modules = [MainModule::class])
abstract fun bindMainActivity(): MainActivity abstract fun bindMainActivity(): MainActivity
@ContributesAndroidInjector
abstract fun bindMessageSendActivity(): SendMessageActivity
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService abstract fun bindTimetableWidgetService(): TimetableWidgetService

View File

@ -20,7 +20,6 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageModule import io.github.wulkanowy.ui.modules.message.MessageModule
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment 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.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
@ -97,10 +96,6 @@ abstract class MainModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment abstract fun bindCompletedLessonsFragment(): CompletedLessonsFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindSendMessageFragment(): SendMessageFragment
@PerFragment @PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindAccountDialog(): AccountDialog abstract fun bindAccountDialog(): AccountDialog

View File

@ -12,9 +12,8 @@ import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter 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.main.MainView
import io.github.wulkanowy.ui.modules.message.send.SendMessageFragment import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment
import io.github.wulkanowy.utils.setOnSelectPageListener import io.github.wulkanowy.utils.setOnSelectPageListener
import kotlinx.android.synthetic.main.fragment_message.* import kotlinx.android.synthetic.main.fragment_message.*
@ -85,7 +84,7 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView {
} }
override fun openSendMessage() { override fun openSendMessage() {
(activity as? MainActivity)?.pushView(SendMessageFragment.newInstance()) context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) }
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -1,20 +1,31 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.session.BaseSessionFragment import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import kotlinx.android.synthetic.main.fragment_message_preview.* import kotlinx.android.synthetic.main.fragment_message_preview.*
import javax.inject.Inject import javax.inject.Inject
@SuppressLint("SetTextI18n")
class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainView.TitledView { class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainView.TitledView {
@Inject @Inject
lateinit var presenter: MessagePreviewPresenter lateinit var presenter: MessagePreviewPresenter
private var menuReplyButton: MenuItem? = null
override val titleStringId: Int override val titleStringId: Int
get() = R.string.message_title get() = R.string.message_title
@ -31,42 +42,66 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
} }
} }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_message_preview, container, false) return inflater.inflate(R.layout.fragment_message_preview, container, false)
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
messageContainer = message messageContainer = messagePreviewContainer
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0) presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getInt(MESSAGE_ID_KEY) ?: 0)
} }
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater?.inflate(R.menu.action_menu_message_preview, menu)
menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply)
presenter.onCreateOptionsMenu()
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply()
else false
}
override fun setSubject(subject: String) { override fun setSubject(subject: String) {
messageSubject.text = subject messagePreviewSubject.text = subject
} }
override fun setRecipient(recipient: String?) { override fun setRecipient(recipient: String) {
messageAuthor.text = getString(R.string.message_to, recipient) messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient"
} }
override fun setSender(sender: String?) { override fun setSender(sender: String) {
messageAuthor.text = getString(R.string.message_from, sender) messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender"
} }
override fun setDate(date: String?) { override fun setDate(date: String) {
messageDate.text = getString(R.string.message_date, date) messagePreviewDate.text = getString(R.string.message_date, date)
} }
override fun setContent(content: String?) { override fun setContent(content: String) {
messageContent.text = content messagePreviewContent.text = content
} }
override fun showProgress(show: Boolean) { override fun showProgress(show: Boolean) {
messageProgress.visibility = if (show) View.VISIBLE else View.GONE messagePreviewProgress.visibility = if (show) VISIBLE else GONE
}
override fun showReplyButton(show: Boolean) {
menuReplyButton?.isVisible = show
} }
override fun showMessageError() { override fun showMessageError() {
messageError.visibility = View.VISIBLE messagePreviewError.visibility = VISIBLE
}
override fun openMessageReply(message: Message?) {
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) }
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
@ -21,6 +22,8 @@ class MessagePreviewPresenter @Inject constructor(
var messageId: Int = 0 var messageId: Int = 0
private var replyMessage: Message? = null
fun onAttachView(view: MessagePreviewView, id: Int) { fun onAttachView(view: MessagePreviewView, id: Int) {
super.onAttachView(view) super.onAttachView(view)
loadData(id) loadData(id)
@ -38,11 +41,13 @@ class MessagePreviewPresenter @Inject constructor(
.doFinally { view?.showProgress(false) } .doFinally { view?.showProgress(false) }
.subscribe({ message -> .subscribe({ message ->
Timber.i("Loading message $id preview result: Success ") Timber.i("Loading message $id preview result: Success ")
replyMessage = message
view?.run { view?.run {
message.let { message.let {
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
setContent(it.content) setContent(it.content.orEmpty())
showReplyButton(true)
if (it.recipient.isNotBlank()) setRecipient(it.recipient) if (it.recipient.isNotBlank()) setRecipient(it.recipient)
else setSender(it.sender) else setSender(it.sender)
@ -56,4 +61,15 @@ class MessagePreviewPresenter @Inject constructor(
}) })
} }
} }
fun onReply(): Boolean {
return if (replyMessage != null) {
view?.openMessageReply(replyMessage)
true
} else false
}
fun onCreateOptionsMenu() {
view?.showReplyButton(replyMessage != null)
}
} }

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.session.BaseSessionView import io.github.wulkanowy.ui.base.session.BaseSessionView
interface MessagePreviewView : BaseSessionView { interface MessagePreviewView : BaseSessionView {
@ -8,15 +9,19 @@ interface MessagePreviewView : BaseSessionView {
fun setSubject(subject: String) fun setSubject(subject: String)
fun setRecipient(recipient: String?) fun setRecipient(recipient: String)
fun setSender(sender: String?) fun setSender(sender: String)
fun setDate(date: String?) fun setDate(date: String)
fun setContent(content: String?) fun setContent(content: String)
fun showProgress(show: Boolean) fun showProgress(show: Boolean)
fun showReplyButton(show: Boolean)
fun showMessageError() fun showMessageError()
fun openMessageReply(message: Message?)
} }

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.ui.modules.message.send
import android.graphics.drawable.Drawable
import android.net.Uri
import com.pchmn.materialchips.model.ChipInterface
import io.github.wulkanowy.data.db.entities.Recipient
class RecipientChip(var recipient: Recipient) : ChipInterface {
override fun getAvatarDrawable(): Drawable? = null
override fun getAvatarUri(): Uri? = null
override fun getId(): Any = recipient.id
override fun getLabel(): String = recipient.name
override fun getInfo(): String {
return recipient.realName.run {
substringBeforeLast("-").let { sub ->
when {
(sub == this) -> this
(sub.indexOf('(') != -1) -> indexOf("(").let { substring(if (it != -1) it else 0) }
(sub.indexOf('[') != -1) -> indexOf("[").let { substring(if (it != -1) it else 0) }
else -> substringAfter('-')
}
}.trim()
}
}
}

View File

@ -0,0 +1,127 @@
package io.github.wulkanowy.ui.modules.message.send
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.activity_send_message.*
import javax.inject.Inject
class SendMessageActivity : BaseActivity(), SendMessageView {
@Inject
lateinit var presenter: SendMessagePresenter
companion object {
private const val EXTRA_MESSAGE = "EXTRA_MESSAGE"
fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java)
fun getStartIntent(context: Context, message: Message?): Intent {
return getStartIntent(context).putExtra(EXTRA_MESSAGE, message)
}
}
override val formRecipientsData: List<Recipient>
get() = (sendMessageRecipientsInput.selectedChipList).map { (it as RecipientChip).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.message_required_recipients)
override val messageContentMinLength: String
get() = getString(R.string.message_content_min_length)
override val messageSuccess: String
get() = getString(R.string.message_send_successful)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_send_message)
setSupportActionBar(sendMessageToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
messageContainer = sendMessageContainer
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.action_menu_send_message, menu)
return true
}
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<Recipient>) {
sendMessageRecipientsInput.filterableList = recipients.map { RecipientChip(it) }
}
override fun setSelectedRecipients(recipients: List<Recipient>) {
recipients.map { sendMessageRecipientsInput.addChip(RecipientChip(it)) }
}
override fun showProgress(show: Boolean) {
sendMessageProgress.visibility = if (show) VISIBLE else GONE
}
override fun showContent(show: Boolean) {
sendMessageContent.visibility = if (show) VISIBLE else GONE
}
override fun showEmpty(show: Boolean) {
sendMessageEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showActionBar(show: Boolean) {
supportActionBar?.apply { if (show) show() else hide() }
}
override fun setSubject(subject: String) {
sendMessageSubjectInput.setText(subject)
}
override fun setContent(content: String) {
sendMessageContentInput.setText(content)
}
override fun showMessage(text: String) {
Toast.makeText(this, text, LENGTH_LONG).show()
}
override fun showSoftInput(show: Boolean) {
if (show) showSoftInput() else hideSoftInput()
}
override fun popView() {
onBackPressed()
}
override fun onDestroy() {
presenter.onDetachView()
super.onDestroy()
}
}

View File

@ -1,154 +0,0 @@
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<Recipient> = emptyList()
private lateinit var recipientsAdapter: ArrayAdapter<Recipient>
companion object {
fun newInstance() = SendMessageFragment()
}
override val titleStringId: Int
get() = R.string.send_message_title
override val formRecipientsData: List<Recipient>
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<ChipSpan>(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<Recipient>) {
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()
}
}

View File

@ -1,50 +1,77 @@
package io.github.wulkanowy.ui.modules.message.send package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.recipient.RecipientRepository import io.github.wulkanowy.data.repositories.recipient.RecipientRepository
import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Completable
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class SendMessagePresenter @Inject constructor( class SendMessagePresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val studentRepository: StudentRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val reportingUnitRepository: ReportingUnitRepository, private val reportingUnitRepository: ReportingUnitRepository,
private val recipientRepository: RecipientRepository, private val recipientRepository: RecipientRepository,
private val preferencesRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<SendMessageView>(errorHandler) { ) : BasePresenter<SendMessageView>(errorHandler) {
private lateinit var reportingUnit: ReportingUnit fun onAttachView(view: SendMessageView, message: Message?) {
override fun onAttachView(view: SendMessageView) {
super.onAttachView(view) super.onAttachView(view)
Timber.i("Send message view is attached") Timber.i("Send message view is attached")
view.run { loadData(message)
initView() view.apply {
showBottomNav(false) message?.let {
setSubject("RE: ${message.subject}")
if (preferencesRepository.fillMessageContent) {
setContent(when (message.sender.isNotEmpty()) {
true -> "\n\nOd: ${message.sender}\n"
false -> "\n\nDo: ${message.recipient}\n"
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
}
}
} }
loadRecipients()
} }
private fun loadRecipients() { private fun loadData(message: Message?) {
var reportingUnit: ReportingUnit? = null
var recipients: List<Recipient> = emptyList()
var selectedRecipient: List<Recipient> = emptyList()
Timber.i("Loading recipients started") Timber.i("Loading recipients started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMapMaybe { student -> .flatMap { semesterRepository.getCurrentSemester(it).map { semester -> it to semester } }
semesterRepository.getCurrentSemester(student) .flatMapCompletable { (student, semester) ->
.flatMapMaybe { reportingUnitRepository.getReportingUnit(student, it.unitId) } reportingUnitRepository.getReportingUnit(student, semester.unitId)
.doOnSuccess { reportingUnit = it } .doOnSuccess { reportingUnit = it }
.flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() } .flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() }
.doOnSuccess {
Timber.i("Loading recipients result: Success, fetched %d recipients", it.size)
recipients = it
}
.flatMapCompletable {
if (message == null) Completable.complete()
else recipientRepository.getMessageRecipients(student, message)
.doOnSuccess {
Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size)
selectedRecipient = it
}
.ignoreElement()
}
} }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
@ -54,28 +81,24 @@ class SendMessagePresenter @Inject constructor(
showContent(false) showContent(false)
} }
} }
.doFinally { .doFinally { view?.run { showProgress(false) } }
view?.run {
showProgress(false)
}
}
.subscribe({ .subscribe({
Timber.i("Loading recipients result: Success, fetched %s recipients", it.size.toString())
view?.apply { view?.apply {
setReportingUnit(reportingUnit) if (reportingUnit !== null) {
setRecipients(it) reportingUnit?.let { setReportingUnit(it) }
refreshRecipientsAdapter() setRecipients(recipients)
if (selectedRecipient.isNotEmpty()) setSelectedRecipients(selectedRecipient)
showContent(true) showContent(true)
} else {
Timber.e("Loading recipients result: Can't find the reporting unit")
view?.showEmpty(true)
}
} }
}, { }, {
Timber.i("Loading recipients result: An exception occurred") Timber.e("Loading recipients result: An exception occurred")
view?.showContent(true) view?.showContent(true)
errorHandler.dispatch(it) 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<Recipient>) { private fun sendMessage(subject: String, content: String, recipients: List<Recipient>) {
@ -85,14 +108,12 @@ class SendMessagePresenter @Inject constructor(
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {
view?.run { view?.run {
hideSoftInput() showSoftInput(false)
showContent(false) showContent(false)
showProgress(true) showProgress(true)
showActionBar(false)
} }
} }
.doFinally {
view?.showProgress(false)
}
.subscribe({ .subscribe({
Timber.i("Sending message result: Success") Timber.i("Sending message result: Success")
analytics.logEvent("send_message", "recipients" to recipients.size) analytics.logEvent("send_message", "recipients" to recipients.size)
@ -102,16 +123,16 @@ class SendMessagePresenter @Inject constructor(
} }
}, { }, {
Timber.i("Sending message result: An exception occurred") Timber.i("Sending message result: An exception occurred")
view?.showContent(true) view?.run {
showContent(true)
showProgress(false)
showActionBar(true)
}
errorHandler.dispatch(it) errorHandler.dispatch(it)
}) })
) )
} }
fun onTypingRecipients() {
view?.refreshRecipientsAdapter()
}
fun onSend(): Boolean { fun onSend(): Boolean {
view?.run { view?.run {
when { when {
@ -129,9 +150,4 @@ class SendMessagePresenter @Inject constructor(
} }
return false return false
} }
override fun onDetachView() {
view?.showBottomNav(true)
super.onDetachView()
}
} }

View File

@ -2,9 +2,9 @@ package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.ui.base.session.BaseSessionView import io.github.wulkanowy.ui.base.BaseView
interface SendMessageView : BaseSessionView { interface SendMessageView : BaseView {
val formRecipientsData: List<Recipient> val formRecipientsData: List<Recipient>
@ -18,13 +18,11 @@ interface SendMessageView : BaseSessionView {
val messageSuccess: String val messageSuccess: String
fun initView()
fun setReportingUnit(unit: ReportingUnit) fun setReportingUnit(unit: ReportingUnit)
fun setRecipients(recipients: List<Recipient>) fun setRecipients(recipients: List<Recipient>)
fun refreshRecipientsAdapter() fun setSelectedRecipients(recipients: List<Recipient>)
fun showProgress(show: Boolean) fun showProgress(show: Boolean)
@ -32,9 +30,13 @@ interface SendMessageView : BaseSessionView {
fun showEmpty(show: Boolean) fun showEmpty(show: Boolean)
fun showActionBar(show: Boolean)
fun setSubject(subject: String)
fun setContent(content: String)
fun showSoftInput(show: Boolean)
fun popView() fun popView()
fun hideSoftInput()
fun showBottomNav(show: Boolean)
} }

View File

@ -57,7 +57,7 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
messageContainer = messageTabRecycler messageContainer = messageTabRecycler
presenter.onAttachView(this, MessageFolder.valueOf( presenter.onAttachView(this, MessageFolder.valueOf(
(savedInstanceState ?: arguments)?.getString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID) ?: "" (savedInstanceState ?: arguments)?.getString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID).orEmpty()
)) ))
} }

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
</vector>

View File

@ -3,13 +3,28 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/sendMessageContainer"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/sendMessageAppBarContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/WulkanowyTheme.ActionBar"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/sendMessageToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout <LinearLayout
android:id="@+id/sendMessageContent" android:id="@+id/sendMessageContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@id/sendMessageAppBarContainer"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
@ -22,14 +37,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_message_from" android:text="@string/message_from"
android:textColor="@android:color/darker_gray" android:textColor="@android:color/darker_gray"
android:textSize="18sp" /> android:textSize="18sp" />
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/sendMessageFromTextView" android:id="@+id/sendMessageFromTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="38dp" android:layout_height="30dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:enabled="false" android:enabled="false"
android:textColor="?android:attr/textColorPrimaryNoDisable" android:textColor="?android:attr/textColorPrimaryNoDisable"
@ -43,32 +58,32 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="58dp"
android:paddingStart="14dp" android:paddingStart="14dp"
android:paddingLeft="14dp" android:paddingLeft="14dp"
android:paddingTop="14dp" tools:ignore="RtlSymmetry">
android:paddingEnd="0dp"
android:paddingRight="0dp"
android:paddingBottom="14dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_message_to" android:text="@string/message_to"
android:textAlignment="center"
android:textColor="@android:color/darker_gray" android:textColor="@android:color/darker_gray"
android:textSize="18sp" /> android:textSize="18sp" />
<com.hootsuite.nachos.NachoTextView <com.pchmn.materialchips.ChipsInput
android:id="@+id/sendMessageRecipientInput" android:id="@+id/sendMessageRecipientsInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:inputType="text" app:horizontalScroll="true"
android:maxLines="1" tools:layout_height="30dp">
android:textColor="?attr/chipTextColor"
app:chipBackground="?attr/chipBackgroundColor" /> </com.pchmn.materialchips.ChipsInput>
</LinearLayout> </LinearLayout>
<View <View
@ -79,9 +94,9 @@
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:id="@+id/sendMessageSubjectInput" android:id="@+id/sendMessageSubjectInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="66dp" android:layout_height="58dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:hint="@string/send_message_subject" android:hint="@string/message_subject"
android:inputType="text" android:inputType="text"
android:maxLines="1" android:maxLines="1"
android:padding="14dp" /> android:padding="14dp" />
@ -97,7 +112,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:gravity="top|start" android:gravity="top|start"
android:hint="@string/send_message_content" android:hint="@string/message_content"
android:inputType="textMultiLine" android:inputType="textMultiLine"
android:paddingStart="14dp" android:paddingStart="14dp"
android:paddingLeft="14dp" android:paddingLeft="14dp"
@ -107,9 +122,10 @@
android:singleLine="false" /> android:singleLine="false" />
</LinearLayout> </LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:layout_below="@id/sendMessageAppBarContainer">
<LinearLayout <LinearLayout
android:id="@+id/sendMessageEmpty" android:id="@+id/sendMessageEmpty"
@ -141,5 +157,5 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:visibility="gone" /> android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </FrameLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -1,22 +1,22 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/messagePreviewContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:id="@+id/message"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<TextView <TextView
android:id="@+id/messageSubject" android:id="@+id/messagePreviewSubject"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
@ -25,7 +25,7 @@
tools:text="@tools:sample/lorem" /> tools:text="@tools:sample/lorem" />
<TextView <TextView
android:id="@+id/messageAuthor" android:id="@+id/messagePreviewAuthor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
@ -34,7 +34,7 @@
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
<TextView <TextView
android:id="@+id/messageDate" android:id="@+id/messagePreviewDate"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
@ -43,7 +43,7 @@
tools:text="@tools:sample/date/ddmmyy" /> tools:text="@tools:sample/date/ddmmyy" />
<TextView <TextView
android:id="@+id/messageContent" android:id="@+id/messagePreviewContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="web" android:autoLink="web"
@ -51,11 +51,10 @@
android:textIsSelectable="true" android:textIsSelectable="true"
tools:text="@tools:sample/lorem" /> tools:text="@tools:sample/lorem" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView>
</ScrollView>
<LinearLayout <LinearLayout
android:id="@+id/messageError" android:id="@+id/messagePreviewError"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center" android:gravity="center"
@ -82,7 +81,7 @@
</LinearLayout> </LinearLayout>
<ProgressBar <ProgressBar
android:id="@+id/messageProgress" android:id="@+id/messagePreviewProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/messagePreviewMenuReply"
android:icon="@drawable/ic_message_reply_24dp"
android:orderInCategory="1"
android:title="@string/message_reply"
app:showAsAction="ifRoom" />
</menu>

View File

@ -7,9 +7,12 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
<item name="bottomNavBackground">@color/bottom_nav_background</item> <item name="bottomNavBackground">@color/bottom_nav_background</item>
<item name="android:navigationBarColor" tools:targetApi="21">@color/bottom_nav_background</item> <item name="android:navigationBarColor" tools:targetApi="21">@color/bottom_nav_background</item>
<item name="chipBackgroundColor">@color/chip_material_background_inverse</item>
<item name="chipTextColor">@color/chip_default_text_color_inverse</item>
<item name="dividerColor">@color/divider_inverse</item> <item name="dividerColor">@color/divider_inverse</item>
<item name="chip_backgroundColor">@color/chip_backgroundColor_inverse</item>
<item name="chip_labelColor">@color/chip_labelColor_inverse</item>
<item name="chip_detailed_backgroundColor">@color/chip_backgroundColor_inverse</item>
<item name="filterable_list_backgroundColor">@color/filterable_list_backgroundColor_inverse</item>
<item name="filterable_list_textColor">@color/filterable_list_textColor_inverse</item>
<!--AboutLibraries specific values--> <!--AboutLibraries specific values-->
<item name="about_libraries_window_background">@color/about_libraries_window_background_dark</item> <item name="about_libraries_window_background">@color/about_libraries_window_background_dark</item>

View File

@ -13,6 +13,7 @@
<string name="more_title">Więcej</string> <string name="more_title">Więcej</string>
<string name="about_title">O aplikacji</string> <string name="about_title">O aplikacji</string>
<string name="message_title">Wiadomości</string> <string name="message_title">Wiadomości</string>
<string name="send_message_title">Nowa wiadomość</string>
<string name="note_title">Uwagi i osiągnięcia</string> <string name="note_title">Uwagi i osiągnięcia</string>
<string name="homework_title">Zadania domowe</string> <string name="homework_title">Zadania domowe</string>
<string name="account_title">Wybierz konto</string> <string name="account_title">Wybierz konto</string>
@ -135,9 +136,15 @@
<string name="message_no_subject">(brak tematu)</string> <string name="message_no_subject">(brak tematu)</string>
<string name="message_no_items">Brak wiadomości</string> <string name="message_no_items">Brak wiadomości</string>
<string name="message_preview_error">Wystąpił błąd podczas pobierania treści wiadomości</string> <string name="message_preview_error">Wystąpił błąd podczas pobierania treści wiadomości</string>
<string name="message_from">Od: %s</string> <string name="message_from">Od:</string>
<string name="message_to">Do: %s</string> <string name="message_to">Do:</string>
<string name="message_date">Data: %s</string> <string name="message_date">Data: %s</string>
<string name="message_reply">Odpowiedz</string>
<string name="message_subject">Temat</string>
<string name="message_content">Treść</string>
<string name="message_send_successful">Wiadomość wysłana pomyślnie</string>
<string name="message_required_recipients">Musisz wybrać co najmniej 1 adresata</string>
<string name="message_content_min_length">Treść wiadomości musi zawierać co najmniej 3 znaki</string>
<plurals name="message_number_item"> <plurals name="message_number_item">
<item quantity="one">%d wiadomość</item> <item quantity="one">%d wiadomość</item>
<item quantity="few">%d wiadomości</item> <item quantity="few">%d wiadomości</item>
@ -154,16 +161,6 @@
<item quantity="many">Dostałeś %1$d wiadomości</item> <item quantity="many">Dostałeś %1$d wiadomości</item>
</plurals> </plurals>
<!--Send message-->
<string name="send_message_title">Wyślij</string>
<string name="send_message_from">Od:</string>
<string name="send_message_to">Do:</string>
<string name="send_message_subject">Temat</string>
<string name="send_message_content">Treść</string>
<string name="send_message_successful">Wiadomość wysłana pomyślnie</string>
<string name="send_message_required_recipients">Musisz wybrać co najmniej 1 adresata</string>
<string name="send_message_content_min_length">Treść wiadomości musi zawierać co najmniej 3 znaki</string>
<!--Note--> <!--Note-->
<string name="note_no_items">Brak informacji o uwagach</string> <string name="note_no_items">Brak informacji o uwagach</string>
@ -241,8 +238,6 @@
<string name="pref_view_summary">Pokazuj podsumowanie w ocenach</string> <string name="pref_view_summary">Pokazuj podsumowanie w ocenach</string>
<string name="pref_view_present">Pokazuj obecność we frekwencji</string> <string name="pref_view_present">Pokazuj obecność we frekwencji</string>
<string name="pref_view_theme_dark">Ciemny motyw (Beta)</string> <string name="pref_view_theme_dark">Ciemny motyw (Beta)</string>
<string name="pref_view_grade_modifier_plus">Wartość plusa</string>
<string name="pref_view_grade_modifier_minus">Wartość minusa</string>
<string name="pref_view_expand_grade">Rozwiń oceny</string> <string name="pref_view_expand_grade">Rozwiń oceny</string>
<string name="pref_grade_color_scheme">Schemat kolorów ocen</string> <string name="pref_grade_color_scheme">Schemat kolorów ocen</string>
@ -256,6 +251,11 @@
<string name="pref_services_interval">Interwał aktualizacji</string> <string name="pref_services_interval">Interwał aktualizacji</string>
<string name="pref_services_wifi">Tylko WiFi</string> <string name="pref_services_wifi">Tylko WiFi</string>
<string name="pref_other_header">Inne</string>
<string name="pref_other_grade_modifier_plus">Wartość plusa</string>
<string name="pref_other_grade_modifier_minus">Wartość minusa</string>
<string name="pref_other_fill_message_content">Odpowiadaj z historią wiadomości</string>
<!--Notification Channels--> <!--Notification Channels-->
<string name="channel_new_entries">Nowe wpisy w dzienniku</string> <string name="channel_new_entries">Nowe wpisy w dzienniku</string>

View File

@ -32,8 +32,11 @@
<color name="bottom_nav_background">#303030</color> <color name="bottom_nav_background">#303030</color>
<color name="bottom_nav_background_inverse">#ffffff</color> <color name="bottom_nav_background_inverse">#ffffff</color>
<color name="chip_material_background_inverse">#595959</color> <color name="chip_backgroundColor_inverse">#595959</color>
<color name="chip_default_text_color_inverse">#fefefe</color> <color name="chip_labelColor_inverse">#fefefe</color>
<color name="chip_detailed_backgroundColor">#fefefe</color>
<color name="filterable_list_backgroundColor_inverse">#393939</color>
<color name="filterable_list_textColor_inverse">#fefefe</color>
<color name="divider">#cccccc</color> <color name="divider">#cccccc</color>
<color name="divider_inverse">#777777</color> <color name="divider_inverse">#777777</color>

View File

@ -3,8 +3,6 @@
<string name="pref_key_start_menu">start_menu</string> <string name="pref_key_start_menu">start_menu</string>
<string name="pref_key_attendance_present">attendance_present</string> <string name="pref_key_attendance_present">attendance_present</string>
<string name="pref_key_theme">theme</string> <string name="pref_key_theme">theme</string>
<string name="pref_key_grade_modifier_plus">grade_modifier_plus</string>
<string name="pref_key_grade_modifier_minus">grade_modifier_minus</string>
<string name="pref_key_grade_color_scheme">grade_color_scheme</string> <string name="pref_key_grade_color_scheme">grade_color_scheme</string>
<string name="pref_key_expand_grade">expand_grade</string> <string name="pref_key_expand_grade">expand_grade</string>
<string name="pref_key_services_enable">services_enable</string> <string name="pref_key_services_enable">services_enable</string>
@ -12,4 +10,7 @@
<string name="pref_key_services_wifi_only">services_disable_wifi_only</string> <string name="pref_key_services_wifi_only">services_disable_wifi_only</string>
<string name="pref_key_notifications_enable">notifications_enable</string> <string name="pref_key_notifications_enable">notifications_enable</string>
<string name="pref_key_notification_debug">notification_debug</string> <string name="pref_key_notification_debug">notification_debug</string>
<string name="pref_key_grade_modifier_plus">grade_modifier_plus</string>
<string name="pref_key_grade_modifier_minus">grade_modifier_minus</string>
<string name="pref_key_fill_message_content">fill_message_content</string>
</resources> </resources>

View File

@ -13,6 +13,7 @@
<string name="more_title">More</string> <string name="more_title">More</string>
<string name="about_title">About</string> <string name="about_title">About</string>
<string name="message_title">Messages</string> <string name="message_title">Messages</string>
<string name="send_message_title">New message</string>
<string name="note_title">Notes and achievements</string> <string name="note_title">Notes and achievements</string>
<string name="homework_title">Homework</string> <string name="homework_title">Homework</string>
<string name="account_title">Choose account</string> <string name="account_title">Choose account</string>
@ -129,9 +130,15 @@
<string name="message_no_subject">(no subject)</string> <string name="message_no_subject">(no subject)</string>
<string name="message_no_items">No messages</string> <string name="message_no_items">No messages</string>
<string name="message_preview_error">An error occurred while downloading message content</string> <string name="message_preview_error">An error occurred while downloading message content</string>
<string name="message_from">From: %s</string> <string name="message_from">From:</string>
<string name="message_to">To: %s</string> <string name="message_to">To:</string>
<string name="message_date">Date: %s</string> <string name="message_date">Date: %s</string>
<string name="message_reply">Reply</string>
<string name="message_subject">Subject</string>
<string name="message_content">Content</string>
<string name="message_send_successful">Message sent successfully</string>
<string name="message_required_recipients">You need to choose at least 1 recipient</string>
<string name="message_content_min_length">The message content must be at least 3 characters</string>
<plurals name="message_number_item"> <plurals name="message_number_item">
<item quantity="one">%d message</item> <item quantity="one">%d message</item>
<item quantity="other">%d messages</item> <item quantity="other">%d messages</item>
@ -145,16 +152,6 @@
<item quantity="other">You received %1$d messages</item> <item quantity="other">You received %1$d messages</item>
</plurals> </plurals>
<!--Send message-->
<string name="send_message_title">Send</string>
<string name="send_message_from">From:</string>
<string name="send_message_to">To:</string>
<string name="send_message_subject">Subject</string>
<string name="send_message_content">Content</string>
<string name="send_message_successful">Message sent successfully</string>
<string name="send_message_required_recipients">You need to choose at least 1 recipient</string>
<string name="send_message_content_min_length">The message content must be at least 3 characters</string>
<!--Note--> <!--Note-->
<string name="note_no_items">No info about notes</string> <string name="note_no_items">No info about notes</string>
@ -226,8 +223,6 @@
<string name="pref_view_summary">Show the summary in the grades</string> <string name="pref_view_summary">Show the summary in the grades</string>
<string name="pref_view_present">Show presence in attendance</string> <string name="pref_view_present">Show presence in attendance</string>
<string name="pref_view_theme_dark">Dark theme (Beta)</string> <string name="pref_view_theme_dark">Dark theme (Beta)</string>
<string name="pref_view_grade_modifier_plus">Value of the plus</string>
<string name="pref_view_grade_modifier_minus">Value of the minus</string>
<string name="pref_view_expand_grade">Expand grades</string> <string name="pref_view_expand_grade">Expand grades</string>
<string name="pref_grade_color_scheme">Grades color scheme</string> <string name="pref_grade_color_scheme">Grades color scheme</string>
@ -241,6 +236,11 @@
<string name="pref_services_interval">Updates interval</string> <string name="pref_services_interval">Updates interval</string>
<string name="pref_services_wifi">Wi-Fi only</string> <string name="pref_services_wifi">Wi-Fi only</string>
<string name="pref_other_header">Other</string>
<string name="pref_other_grade_modifier_plus">Value of the plus</string>
<string name="pref_other_grade_modifier_minus">Value of the minus</string>
<string name="pref_other_fill_message_content">Reply with message history</string>
<!--Notification Channels--> <!--Notification Channels-->
<string name="channel_new_entries">New entries in register</string> <string name="channel_new_entries">New entries in register</string>

View File

@ -14,9 +14,8 @@
<item name="subtitleTextColor">@android:color/primary_text_dark</item> <item name="subtitleTextColor">@android:color/primary_text_dark</item>
<item name="android:colorBackground">@android:color/white</item> <item name="android:colorBackground">@android:color/white</item>
<item name="bottomNavBackground">@color/bottom_nav_background_inverse</item> <item name="bottomNavBackground">@color/bottom_nav_background_inverse</item>
<item name="chipBackgroundColor">@color/chip_material_background</item>
<item name="chipTextColor">@color/chip_default_text_color</item>
<item name="dividerColor">@color/divider</item> <item name="dividerColor">@color/divider</item>
<item name="chip_detailed_backgroundColor">@color/chip_detailed_backgroundColor</item>
<!-- AboutLibraries specific values --> <!-- AboutLibraries specific values -->
<item name="about_libraries_window_background">@color/about_libraries_window_background</item> <item name="about_libraries_window_background">@color/about_libraries_window_background</item>

View File

@ -38,22 +38,6 @@
android:summary="%s" android:summary="%s"
android:title="@string/pref_grade_color_scheme" android:title="@string/pref_grade_color_scheme"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="0.0"
android:entries="@array/grade_modifier_entries"
android:entryValues="@array/grade_modifier_value"
android:key="@string/pref_key_grade_modifier_plus"
android:summary="%s"
android:title="@string/pref_view_grade_modifier_plus"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="0.0"
android:entries="@array/grade_modifier_entries"
android:entryValues="@array/grade_modifier_value"
android:key="@string/pref_key_grade_modifier_minus"
android:summary="%s"
android:title="@string/pref_view_grade_modifier_minus"
app:iconSpaceReserved="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_services_header" android:title="@string/pref_services_header"
@ -94,4 +78,29 @@
android:title="@string/pref_notify_debug_switch" android:title="@string/pref_notify_debug_switch"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_other_header"
app:iconSpaceReserved="false">
<ListPreference
android:defaultValue="0.0"
android:entries="@array/grade_modifier_entries"
android:entryValues="@array/grade_modifier_value"
android:key="@string/pref_key_grade_modifier_plus"
android:summary="%s"
android:title="@string/pref_other_grade_modifier_plus"
app:iconSpaceReserved="false" />
<ListPreference
android:defaultValue="0.0"
android:entries="@array/grade_modifier_entries"
android:entryValues="@array/grade_modifier_value"
android:key="@string/pref_key_grade_modifier_minus"
android:summary="%s"
android:title="@string/pref_other_grade_modifier_minus"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/pref_key_fill_message_content"
android:title="@string/pref_other_fill_message_content"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.Recipient
import org.junit.Assert.assertEquals
import org.junit.Test
class TestRecipientChip {
@Test
fun testRecipientChipInfo() {
assertEquals("Uczeń", getRecipientChip("Kowalski Jan - Uczeń").info)
assertEquals("(JK) - pracownik [Fake123456]", getRecipientChip("Kowalski Jan (JK) - pracownik [Fake123456]").info)
assertEquals("[KK] - pracownik (Fake123456)", getRecipientChip("Kowalska Karolina [KK] - pracownik (Fake123456)").info)
assertEquals("(BK) - Nauczyciel [Fake123456]", getRecipientChip("Kowal-Mazur Barbara (BK) - Nauczyciel [Fake123456]").info)
}
private fun getRecipientChip(realName: String): RecipientChip {
return RecipientChip(Recipient(0, "", "", realName, 0, 0, 0, ""))
}
}