1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-11-23 16:36:28 -06:00

Logger refactor (#175)

This commit is contained in:
Mikołaj Pich 2018-11-03 14:49:20 +01:00 committed by Rafał Borcz
parent 7f6f632b73
commit 5e30c8e949
22 changed files with 392 additions and 266 deletions

View File

@ -113,6 +113,7 @@ dependencies {
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.akaita.java:rxjava2-debug:1.3.0'
implementation("com.crashlytics.sdk.android:crashlytics:2.9.5@aar") {
transitive = true
}
@ -125,7 +126,7 @@ dependencies {
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.8.9"
testImplementation "org.mockito:mockito-inline:2.23.0"
testImplementation 'org.threeten:threetenbp:1.3.7'
testImplementation 'org.threeten:threetenbp:1.3.8'
androidTestImplementation 'androidx.test:core:1.0.0'
androidTestImplementation 'androidx.test:runner:1.1.0'

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy
import android.content.Context
import androidx.multidex.MultiDex
import com.akaita.java.rxjava2debug.RxJava2Debug
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.core.CrashlyticsCore
@ -29,22 +30,20 @@ class WulkanowyApp : DaggerApplication() {
AndroidThreeTen.init(this)
initializeFabric()
if (DEBUG) enableDebugLog()
RxJava2Debug.enableRxJava2AssemblyTracking(arrayOf(BuildConfig.APPLICATION_ID))
}
private fun enableDebugLog() {
Timber.plant(DebugLogTree)
Timber.plant(DebugLogTree())
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
}
private fun initializeFabric() {
Fabric.with(Fabric.Builder(this)
.kits(Crashlytics.Builder()
.core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG || !BuildConfig.FABRIC_ENABLED).build())
.build(),
Answers())
.debuggable(BuildConfig.DEBUG)
.build())
Timber.plant(CrashlyticsTree)
Fabric.with(Fabric.Builder(this).kits(
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG || !BuildConfig.FABRIC_ENABLED).build()).build(),
Answers()
).debuggable(BuildConfig.DEBUG).build())
Timber.plant(CrashlyticsTree())
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data
import android.content.res.Resources
import com.akaita.java.rxjava2debug.RxJava2Debug
import io.github.wulkanowy.R
import io.github.wulkanowy.api.login.NotLoggedInException
import timber.log.Timber
@ -14,7 +15,7 @@ open class ErrorHandler @Inject constructor(private val resources: Resources) {
var showErrorMessage: (String) -> Unit = {}
open fun proceed(error: Throwable) {
Timber.e(error, "An exception occurred while the Wulkanowy was running")
Timber.e(RxJava2Debug.getEnhancedStackTrace(error), "An exception occurred while the Wulkanowy was running")
showErrorMessage((when (error) {
is UnknownHostException -> resources.getString(R.string.all_no_internet)

View File

@ -4,6 +4,8 @@ import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Single
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
import javax.inject.Singleton
@ -75,6 +77,7 @@ class SessionRemote @Inject constructor(private val api: Api) {
fun initApi(student: Student, reInitialize: Boolean = false) {
if (if (reInitialize) true else 0 == api.studentId) {
api.run {
logLevel = HttpLoggingInterceptor.Level.NONE
email = student.email
password = student.password
symbol = student.symbol
@ -84,6 +87,9 @@ class SessionRemote @Inject constructor(private val api: Api) {
studentId = student.studentId
loginType = Api.LoginType.valueOf(student.loginType)
notifyDataChanged()
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
Timber.d(it)
}).setLevel(HttpLoggingInterceptor.Level.BASIC))
}
}
}

View File

@ -3,8 +3,10 @@ package io.github.wulkanowy.ui.modules.about
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3
import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import timber.log.Timber
import javax.inject.Inject
class AboutPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter<AboutView>(errorHandler) {
@ -12,9 +14,15 @@ class AboutPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePrese
fun onExtraSelect(type: Libs.SpecialButton?) {
view?.run {
when (type) {
SPECIAL1 -> openSourceWebView()
SPECIAL2 -> openIssuesWebView()
else -> TODO()
SPECIAL1 -> {
Timber.i("Opening github page")
openSourceWebView()
}
SPECIAL2 -> {
Timber.i("Opening issues page")
openIssuesWebView()
}
SPECIAL3 -> { }
}
}
}

View File

@ -6,7 +6,13 @@ import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -14,11 +20,11 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class AttendancePresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val attendanceRepository: AttendanceRepository,
private val sessionRepository: SessionRepository,
private val prefRepository: PreferencesRepository
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val attendanceRepository: AttendanceRepository,
private val sessionRepository: SessionRepository,
private val prefRepository: PreferencesRepository
) : BasePresenter<AttendanceView>(errorHandler) {
lateinit var currentDate: LocalDate
@ -34,11 +40,13 @@ class AttendancePresenter @Inject constructor(
fun onPreviousDay() {
loadData(currentDate.previousSchoolDay)
reloadView()
logEvent("Attendance day changed", mapOf("button" to "prev", "date" to currentDate.toFormattedString()))
}
fun onNextDay() {
loadData(currentDate.nextSchoolDay)
reloadView()
logEvent("Attendance day changed", mapOf("button" to "next", "date" to currentDate.toFormattedString()))
}
fun onSwipeRefresh() {
@ -59,33 +67,34 @@ class AttendancePresenter @Inject constructor(
disposable.apply {
clear()
add(sessionRepository.getSemesters()
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) }
.map { list ->
if (prefRepository.showPresent) list
else list.filter { !it.presence }
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) }
.map { list ->
if (prefRepository.showPresent) list
else list.filter { !it.presence }
}
.map { items -> items.map { AttendanceItem(it) } }
.map { items -> items.sortedBy { it.attendance.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
.map { items -> items.map { AttendanceItem(it) } }
.map { items -> items.sortedBy { it.attendance.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
logEvent("Attendance load", mapOf("items" to it.size, "forceRefresh" to forceRefresh, "date" to currentDate.toFormattedString()))
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
}
)
}
}

View File

@ -6,7 +6,13 @@ import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.friday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -14,10 +20,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class ExamPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val examRepository: ExamRepository,
private val sessionRepository: SessionRepository
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val examRepository: ExamRepository,
private val sessionRepository: SessionRepository
) : BasePresenter<ExamView>(errorHandler) {
lateinit var currentDate: LocalDate
@ -33,11 +39,13 @@ class ExamPresenter @Inject constructor(
fun onPreviousWeek() {
loadData(currentDate.minusDays(7))
reloadView()
logEvent("Exam week changed", mapOf("button" to "prev", "date" to currentDate.toFormattedString()))
}
fun onNextWeek() {
loadData(currentDate.plusDays(7))
reloadView()
logEvent("Exam week changed", mapOf("button" to "next", "date" to currentDate.toFormattedString()))
}
fun onSwipeRefresh() {
@ -58,30 +66,31 @@ class ExamPresenter @Inject constructor(
disposable.apply {
clear()
add(sessionRepository.getSemesters()
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap {
examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh)
}.map { it.groupBy { exam -> exam.date }.toSortedMap() }
.map { createExamItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap {
examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh)
}.map { it.groupBy { exam -> exam.date }.toSortedMap() }
.map { createExamItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
logEvent("Exam load", mapOf("items" to it.size, "forceRefresh" to forceRefresh, "date" to currentDate.toFormattedString()))
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}
}
@ -102,7 +111,7 @@ class ExamPresenter @Inject constructor(
showPreButton(!currentDate.minusDays(7).isHolidays)
showNextButton(!currentDate.plusDays(7).isHolidays)
updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " +
currentDate.friday.toFormattedString("dd.MM"))
currentDate.friday.toFormattedString("dd.MM"))
}
}
}

View File

@ -5,7 +5,9 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logEvent
import io.reactivex.Completable
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
@ -48,6 +50,7 @@ class GradePresenter @Inject constructor(
notifyChildrenSemesterChange()
loadChild(it.currentPageIndex)
}
logEvent("Semester changed", mapOf("number" to index + 1))
}
}

View File

@ -8,15 +8,17 @@ import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.valueColor
import timber.log.Timber
import javax.inject.Inject
class GradeDetailsPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository) : BasePresenter<GradeDetailsView>(errorHandler) {
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository
) : BasePresenter<GradeDetailsView>(errorHandler) {
override fun onAttachView(view: GradeDetailsView) {
super.onAttachView(view)
@ -25,27 +27,28 @@ class GradeDetailsPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
disposable.add(sessionRepository.getSemesters()
.flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) }
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
notifyParentDataLoaded(semesterId)
}
.flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) }
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
notifyParentDataLoaded(semesterId)
}
.subscribe({
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
}
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}
.subscribe({
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
}
logEvent("Grade details load", mapOf("items" to it.size, "forceRefresh" to forceRefresh))
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}
fun onGradeItemSelected(item: AbstractFlexibleItem<*>?) {
@ -92,16 +95,16 @@ class GradeDetailsPresenter @Inject constructor(
return items.map {
it.value.calcAverage().let { average ->
GradeDetailsHeader(
subject = it.key,
average = formatAverage(average),
number = view?.getGradeNumberString(it.value.size).orEmpty(),
newGrades = it.value.filter { grade -> !grade.isRead }.size
subject = it.key,
average = formatAverage(average),
number = view?.getGradeNumberString(it.value.size).orEmpty(),
newGrades = it.value.filter { grade -> !grade.isRead }.size
).apply {
subItems = it.value.map { item ->
GradeDetailsItem(
grade = item,
weightString = view?.weightString.orEmpty(),
valueColor = item.valueColor
grade = item,
weightString = view?.weightString.orEmpty(),
valueColor = item.valueColor
)
}
}
@ -118,9 +121,9 @@ class GradeDetailsPresenter @Inject constructor(
private fun updateGrade(grade: Grade) {
disposable.add(gradeRepository.updateGrade(grade)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({}) { error -> errorHandler.proceed(error) })
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({}) { error -> errorHandler.proceed(error) })
Timber.d("Grade ${grade.id} updated")
}
}

View File

@ -8,17 +8,18 @@ import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.logEvent
import java.lang.String.format
import java.util.Locale.FRANCE
import javax.inject.Inject
class GradeSummaryPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val gradeSummaryRepository: GradeSummaryRepository,
private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository,
private val schedulers: SchedulersProvider)
: BasePresenter<GradeSummaryView>(errorHandler) {
private val errorHandler: ErrorHandler,
private val gradeSummaryRepository: GradeSummaryRepository,
private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository,
private val schedulers: SchedulersProvider
) : BasePresenter<GradeSummaryView>(errorHandler) {
override fun onAttachView(view: GradeSummaryView) {
super.onAttachView(view)
@ -27,43 +28,44 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
disposable.add(sessionRepository.getSemesters()
.map { semester -> semester.first { it.semesterId == semesterId } }
.flatMap {
gradeSummaryRepository.getGradesSummary(it, forceRefresh)
.flatMap { gradesSummary ->
gradeRepository.getGrades(it, forceRefresh)
.map { grades ->
grades.groupBy { grade -> grade.subject }
.mapValues { entry -> entry.value.calcAverage() }
.filterValues { value -> value != 0.0 }
.let { averages ->
createGradeSummaryItems(gradesSummary, averages) to
GradeSummaryScrollableHeader(
formatAverage(gradesSummary.calcAverage()),
formatAverage(averages.values.average())
)
}
}
.map { semester -> semester.first { it.semesterId == semesterId } }
.flatMap {
gradeSummaryRepository.getGradesSummary(it, forceRefresh)
.flatMap { gradesSummary ->
gradeRepository.getGrades(it, forceRefresh)
.map { grades ->
grades.groupBy { grade -> grade.subject }
.mapValues { entry -> entry.value.calcAverage() }
.filterValues { value -> value != 0.0 }
.let { averages ->
createGradeSummaryItems(gradesSummary, averages) to
GradeSummaryScrollableHeader(
formatAverage(gradesSummary.calcAverage()),
formatAverage(averages.values.average())
)
}
}
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
notifyParentDataLoaded(semesterId)
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
notifyParentDataLoaded(semesterId)
}
}.subscribe({
view?.run {
showEmpty(it.first.isEmpty())
showContent(it.first.isNotEmpty())
updateDataSet(it.first, it.second)
}
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}.subscribe({
view?.run {
showEmpty(it.first.isEmpty())
showContent(it.first.isNotEmpty())
updateDataSet(it.first, it.second)
}
logEvent("Grade summary load", mapOf("items" to it.first.size, "forceRefresh" to forceRefresh))
}) {
view?.run { showEmpty(isViewEmpty) }
errorHandler.proceed(it)
})
}
fun onSwipeRefresh() {
@ -88,24 +90,24 @@ class GradeSummaryPresenter @Inject constructor(
}
private fun createGradeSummaryItems(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
: List<GradeSummaryItem> {
: List<GradeSummaryItem> {
return gradesSummary.filter { !checkEmpty(it, averages) }
.flatMap { gradeSummary ->
GradeSummaryHeader(
name = gradeSummary.subject,
average = formatAverage(averages.getOrElse(gradeSummary.subject) { 0.0 }, "")
).let {
listOf(GradeSummaryItem(
header = it,
title = view?.predictedString.orEmpty(),
grade = gradeSummary.predictedGrade
), GradeSummaryItem(
header = it,
title = view?.finalString.orEmpty(),
grade = gradeSummary.finalGrade
))
}
.flatMap { gradeSummary ->
GradeSummaryHeader(
name = gradeSummary.subject,
average = formatAverage(averages.getOrElse(gradeSummary.subject) { 0.0 }, "")
).let {
listOf(GradeSummaryItem(
header = it,
title = view?.predictedString.orEmpty(),
grade = gradeSummary.predictedGrade
), GradeSummaryItem(
header = it,
title = view?.finalString.orEmpty(),
grade = gradeSummary.finalGrade
))
}
}
}
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {

View File

@ -4,13 +4,16 @@ import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.logRegister
import timber.log.Timber
import javax.inject.Inject
class LoginFormPresenter @Inject constructor(
private val schedulers: SchedulersProvider,
private val errorHandler: LoginErrorHandler,
private val sessionRepository: SessionRepository)
: BasePresenter<LoginFormView>(errorHandler) {
private val schedulers: SchedulersProvider,
private val errorHandler: LoginErrorHandler,
private val sessionRepository: SessionRepository
) : BasePresenter<LoginFormView>(errorHandler) {
private var wasEmpty = false
@ -22,33 +25,39 @@ class LoginFormPresenter @Inject constructor(
fun attemptLogin(email: String, password: String, symbol: String, endpoint: String) {
if (!validateCredentials(email, password, symbol)) return
disposable.add(sessionRepository.getConnectedStudents(email, password, symbol, endpoint)
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.doOnSubscribe {
view?.run {
hideSoftKeyboard()
showLoginProgress(true)
errorHandler.doOnBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
}
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.doOnSubscribe {
view?.run {
hideSoftKeyboard()
showLoginProgress(true)
errorHandler.doOnBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
Timber.i("Entered wrong username or password")
}
sessionRepository.clearCache()
}
.doFinally { view?.showLoginProgress(false) }
.subscribe({
view?.run {
if (it.isEmpty() && !wasEmpty) {
showSymbolInput()
wasEmpty = true
} else if (it.isEmpty() && wasEmpty) {
showSymbolInput()
setErrorSymbolIncorrect()
} else {
switchNextView()
}
sessionRepository.clearCache()
}
.doFinally { view?.showLoginProgress(false) }
.subscribe({
view?.run {
if (it.isEmpty() && !wasEmpty) {
showSymbolInput()
wasEmpty = true
} else if (it.isEmpty() && wasEmpty) {
showSymbolInput()
setErrorSymbolIncorrect()
logRegister("No student found", false, if (symbol.isEmpty()) "nil" else symbol, endpoint)
} else {
switchNextView()
logEvent("Found students", mapOf("students" to it.size, "symbol" to it.joinToString { student -> student.symbol }, "endpoint" to endpoint))
}
}, { errorHandler.proceed(it) }))
}
}, {
errorHandler.proceed(it)
logRegister(it.localizedMessage, false, if (symbol.isEmpty()) "nil" else symbol, endpoint)
}))
}
private fun validateCredentials(login: String, password: String, symbol: String): Boolean {

View File

@ -5,13 +5,14 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logRegister
import javax.inject.Inject
class LoginOptionsPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val repository: SessionRepository,
private val schedulers: SchedulersProvider)
: BasePresenter<LoginOptionsView>(errorHandler) {
private val errorHandler: ErrorHandler,
private val repository: SessionRepository,
private val schedulers: SchedulersProvider
) : BasePresenter<LoginOptionsView>(errorHandler) {
override fun onAttachView(view: LoginOptionsView) {
super.onAttachView(view)
@ -20,25 +21,30 @@ class LoginOptionsPresenter @Inject constructor(
fun refreshData() {
disposable.add(repository.cachedStudents
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.doOnSubscribe { view?.showActionBar(true) }
.doFinally { repository.clearCache() }
.subscribe({
view?.updateData(it.map { student ->
LoginOptionsItem(student)
})
}, { errorHandler.proceed(it) }))
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.doOnSubscribe { view?.showActionBar(true) }
.doFinally { repository.clearCache() }
.subscribe({
view?.updateData(it.map { student ->
LoginOptionsItem(student)
})
}, { errorHandler.proceed(it) }))
}
fun onSelectStudent(student: Student) {
disposable.add(repository.saveStudent(student)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe { _ ->
view?.showLoginProgress(true)
view?.showActionBar(false)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
showLoginProgress(true)
showActionBar(false)
}
.subscribe({ view?.openMainView() }, { errorHandler.proceed(it) }))
}
.subscribe({
logRegister("Success", true, student.symbol, student.endpoint)
view?.openMainView()
}, { errorHandler.proceed(it) }))
}
}

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.logLogin
import io.github.wulkanowy.utils.safelyPopFragment
import io.github.wulkanowy.utils.setOnViewChangeListener
import kotlinx.android.synthetic.main.activity_main.*

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.job.ServiceHelper
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.logLogin
import javax.inject.Inject
class MainPresenter @Inject constructor(
@ -21,6 +22,11 @@ class MainPresenter @Inject constructor(
initView()
}
when (initMenuIndex) {
1 -> logLogin("Grades")
3 -> logLogin("Timetable")
}
serviceHelper.startFullSyncService()
}

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.job.ServiceHelper
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.logEvent
import org.threeten.bp.LocalDate.now
import javax.inject.Inject
@ -36,5 +37,7 @@ class SettingsPresenter @Inject constructor(
view?.setTheme(preferencesRepository.currentTheme)
}
}
logEvent("Setting changed", mapOf("name" to key))
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.logLogin
import javax.inject.Inject
class SplashPresenter @Inject constructor(
@ -16,7 +17,10 @@ class SplashPresenter @Inject constructor(
super.onAttachView(view)
view.run {
setCurrentThemeMode(preferencesRepository.currentTheme)
if (sessionRepository.isSessionSaved) openMainView() else openLoginView()
if (sessionRepository.isSessionSaved) {
logLogin("Open app")
openMainView()
} else openLoginView()
}
}
}

View File

@ -5,7 +5,13 @@ import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -13,10 +19,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class TimetablePresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val timetableRepository: TimetableRepository,
private val sessionRepository: SessionRepository
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val timetableRepository: TimetableRepository,
private val sessionRepository: SessionRepository
) : BasePresenter<TimetableView>(errorHandler) {
lateinit var currentDate: LocalDate
@ -32,11 +38,13 @@ class TimetablePresenter @Inject constructor(
fun onPreviousDay() {
loadData(currentDate.previousSchoolDay)
reloadView()
logEvent("Timetable day changed", mapOf("button" to "prev", "date" to currentDate.toFormattedString()))
}
fun onNextDay() {
loadData(currentDate.nextSchoolDay)
reloadView()
logEvent("Timetable day changed", mapOf("button" to "next", "date" to currentDate.toFormattedString()))
}
fun onSwipeRefresh() {
@ -57,29 +65,30 @@ class TimetablePresenter @Inject constructor(
disposable.apply {
clear()
add(sessionRepository.getSemesters()
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
.map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } }
.map { items -> items.sortedBy { it.lesson.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
.delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
.map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } }
.map { items -> items.sortedBy { it.lesson.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
}) {
view?.run { showEmpty(isViewEmpty()) }
errorHandler.proceed(it)
})
}
.subscribe({
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
logEvent("Timetable load", mapOf("items" to it.size, "forceRefresh" to forceRefresh, "date" to currentDate.toFormattedString()))
}) {
view?.run { showEmpty(isViewEmpty()) }
errorHandler.proceed(it)
})
}
}

View File

@ -15,6 +15,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import io.github.wulkanowy.utils.logEvent
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
@ -78,16 +79,19 @@ class TimetableWidgetProvider : AppWidgetProvider() {
AndroidInjection.inject(this, context)
intent?.let {
val widgetKey = "timetable_widget_${it.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)}"
when (it.getStringExtra(EXTRA_BUTTON_TYPE)) {
BUTTON_NEXT -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).nextSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
it.getStringExtra(EXTRA_BUTTON_TYPE).let { button ->
when (button) {
BUTTON_NEXT -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).nextSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
}
BUTTON_PREV -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).previousSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
}
BUTTON_RESET -> sharedPref.putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true)
}
BUTTON_PREV -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).previousSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
}
BUTTON_RESET -> sharedPref.putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true)
button?.also { btn -> if (btn.isNotBlank()) logEvent("Widget day changed", mapOf("button" to button)) }
}
}
super.onReceive(context, intent)

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.utils
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.answers.CustomEvent
import com.crashlytics.android.answers.LoginEvent
import com.crashlytics.android.answers.SignUpEvent
import timber.log.Timber
fun logLogin(method: String) {
try {
Answers.getInstance().logLogin(LoginEvent().putMethod(method))
} catch (e: Throwable) {
Timber.d(e)
}
}
fun logRegister(message: String, result: Boolean, symbol: String, endpoint: String) {
try {
Answers.getInstance().logSignUp(SignUpEvent()
.putMethod("Login activity")
.putSuccess(result)
.putCustomAttribute("symbol", symbol)
.putCustomAttribute("message", message)
.putCustomAttribute("endpoint", endpoint)
)
} catch (e: Throwable) {
Timber.d(e)
}
}
fun <T> logEvent(name: String, params: Map<String, T>) {
try {
Answers.getInstance().logCustom(CustomEvent(name)
.apply {
params.forEach {
when {
it.value is String -> putCustomAttribute(it.key, it.value as String)
it.value is Number -> putCustomAttribute(it.key, it.value as Number)
it.value is Boolean -> putCustomAttribute(it.key, if ((it.value as Boolean)) "true" else "false")
else -> Timber.w("logEvent() unknown value type: ${it.value}")
}
}
}
)
} catch (e: Throwable) {
Timber.d(e)
}
}

View File

@ -3,25 +3,20 @@ package io.github.wulkanowy.utils
import com.crashlytics.android.Crashlytics
import timber.log.Timber
object CrashlyticsTree : Timber.Tree() {
class DebugLogTree : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
super.log(priority, "Wulkanowy", message, t)
}
}
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
Crashlytics.setInt("priority", priority)
Crashlytics.setString("tag", tag)
if (t == null) {
Crashlytics.log(message)
} else {
Crashlytics.setString("message", message)
Crashlytics.logException(t)
}
if (t == null) Crashlytics.log(message)
else Crashlytics.logException(t)
}
}
object DebugLogTree : Timber.DebugTree() {
override fun createStackElementTag(element: StackTraceElement): String? {
return super.createStackElementTag(element) + " - ${element.lineNumber}"
}
}

View File

@ -11,4 +11,4 @@ open class SchedulersProvider {
open val backgroundThread: Scheduler
get() = Schedulers.io()
}
}

View File

@ -123,7 +123,7 @@ class LoginFormPresenterTest {
@Test
fun loginErrorTest() {
val testException = RuntimeException()
val testException = RuntimeException("test")
doReturn(Single.error<List<Student>>(testException))
.`when`(repository).getConnectedStudents(anyString(), anyString(), anyString(), anyString())
presenter.attemptLogin("@", "123456", "test", "https://fakelog.cf")