1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 01:19:08 -05:00

Add lucky numbers (#216)

This commit is contained in:
Kacper Ziubryniewicz 2019-01-25 20:54:27 +01:00 committed by Rafał Borcz
parent d3c13b8fc3
commit 4da812af39
29 changed files with 658 additions and 15 deletions

View File

@ -41,7 +41,7 @@ before_script:
- "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash"
script: script:
- ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --stacktrace --daemon - ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true - fossa --no-ansi || true
- ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon - ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon
- ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon - ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon

View File

@ -0,0 +1,47 @@
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.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class LuckyNumberLocalTest {
private lateinit var luckyNumberLocal: LuckyNumberLocal
private lateinit var testDb: AppDatabase
@Before
fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build()
luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao)
}
@After
fun closeDb() {
testDb.close()
}
@Test
fun saveAndReadTest() {
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, 2, "", 3, 1),
LocalDate.of(2019, 1, 20)
).blockingGet()
assertEquals(1, luckyNumber.studentId)
assertEquals(LocalDate.of(2019, 1, 20), luckyNumber.date)
assertEquals(14, luckyNumber.luckyNumber)
}
}

View File

@ -113,4 +113,8 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideSubjectDao(database: AppDatabase) = database.subjectDao fun provideSubjectDao(database: AppDatabase) = database.subjectDao
@Singleton
@Provides
fun provideLuckyNumberDao(database: AppDatabase) = database.luckyNumberDao
} }

View File

@ -6,6 +6,8 @@ import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.RoomDatabase.JournalMode.TRUNCATE import androidx.room.RoomDatabase.JournalMode.TRUNCATE
import androidx.room.TypeConverters import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.github.wulkanowy.data.db.dao.AttendanceDao import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.dao.ExamDao import io.github.wulkanowy.data.db.dao.ExamDao
@ -13,6 +15,7 @@ import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.dao.HomeworkDao import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.dao.NoteDao import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.dao.StudentDao
@ -25,11 +28,13 @@ import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
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.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.Migration2
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -46,9 +51,10 @@ import javax.inject.Singleton
Message::class, Message::class,
Note::class, Note::class,
Homework::class, Homework::class,
Subject::class Subject::class,
LuckyNumber::class
], ],
version = 1, version = 2,
exportSchema = false exportSchema = false
) )
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
@ -58,6 +64,9 @@ abstract class AppDatabase : RoomDatabase() {
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")
.setJournalMode(TRUNCATE) .setJournalMode(TRUNCATE)
.addMigrations(
Migration2()
)
.build() .build()
} }
} }
@ -85,4 +94,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val homeworkDao: HomeworkDao abstract val homeworkDao: HomeworkDao
abstract val subjectDao: SubjectDao abstract val subjectDao: SubjectDao
abstract val luckyNumberDao: LuckyNumberDao
} }

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@Singleton
@Dao
interface LuckyNumberDao {
@Insert
fun insert(luckyNumber: LuckyNumber)
@Update
fun update(luckyNumber: LuckyNumber)
@Delete
fun delete(luckyNumber: LuckyNumber)
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
fun loadFromDate(studentId: Int, date: LocalDate): Maybe<LuckyNumber>
}

View File

@ -0,0 +1,28 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable
@Entity(tableName = "LuckyNumbers")
data class LuckyNumber (
@ColumnInfo(name = "student_id")
var studentId: Int,
var date: LocalDate,
@ColumnInfo(name = "lucky_number")
var luckyNumber: Int
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE LuckyNumbers (" +
"id INTEGER NOT NULL PRIMARY KEY, " +
"is_notified INTEGER NOT NULL, " +
"student_id INTEGER NOT NULL, " +
"date INTEGER NOT NULL, " +
"lucky_number INTEGER NOT NULL)")
}
}

View File

