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"
script:
- ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --stacktrace --daemon
- ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true
- ./gradlew lint -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
@Provides
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.JournalMode.TRUNCATE
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.AttendanceSummaryDao
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.MessagesDao
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.SemesterDao
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.Message
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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.migrations.Migration2
import javax.inject.Singleton
@Singleton
@ -46,9 +51,10 @@ import javax.inject.Singleton
Message::class,
Note::class,
Homework::class,
Subject::class
Subject::class,
LuckyNumber::class
],
version = 1,
version = 2,
exportSchema = false
)
@TypeConverters(Converters::class)
@ -58,6 +64,9 @@ abstract class AppDatabase : RoomDatabase() {
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
.setJournalMode(TRUNCATE)
.addMigrations(
Migration2()
)
.build()
}
}
@ -85,4 +94,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val homeworkDao: HomeworkDao
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.GradeSummaryRepository
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.MessageFolder.RECEIVED
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.TimetableRepository
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.NoteNotification
import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday
import io.reactivex.Single
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import org.threeten.bp.LocalDate
import timber.log.Timber
@ -59,6 +61,9 @@ class SyncWorker : SimpleJobService() {
@Inject
lateinit var homework: HomeworkRepository
@Inject
lateinit var luckyNumber: LuckyNumberRepository
@Inject
lateinit var prefRepository: PreferencesRepository
@ -88,18 +93,19 @@ class SyncWorker : SimpleJobService() {
disposable.add(student.getCurrentStudent()
.flatMap { semester.getCurrentSemester(it, true).map { semester -> semester to it } }
.flatMapPublisher {
Single.merge(
.flatMapCompletable {
Completable.merge(
listOf(
gradesDetails.getGrades(it.first, true, notify),
gradesSummary.getGradesSummary(it.first, true),
attendance.getAttendance(it.first, start, end, true),
exam.getExams(it.first, start, end, true),
timetable.getTimetable(it.first, start, end, true),
message.getMessages(it.second, RECEIVED, true, notify),
note.getNotes(it.first, true, notify),
homework.getHomework(it.first, LocalDate.now(), true),
homework.getHomework(it.first, LocalDate.now().plusDays(1), true)
gradesDetails.getGrades(it.first, true, notify).ignoreElement(),
gradesSummary.getGradesSummary(it.first, true).ignoreElement(),
attendance.getAttendance(it.first, start, end, true).ignoreElement(),
exam.getExams(it.first, start, end, true).ignoreElement(),
timetable.getTimetable(it.first, start, end, true).ignoreElement(),
message.getMessages(it.second, RECEIVED, true, notify).ignoreElement(),
note.getNotes(it.first, true, notify).ignoreElement(),
homework.getHomework(it.first, LocalDate.now(), true).ignoreElement(),
homework.getHomework(it.first, LocalDate.now().plusDays(1), true).ignoreElement(),
luckyNumber.getLuckyNumber(it.first, true, notify).ignoreElement()
)
)
}
@ -119,6 +125,7 @@ class SyncWorker : SimpleJobService() {
sendGradeNotifications()
sendMessageNotification()
sendNoteNotification()
sendLuckyNumberNotification()
}
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() {
super.onDestroy()
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.GradeModule
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.MessageModule
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
@ -86,6 +87,10 @@ abstract class MainModule {
@ContributesAndroidInjector
abstract fun bindHomeworkFragment(): HomeworkFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindLuckyNumberFragment(): LuckyNumberFragment
@PerFragment
@ContributesAndroidInjector
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.modules.about.AboutFragment
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.MainView
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?>?
get() {
return context?.run {
@ -114,6 +123,10 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
(activity as? MainActivity)?.pushView(NoteFragment.newInstance())
}
override fun openLuckyNumberView() {
(activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance())
}
override fun openSettingsView() {
(activity as? MainActivity)?.pushView(SettingsFragment.newInstance())
}

View File

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

View File

@ -11,6 +11,8 @@ interface MoreView : BaseView {
val noteRes: Pair<String, Drawable?>?
val luckyNumberRes: Pair<String, Drawable?>?
val settingsRes: Pair<String, Drawable?>?
val aboutRes: Pair<String, Drawable?>?
@ -30,4 +32,6 @@ interface MoreView : BaseView {
fun openHomeworkView()
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-->
<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-->
<string name="account_add_new">Dodaj konto</string>

View File

@ -167,6 +167,15 @@
<!--Homework-->
<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-->
<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)
}
}