Merge branch '0.7.x'

This commit is contained in:
Mikołaj Pich 2019-03-24 23:08:50 +01:00
commit a2a18e5652
No known key found for this signature in database
GPG Key ID: F62B26E36D4C4BAA
45 changed files with 296 additions and 225 deletions

View File

@ -11,10 +11,10 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
#branches:
# only:
# - master
# - 0.7.x
branches:
only:
- master
- 0.7.x
android:
licenses:

View File

@ -16,8 +16,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 28
versionCode 27
versionName "0.7.1"
versionCode 28
versionName "0.7.2"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -77,7 +77,7 @@ play {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('io.github.wulkanowy:api:0.7.1') { exclude module: "threetenbp" }
implementation('com.github.wulkanowy:api:master-SNAPSHOT') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2"
@ -86,10 +86,14 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation "androidx.work:work-runtime:2.0.0"
implementation "androidx.work:work-rxjava2:2.0.0"
implementation "android.arch.work:work-rxjava2:1.0.0"
implementation "android.arch.work:work-runtime:1.0.0"
implementation "androidx.room:room-runtime:2.1.0-alpha06"
implementation "androidx.room:room-rxjava2:2.1.0-alpha06"
kapt "androidx.room:room-compiler:2.1.0-alpha06"
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
@ -98,17 +102,13 @@ dependencies {
kapt "com.google.dagger:dagger-compiler:2.21"
kapt "com.google.dagger:dagger-android-processor:2.21"
implementation "androidx.room:room-runtime:2.1.0-alpha05"
implementation "androidx.room:room-rxjava2:2.1.0-alpha05"
kapt "androidx.room:room-compiler:2.1.0-alpha05"
implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation 'com.ncapdevi:frag-nav:3.1.0'
implementation 'com.ncapdevi:frag-nav:3.2.0'
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
implementation 'com.github.PhilJay:MPAndroidChart:971640b29d'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
@ -128,15 +128,15 @@ dependencies {
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.9.1"
testImplementation "org.mockito:mockito-inline:2.25.0"
testImplementation "io.mockk:mockk:1.9.2"
testImplementation "org.mockito:mockito-inline:2.25.1"
testImplementation 'org.threeten:threetenbp:1.3.8'
androidTestImplementation "io.mockk:mockk-android:1.9.1"
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'org.mockito:mockito-android:2.25.0'
androidTestImplementation "io.mockk:mockk-android:1.9.2"
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}

View File

@ -52,6 +52,7 @@
<receiver
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
android:exported="true"
android:label="@string/timetable_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy.IGNORE
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
@ -12,15 +12,12 @@ import javax.inject.Singleton
@Dao
interface SemesterDao {
@Insert(onConflict = IGNORE)
@Insert
fun insertAll(semester: List<Semester>)
@Delete
fun deleteAll(semester: List<Semester>)
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
fun loadAll(studentId: Int): Maybe<List<Semester>>
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
fun updateCurrent(semesterId: Int, diaryId: Int)
@Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId")
fun resetCurrent(studentId: Int)
}

View File

@ -11,17 +11,14 @@ import javax.inject.Singleton
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
fun saveSemesters(semesters: List<Semester>) {
return semesterDb.insertAll(semesters)
semesterDb.insertAll(semesters)
}
fun deleteSemesters(semesters: List<Semester>) {
semesterDb.deleteAll(semesters)
}
fun getSemesters(student: Student): Maybe<List<Semester>> {
return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() }
}
fun setCurrentSemester(semester: Semester) {
semesterDb.run {
resetCurrent(semester.studentId)
updateCurrent(semester.semesterId, semester.diaryId)
}
}
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
import io.reactivex.Single
import timber.log.Timber
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@ -25,10 +26,17 @@ class SemesterRepository @Inject constructor(
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
}.map { newSemesters ->
local.apply {
saveSemesters(newSemesters)
setCurrentSemester(newSemesters.single { it.isCurrent })
}.flatMap { new ->
val currentSemesters = new.filter { it.isCurrent }
if (currentSemesters.size == 1) {
local.getSemesters(student).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteSemesters(old - new)
local.saveSemesters(new - old)
}
} else {
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
throw IllegalArgumentException("Current semester can be only one.")
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
}

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.main.MainActivity
@ -30,9 +29,6 @@ internal abstract class BuilderModule {
@ContributesAndroidInjector
abstract fun bindMessageSendActivity(): SendMessageActivity
@ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService
@ContributesAndroidInjector
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
}

View File

@ -9,6 +9,7 @@ import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import dagger.multibindings.IntoSet
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
import io.github.wulkanowy.services.sync.works.AttendanceWork
@ -24,6 +25,7 @@ import io.github.wulkanowy.services.sync.works.NoteWork
import io.github.wulkanowy.services.sync.works.RecipientWork
import io.github.wulkanowy.services.sync.works.TimetableWork
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import javax.inject.Singleton
@AssistedModule
@ -48,6 +50,9 @@ abstract class ServicesModule {
fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}
@ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService
@Binds
@IntoSet
abstract fun provideGradeWork(work: GradeWork): Work

View File

@ -11,6 +11,7 @@ import androidx.work.WorkerParameters
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import io.github.wulkanowy.R
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -18,6 +19,7 @@ import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Single
import timber.log.Timber
import kotlin.random.Random
@ -33,19 +35,31 @@ class SyncWorker @AssistedInject constructor(
) : RxWorker(appContext, workerParameters) {
override fun createWork(): Single<Result> {
return studentRepository.getCurrentStudent()
Timber.i("SyncWorker is starting")
return studentRepository.isStudentSaved()
.filter { true }
.flatMap { studentRepository.getCurrentStudent().toMaybe() }
.flatMapCompletable { student ->
semesterRepository.getCurrentSemester(student, true)
.flatMapCompletable { semester ->
Completable.mergeDelayError(works.map { it.create(student, semester) })
Completable.mergeDelayError(Flowable.fromIterable(works.map { work ->
work.create(student, semester)
.doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") }
.doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") }
.doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") }
}), 3)
}
}
.toSingleDefault(Result.success())
.onErrorReturn {
Timber.e(it, "There was an error during synchronization")
Result.retry()
if (it is FeatureDisabledException) Result.success()
else Result.retry()
}
.doOnSuccess {
if (preferencesRepository.isDebugNotificationEnable) notify(it)
Timber.i("SyncWorker result: $it")
}
.doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) }
}
private fun notify(result: Result) {

View File

@ -13,10 +13,10 @@ class RecipientWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return reportingUnitRepository.getReportingUnits(student)
return reportingUnitRepository.getReportingUnits(student, true)
.flatMapCompletable { units ->
Completable.mergeDelayError(units.map {
recipientRepository.getRecipients(student, 2, it).ignoreElement()
recipientRepository.getRecipients(student, 2, it, true).ignoreElement()
})
}
}

View File

@ -43,7 +43,8 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
.withAboutSpecial3(getString(R.string.about_feedback))
.withFields(R.string::class.java.fields)
.withCheckCachedDetection(false)
.withExcludedLibraries("fastadapter", "AndroidIconics", "gson", "Jsoup", "Retrofit", "okio", "OkHttp")
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
"OkHttp", "Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) })
}.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
@ -68,7 +69,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(Intent.EXTRA_TEXT,"Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
Build: ${BuildConfig.VERSION_CODE}
SDK: ${android.os.Build.VERSION.SDK_INT}
Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}