@ -0,0 +1,52 @@
package io.github.wulkanowy.data.repositories
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.local.LuckyNumberLocal
import io.github.wulkanowy.data.repositories.remote.LuckyNumberRemote
import io.reactivex.Completable
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LuckyNumberRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: LuckyNumberLocal,
private val remote: LuckyNumberRemote
) {
fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMapMaybe {
if (it) remote.getLuckyNumber(semester)
else Maybe.error(UnknownHostException())
}.flatMap { new ->
local.getLuckyNumber(semester, LocalDate.now())
.doOnSuccess { old ->
if (new != old) {
local.deleteLuckyNumber(old)
local.saveLuckyNumber(new.apply {
if (notify) isNotified = false
})
}
}
.doOnComplete {
local.saveLuckyNumber(new.apply {
if (notify) isNotified = false
})
}
}.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) },
{ local.getLuckyNumber(semester, LocalDate.now()) })
)
}
fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
return local.updateLuckyNumber(luckyNumber)
}
}

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.data.repositories.local
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Completable
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) {
fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe<LuckyNumber> {
return luckyNumberDb.loadFromDate(semester.studentId, date)
}
fun saveLuckyNumber(luckyNumber: LuckyNumber) {
luckyNumberDb.insert(luckyNumber)
}
fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
return Completable.fromCallable { luckyNumberDb.update(luckyNumber) }
}
fun deleteLuckyNumber(luckyNumber: LuckyNumber) {
luckyNumberDb.delete(luckyNumber)
}
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LuckyNumberRemote @Inject constructor(private val api: Api) {
fun getLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMapMaybe { it.getLuckyNumber() }
.map {
LuckyNumber(
studentId = semester.studentId,
date = LocalDate.now(),
luckyNumber = it
)
}
}
}

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.GradeSummaryRepository import io.github.wulkanowy.data.repositories.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.MessagesRepository import io.github.wulkanowy.data.repositories.MessagesRepository
import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.MessagesRepository.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.NoteRepository import io.github.wulkanowy.data.repositories.NoteRepository
@ -16,12 +17,13 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.services.notification.GradeNotification import io.github.wulkanowy.services.notification.GradeNotification
import io.github.wulkanowy.services.notification.LuckyNumberNotification
import io.github.wulkanowy.services.notification.MessageNotification import io.github.wulkanowy.services.notification.MessageNotification
import io.github.wulkanowy.services.notification.NoteNotification import io.github.wulkanowy.services.notification.NoteNotification
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.isHolidays import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Single import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import timber.log.Timber import timber.log.Timber
@ -59,6 +61,9 @@ class SyncWorker : SimpleJobService() {
@Inject @Inject
lateinit var homework: HomeworkRepository lateinit var homework: HomeworkRepository
@Inject
lateinit var luckyNumber: LuckyNumberRepository
@Inject @Inject
lateinit var prefRepository: PreferencesRepository lateinit var prefRepository: PreferencesRepository
@ -88,18 +93,19 @@ class SyncWorker : SimpleJobService() {
disposable.add(student.getCurrentStudent() disposable.add(student.getCurrentStudent()
.flatMap { semester.getCurrentSemester(it, true).map { semester -> semester to it } } .flatMap { semester.getCurrentSemester(it, true).map { semester -> semester to it } }
.flatMapPublisher { .flatMapCompletable {
Single.merge( Completable.merge(
listOf( listOf(
gradesDetails.getGrades(it.first, true, notify), gradesDetails.getGrades(it.first, true, notify).ignoreElement(),
gradesSummary.getGradesSummary(it.first, true), gradesSummary.getGradesSummary(it.first, true).ignoreElement(),
attendance.getAttendance(it.first, start, end, true), attendance.getAttendance(it.first, start, end, true).ignoreElement(),
exam.getExams(it.first, start, end, true), exam.getExams(it.first, start, end, true).ignoreElement(),
timetable.getTimetable(it.first, start, end, true), timetable.getTimetable(it.first, start, end, true).ignoreElement(),
message.getMessages(it.second, RECEIVED, true, notify), message.getMessages(it.second, RECEIVED, true, notify).ignoreElement(),
note.getNotes(it.first, true, notify), note.getNotes(it.first, true, notify).ignoreElement(),
homework.getHomework(it.first, LocalDate.now(), true), homework.getHomework(it.first, LocalDate.now(), true).ignoreElement(),
homework.getHomework(it.first, LocalDate.now().plusDays(1), true) homework.getHomework(it.first, LocalDate.now().plusDays(1), true).ignoreElement(),
luckyNumber.getLuckyNumber(it.first, true, notify).ignoreElement()
) )
) )
} }
@ -119,6 +125,7 @@ class SyncWorker : SimpleJobService() {
sendGradeNotifications() sendGradeNotifications()
sendMessageNotification() sendMessageNotification()
sendNoteNotification() sendNoteNotification()
sendLuckyNumberNotification()
} }
private fun sendGradeNotifications() { private fun sendGradeNotifications() {
@ -170,6 +177,19 @@ class SyncWorker : SimpleJobService() {
) )
} }
private fun sendLuckyNumberNotification() {
disposable.add(student.getCurrentStudent()
.flatMap { semester.getCurrentSemester(it) }
.flatMapMaybe { luckyNumber.getLuckyNumber(it) }
.filter { !it.isNotified }
.doOnSuccess {
LuckyNumberNotification(applicationContext).sendNotification(it)
}
.map { it.apply { isNotified = true } }
.flatMapCompletable { luckyNumber.updateLuckyNumber(it) }
.subscribe({}, { Timber.e("Lucky number notification sending failed") }))
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
disposable.clear() disposable.clear()

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.services.notification
import android.annotation.TargetApi
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.ui.modules.main.MainActivity
class LuckyNumberNotification(context: Context) : BaseNotification(context) {
private val channelId = "Lucky_Number_Notify"
@TargetApi(26)
override fun createChannel(channelId: String) {
notificationManager.createNotificationChannel(NotificationChannel(
channelId, context.getString(R.string.notify_lucky_number_channel), NotificationManager.IMPORTANCE_HIGH
).apply {
enableLights(true)
enableVibration(true)
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
})
}
fun sendNotification(luckyNumber: LuckyNumber) {
notify(notificationBuilder(channelId)
.setContentTitle(context.getString(R.string.notify_lucky_number_new_item_title))
.setContentText(context.getString(R.string.notify_lucky_number_new_item, luckyNumber.luckyNumber))
.setSmallIcon(R.drawable.ic_stat_notify_lucky_number)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(ContextCompat.getColor(context, R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context).putExtra(MainActivity.EXTRA_START_MENU_INDEX, 4),
PendingIntent.FLAG_UPDATE_CURRENT
)
)
.build()
)
}
}

View File

@ -0,0 +1,64 @@
package io.github.wulkanowy.ui.modules.luckynumber
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.main.MainView
import kotlinx.android.synthetic.main.fragment_lucky_number.*
import javax.inject.Inject
class LuckyNumberFragment : BaseSessionFragment(), LuckyNumberView, MainView.TitledView {
@Inject
lateinit var presenter: LuckyNumberPresenter
companion object {
fun newInstance() = LuckyNumberFragment()
}
override val titleStringId: Int
get() = R.string.lucky_number_title
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_lucky_number, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
showContent(false)
showProgress(true)
luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
}
override fun updateData(data: LuckyNumber) {
luckyNumberText.text = data.luckyNumber.toString()
}
override fun hideRefresh() {
luckyNumberSwipe.isRefreshing = false
}
override fun showEmpty(show: Boolean) {
luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showProgress(show: Boolean) {
luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showContent(show: Boolean) {
luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE
}
override fun isViewEmpty(): Boolean {
return luckyNumberText.text.isBlank()
}
}

View File

@ -0,0 +1,65 @@
package io.github.wulkanowy.ui.modules.luckynumber
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.MaybeSource
import javax.inject.Inject
class LuckyNumberPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
private val luckyNumberRepository: LuckyNumberRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LuckyNumberView>(errorHandler) {
override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view)
view.initView()
loadData()
}
private fun loadData(forceRefresh: Boolean = false) {
disposable.apply {
clear()
add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
}
.subscribe({
view?.apply {
updateData(it)
showContent(true)
showEmpty(false)
}
analytics.logEvent("load_lucky_number", mapOf("force_refresh" to forceRefresh))
}, {
view?.run { showEmpty(isViewEmpty()) }
errorHandler.dispatch(it)
}, {
view?.run {
showContent(false)
showEmpty(true)
}
})
)
}
}
fun onSwipeRefresh() {
loadData(true)
}
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.ui.modules.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.ui.base.session.BaseSessionView
interface LuckyNumberView : BaseSessionView {
fun initView()
fun updateData(data: LuckyNumber)
fun hideRefresh()
fun showEmpty(show: Boolean)
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
fun isViewEmpty(): Boolean
}