View File

@ -14,12 +14,12 @@ class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewH
override fun getLayoutRes() = R.layout.item_account
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
holder?.apply {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
accountItemName.text = student.studentName
accountItemSchool.text = student.schoolName
accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0)
@ -38,14 +38,13 @@ class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewH
}
override fun hashCode(): Int {
return student.hashCode()
var result = student.hashCode()
result = 31 * result + student.id.toInt()
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -14,14 +14,13 @@ import kotlinx.android.synthetic.main.item_attendance.*
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_attendance
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_attendance
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
attendanceItemNumber.text = attendance.number.toString()
attendanceItemSubject.text = attendance.subject
@ -37,16 +36,17 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
other as AttendanceItem
if (attendance != other.attendance) return false
return true
}
override fun hashCode(): Int {
return attendance.hashCode()
var result = attendance.hashCode()
result = 31 * result + attendance.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -135,7 +135,7 @@ class AttendancePresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -23,12 +23,12 @@ class AttendanceSummaryItem(
override fun getLayoutRes() = R.layout.item_attendance_summary
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
holder?.apply {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
attendanceSummaryMonth.text = month
attendanceSummaryPercentage.text = percentage
attendanceSummaryPresent.text = present
@ -73,10 +73,8 @@ class AttendanceSummaryItem(
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -18,8 +18,7 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
examItemSubject.text = exam.subject
examItemTeacher.text = exam.teacher
@ -39,12 +38,12 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
}
override fun hashCode(): Int {
return exam.hashCode()
var result = exam.hashCode()
result = 31 * result + exam.id.toInt()
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -7,9 +7,7 @@ import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class GradePresenter @Inject constructor(
@ -30,15 +28,12 @@ class GradePresenter @Inject constructor(
fun onAttachView(view: GradeView, savedIndex: Int?) {
super.onAttachView(view)
Timber.i("Grade view is attached")
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
.subscribe {
selectedIndex = savedIndex ?: 0
view.run {
initView()
enableSwipe(false)
}
loadData()
})
selectedIndex = savedIndex ?: 0
view.run {
initView()
enableSwipe(false)
}
loadData()
}
fun onCreateMenu() {
@ -82,7 +77,7 @@ class GradePresenter @Inject constructor(
}
fun onPageSelected(index: Int) {
loadChild(index)
if (semesters.isNotEmpty()) loadChild(index)
}
fun onSwipeRefresh() {

View File

@ -28,10 +28,7 @@ class GradeDetailsItem(
}
@SuppressLint("SetTextI18n")
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
gradeItemValue.run {
text = grade.entry
@ -70,9 +67,7 @@ class GradeDetailsItem(
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -63,7 +63,7 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onSubjectSelected(name: String) {
Timber.i("Select attendance stats subject $name")
Timber.i("Select grade stats subject $name")
view?.run {
showContent(false)
showProgress(true)
@ -77,7 +77,7 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onTypeChange(isSemester: Boolean) {
Timber.i("Select attendance stats semester: $isSemester")
Timber.i("Select grade stats semester: $isSemester")
disposable.clear()
view?.run {
showContent(false)

View File

@ -18,15 +18,12 @@ class GradeSummaryItem(
override fun getLayoutRes() = R.layout.item_grade_summary
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?,
position: Int, payloads: MutableList<Any>?
) {
holder?.run {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
gradeSummaryItemTitle.text = title
gradeSummaryItemAverage.text = average
gradeSummaryItemPredicted.text = predictedGrade
@ -56,10 +53,8 @@ class GradeSummaryItem(
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -10,9 +10,8 @@ import io.github.wulkanowy.data.db.entities.Homework
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_homework.*
class HomeworkItem(
header: HomeworkHeader, val homework: Homework
) : AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
override fun getLayoutRes() = R.layout.item_homework
@ -20,10 +19,7 @@ class HomeworkItem(
return ViewHolder(view, adapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
homeworkItemSubject.text = homework.subject
homeworkItemTeacher.text = homework.teacher
@ -42,12 +38,12 @@ class HomeworkItem(
}
override fun hashCode(): Int {
return homework.hashCode()
var result = homework.hashCode()
result = 31 * result + homework.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -8,19 +8,21 @@ import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_login_student_select.view.*
import kotlinx.android.synthetic.main.item_login_student_select.*
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
override fun getLayoutRes(): Int = R.layout.item_login_student_select
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ItemViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
return ItemViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ItemViewHolder?,
position: Int, payloads: MutableList<Any>?) {
holder?.bind(student)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
loginItemName.text = student.studentName
loginItemSchool.text = student.schoolName
}
}
override fun equals(other: Any?): Boolean {
@ -38,17 +40,8 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
return student.hashCode()
}
class ItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?)
: FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View?
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = itemView
fun bind(item: Student) {
itemView.run {
loginItemName.text = item.studentName
loginItemSchool.text = item.schoolName
}
}
}
}

View File

@ -22,7 +22,11 @@ class LuckyNumberPresenter @Inject constructor(
override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view)
Timber.i("Lucky number view is attached")
view.initView()
view.run {
initView()
showContent(false)
enableSwipe(false)
}
loadData()
}
@ -35,12 +39,6 @@ class LuckyNumberPresenter @Inject constructor(
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
showContent(false)
enableSwipe(false)
}
}
.doFinally {
view?.run {
hideRefresh()

View File

@ -2,7 +2,10 @@ package io.github.wulkanowy.ui.modules.main
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.os.Handler
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
@ -68,11 +71,6 @@ class MainActivity : BaseActivity(), MainView {
return true
}
override fun onStart() {
super.onStart()
presenter.onViewChange()
}
override fun initView() {
mainBottomNav.run {
addItems(
@ -144,7 +142,9 @@ class MainActivity : BaseActivity(), MainView {
}
override fun notifyMenuViewReselected() {
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
Handler().postDelayed({
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
}, 250)
}
fun showDialogFragment(dialog: DialogFragment) {
@ -165,7 +165,7 @@ class MainActivity : BaseActivity(), MainView {
override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this)
.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) })
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
override fun onSaveInstanceState(outState: Bundle?) {

View File

@ -5,7 +5,6 @@ import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.R
import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.about.AboutModule
@ -33,7 +32,6 @@ abstract class MainModule {
companion object {
@JvmStatic
@PerActivity
@Provides
fun provideFragNavController(activity: MainActivity): FragNavController {
return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer)

View File

@ -24,9 +24,9 @@ class MainPresenter @Inject constructor(
fun onAttachView(view: MainView, initMenuIndex: Int) {
super.onAttachView(view)
Timber.i("Main view is attached with $initMenuIndex menu index")
view.run {
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
Timber.i("Main view is attached with $startMenuIndex menu index")
initView()
}

View File

@ -20,12 +20,9 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
return ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_message
override fun getLayoutRes() = R.layout.item_message
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
val style = if (message.unread) BOLD else NORMAL
@ -58,9 +55,7 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
return message.hashCode()
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -14,12 +14,12 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle
override fun getLayoutRes() = R.layout.item_more
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
holder?.apply {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
moreItemTitle.text = title
moreItemImage.setImageDrawable(drawable)
}
@ -40,9 +40,8 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle
return title.hashCode()
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -15,16 +15,13 @@ import kotlinx.android.synthetic.main.item_note.*
class NoteItem(val note: Note) : AbstractFlexibleItem<NoteItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_note
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): NoteItem.ViewHolder {
return NoteItem.ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_note
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>,
holder: NoteItem.ViewHolder, position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
noteItemDate.apply {
text = note.date.toFormattedString()
@ -57,7 +54,6 @@ class NoteItem(val note: Note) : AbstractFlexibleItem<NoteItem.ViewHolder>() {
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -15,18 +15,17 @@ import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_timetable.*
class TimetableItem(val lesson: Timetable, private val roomText: String)
: AbstractFlexibleItem<TimetableItem.ViewHolder>() {
class TimetableItem(val lesson: Timetable, private val roomText: String) :
AbstractFlexibleItem<TimetableItem.ViewHolder>() {
override fun getLayoutRes(): Int = R.layout.item_timetable
override fun getLayoutRes() = R.layout.item_timetable
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
@SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
timetableItemNumber.text = lesson.number.toString()
timetableItemSubject.text = lesson.subject
@ -34,8 +33,8 @@ class TimetableItem(val lesson: Timetable, private val roomText: String)
timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}"
timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE
timetableItemSubject.paintFlags =
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
}
@ -50,12 +49,12 @@ class TimetableItem(val lesson: Timetable, private val roomText: String)
}
override fun hashCode(): Int {
return lesson.hashCode()
var result = lesson.hashCode()
result = 31 * result + lesson.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -128,7 +128,7 @@ class TimetablePresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -112,7 +112,7 @@ class CompletedLessonsPresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Single
import org.threeten.bp.LocalDate
import timber.log.Timber
@ -51,9 +52,13 @@ class TimetableWidgetFactory(
?.let { date ->
try {
lessons = studentRepository.isStudentSaved()
.flatMap { studentRepository.getCurrentStudent() }
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { timetableRepository.getTimetable(it, date, date) }
.flatMap { isSaved ->
if (isSaved) {
studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { timetableRepository.getTimetable(it, date, date) }
} else Single.just(emptyList())
}
.map { item -> item.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.blockingGet()

View File

@ -62,7 +62,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
private fun onUpdate(context: Context, intent: Intent) {
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId ->
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay)
}
} else {
@ -95,7 +95,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
.apply { action = createWidgetKey(appWidgetId) })
setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT))
setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV))
createNavIntent(context, Int.MAX_VALUE, appWidgetId, BUTTON_RESET).also {
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also {
setOnClickPendingIntent(R.id.timetableWidgetDate, it)
setOnClickPendingIntent(R.id.timetableWidgetDay, it)
}

View File

@ -1,18 +1,6 @@
Dodaliśmy:
- szczęśliwy numerek
- lekcje zrealizowane
- wysyłanie wiadomości
- ucznia na tle klasy
- opcję zmiany kolorów ocen
Zmieniliśmy:
- wygląd ekranu logowania
- sposób wyświetlania zastępstw
- widok zadań domowych na tygodniowy
Naprawiliśmy:
- automatyczne przełączanie widżetu na nowy dzień
- wyświetlanie powiadomień o starych ocenach
- znikające numery sal w planie lekcji
- problemy ze stabilnością podczas przełączania zakładek
- nierówność w nawigacji między dniami w planie lekcji i frekwencji
- zły wygląd odświeżania w szczęśliwym numerku
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.0
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.2

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.repositories
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler
import io.reactivex.Observable
import io.reactivex.Single
class UnitTestInternetObservingStrategy : InternetObservingStrategy {
override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single<Boolean> {
return Single.just(true)
}
override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable<Boolean> {
return Observable.just(true)
}
override fun getDefaultPingHost() = "localhost"
}

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.attendance.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.attendance.AttendanceRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.exams.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.exam.ExamRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.grades.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester

View File

@ -0,0 +1,74 @@
package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
import io.reactivex.Maybe
import io.reactivex.Single
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class SemesterRepositoryTest {
@Mock
private lateinit var semesterRemote: SemesterRemote
@Mock
private lateinit var semesterLocal: SemesterLocal
@Mock
private lateinit var apiHelper: ApiHelper
@Mock
private lateinit var student: Student
private lateinit var semesterRepository: SemesterRepository
private val settings = InternetObservingSettings.builder()
.strategy(UnitTestInternetObservingStrategy())
.build()
@Before
fun initTest() {
MockitoAnnotations.initMocks(this)
semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, apiHelper)
}
@Test
fun singleCurrentSemesterTest() {
val semesters = listOf(
createSemesterEntity(false),
createSemesterEntity(true)
)
doNothing().`when`(apiHelper).initApi(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)
semesterRepository.getSemesters(student).blockingGet()
verify(semesterLocal).deleteSemesters(emptyList())
verify(semesterLocal).saveSemesters(semesters)
}
@Test(expected = IllegalArgumentException::class)
fun twoCurrentSemesterTest() {
val semesters = listOf(
createSemesterEntity(true),
createSemesterEntity(true)
)
doNothing().`when`(apiHelper).initApi(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)
semesterRepository.getSemesters(student).blockingGet()
}
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester
fun createSemesterEntity(current: Boolean): Semester {
return Semester(
studentId = 0,
diaryId = 0,
semesterId = 0,
diaryName = "",
classId = 0,
isCurrent = current,
semesterName = 0,
unitId = 0
)
}

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.register.Student
import io.github.wulkanowy.data.repositories.student.StudentRemote
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.Timetable