View File

@ -16,6 +16,7 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeModule import io.github.wulkanowy.ui.modules.grade.GradeModule
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
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
@ -86,6 +87,10 @@ abstract class MainModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindHomeworkFragment(): HomeworkFragment abstract fun bindHomeworkFragment(): HomeworkFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindLuckyNumberFragment(): LuckyNumberFragment
@PerFragment @PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindsAccountDialog(): AccountDialog abstract fun bindsAccountDialog(): AccountDialog

View File

@ -13,6 +13,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.main.MainActivity 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.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
@ -60,6 +61,14 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
} }
} }
override val luckyNumberRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.lucky_number_title) to
ContextCompat.getDrawable(this, R.drawable.ic_more_lucky_number_24dp)
}
}
override val settingsRes: Pair<String, Drawable?>? override val settingsRes: Pair<String, Drawable?>?
get() { get() {
return context?.run { return context?.run {
@ -114,6 +123,10 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
(activity as? MainActivity)?.pushView(NoteFragment.newInstance()) (activity as? MainActivity)?.pushView(NoteFragment.newInstance())
} }
override fun openLuckyNumberView() {
(activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance())
}
override fun openSettingsView() { override fun openSettingsView() {
(activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) (activity as? MainActivity)?.pushView(SettingsFragment.newInstance())
} }

View File

@ -23,6 +23,7 @@ class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresen
messagesRes?.first -> openMessagesView() messagesRes?.first -> openMessagesView()
homeworkRes?.first -> openHomeworkView() homeworkRes?.first -> openHomeworkView()
noteRes?.first -> openNoteView() noteRes?.first -> openNoteView()
luckyNumberRes?.first -> openLuckyNumberView()
settingsRes?.first -> openSettingsView() settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView() aboutRes?.first -> openAboutView()
} }
@ -42,6 +43,7 @@ class MorePresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresen
messagesRes?.let { MoreItem(it.first, it.second) }, messagesRes?.let { MoreItem(it.first, it.second) },
homeworkRes?.let { MoreItem(it.first, it.second) }, homeworkRes?.let { MoreItem(it.first, it.second) },
noteRes?.let { MoreItem(it.first, it.second) }, noteRes?.let { MoreItem(it.first, it.second) },
luckyNumberRes?.let { MoreItem(it.first, it.second) },
settingsRes?.let { MoreItem(it.first, it.second) }, settingsRes?.let { MoreItem(it.first, it.second) },
aboutRes?.let { MoreItem(it.first, it.second) }) aboutRes?.let { MoreItem(it.first, it.second) })
) )

View File

@ -11,6 +11,8 @@ interface MoreView : BaseView {
val noteRes: Pair<String, Drawable?>? val noteRes: Pair<String, Drawable?>?
val luckyNumberRes: Pair<String, Drawable?>?
val settingsRes: Pair<String, Drawable?>? val settingsRes: Pair<String, Drawable?>?
val aboutRes: Pair<String, Drawable?>? val aboutRes: Pair<String, Drawable?>?
@ -30,4 +32,6 @@ interface MoreView : BaseView {
fun openHomeworkView() fun openHomeworkView()
fun openNoteView() fun openNoteView()
fun openLuckyNumberView()
} }

View File

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956"
android:tint="#FFFFFF">
<group
android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2,9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83,12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22,8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z" />
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2,9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83,12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22,8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z"/>
</vector>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/luckyNumberProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/luckyNumberSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/luckyNumberContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/lucky_number_header"
android:textSize="24sp" />
<TextView
android:id="@+id/luckyNumberText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="14"
android:textSize="52sp" />
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/luckyNumberEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="100dp"
android:minHeight="100dp"
app:srcCompat="@drawable/ic_more_lucky_number_24dp"
app:tint="?android:attr/textColorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/lucky_number_empty"
android:textSize="20sp" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -184,6 +184,15 @@
<!--Homework--> <!--Homework-->
<string name="homework_no_items">Brak zadań domowych</string> <string name="homework_no_items">Brak zadań domowych</string>
<!--Lucky number-->
<string name="lucky_number_title">Szczęśliwy numerek</string>
<string name="lucky_number_header">Dzisiejszym szczęśliwym numerkiem jest</string>
<string name="lucky_number_empty">Brak informacji o szczęśliwym numerku</string>
<!--Lucky number notify-->
<string name="notify_lucky_number_channel">Nowe szczęśliwe numerki</string>
<string name="notify_lucky_number_new_item_title">Szczęśliwy numerek na dzisiaj</string>
<string name="notify_lucky_number_new_item">Dziś szczęśliwym numerkiem jest: %d</string>
<!--Account--> <!--Account-->
<string name="account_add_new">Dodaj konto</string> <string name="account_add_new">Dodaj konto</string>

View File

@ -167,6 +167,15 @@
<!--Homework--> <!--Homework-->
<string name="homework_no_items">No info about homework</string> <string name="homework_no_items">No info about homework</string>
<!--Lucky number-->
<string name="lucky_number_title">Lucky number</string>
<string name="lucky_number_header">Today\'s lucky number is</string>
<string name="lucky_number_empty">No info about the lucky number</string>
<!--Lucky number notify-->
<string name="notify_lucky_number_channel">New lucky numbers</string>
<string name="notify_lucky_number_new_item_title">Lucky number for today</string>
<string name="notify_lucky_number_new_item">Today\'s lucky number is: %d</string>
<!--Account--> <!--Account-->
<string name="account_add_new">Add account</string> <string name="account_add_new">Add account</string>

View File

@ -0,0 +1,44 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Maybe
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
class LuckyNumberRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var semesterMock: Semester
@Before
fun initApi() {
MockKAnnotations.init(this)
}
@Test
fun getLuckyNumberTest() {
every { mockApi.getLuckyNumber() } returns Maybe.just(14)
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1
val luckyNumber = LuckyNumberRemote(mockApi)
.getLuckyNumber(semesterMock)
.blockingGet()
assertEquals(14, luckyNumber.luckyNumber)
assertEquals(LocalDate.now(), luckyNumber.date)
assertEquals(semesterMock.studentId, luckyNumber.studentId)
}
}