Merge branch 'release/0.12.0'

This commit is contained in:
Mikołaj Pich 2019-10-29 00:25:12 +01:00
commit 87a133beb9
77 changed files with 3075 additions and 142 deletions

View File

@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.11.0 - 0.12.0
android: android:
licenses: licenses:

View File

@ -17,8 +17,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 46 versionCode 47
versionName "0.11.0" versionName "0.12.0"
multiDexEnabled true multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -110,9 +110,9 @@ play {
} }
ext { ext {
work_manager = "2.2.0" work_manager = "2.3.0-alpha03"
room = "2.2.0-rc01" room = "2.2.1"
dagger = "2.24" dagger = "2.25.2"
chucker = "2.0.4" chucker = "2.0.4"
mockk = "1.9.2" mockk = "1.9.2"
} }
@ -123,23 +123,23 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:api:0.11.0" implementation "io.github.wulkanowy:api:0.12.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.1.0" implementation "androidx.core:core-ktx:1.2.0-beta01"
implementation "androidx.activity:activity-ktx:1.0.0" implementation "androidx.activity:activity-ktx:1.1.0-rc01"
implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.appcompat:appcompat-resources:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.1.0" implementation "androidx.fragment:fragment-ktx:1.2.0-rc01"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.preference:preference-ktx:1.1.0" implementation "androidx.preference:preference-ktx:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0-beta04" implementation "androidx.recyclerview:recyclerview:1.1.0-rc01"
implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0-beta01" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0-rc01"
implementation "com.google.android.material:material:1.1.0-alpha07" implementation "com.google.android.material:material:1.1.0-alpha07"
implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.wulkanowy:material-chips-input:2.0.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
@ -174,9 +174,9 @@ dependencies {
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.6" implementation "com.squareup.okhttp3:logging-interceptor:3.12.6"
implementation "com.mikepenz:aboutlibraries:7.0.3" implementation "com.mikepenz:aboutlibraries:7.0.4"
playImplementation "com.google.firebase:firebase-core:17.2.0" playImplementation "com.google.firebase:firebase-core:17.2.1"
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
releaseImplementation "fr.o80.chucker:library-no-op:$chucker" releaseImplementation "fr.o80.chucker:library-no-op:$chucker"

File diff suppressed because it is too large Load Diff

View File

@ -144,4 +144,8 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideTeacherDao(database: AppDatabase) = database.teacherDao fun provideTeacherDao(database: AppDatabase) = database.teacherDao
@Singleton
@Provides
fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao
} }

View File

@ -22,6 +22,7 @@ import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.dao.NoteDao import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.dao.RecipientDao import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.dao.ReportingUnitDao import io.github.wulkanowy.data.db.dao.ReportingUnitDao
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.dao.SubjectDao import io.github.wulkanowy.data.db.dao.SubjectDao
@ -42,6 +43,7 @@ import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
@ -55,6 +57,7 @@ import io.github.wulkanowy.data.db.migrations.Migration14
import io.github.wulkanowy.data.db.migrations.Migration15 import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16 import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17 import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
@ -87,7 +90,8 @@ import javax.inject.Singleton
ReportingUnit::class, ReportingUnit::class,
Recipient::class, Recipient::class,
MobileDevice::class, MobileDevice::class,
Teacher::class Teacher::class,
School::class
], ],
version = AppDatabase.VERSION_SCHEMA, version = AppDatabase.VERSION_SCHEMA,
exportSchema = true exportSchema = true
@ -96,7 +100,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 17 const val VERSION_SCHEMA = 18
fun getMigrations(): Array<Migration> { fun getMigrations(): Array<Migration> {
return arrayOf( return arrayOf(
@ -115,7 +119,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration14(), Migration14(),
Migration15(), Migration15(),
Migration16(), Migration16(),
Migration17() Migration17(),
Migration18()
) )
} }
@ -168,4 +173,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val mobileDeviceDao: MobileDeviceDao abstract val mobileDeviceDao: MobileDeviceDao
abstract val teacherDao: TeacherDao abstract val teacherDao: TeacherDao
abstract val schoolDao: SchoolDao
} }

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface SchoolDao {
@Insert
fun insert(school: School)
@Delete
fun delete(school: School)
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun load(studentId: Int, classId: Int): Maybe<School>
}

View File

@ -13,10 +13,10 @@ import javax.inject.Singleton
interface TeacherDao { interface TeacherDao {
@Insert @Insert
fun insertAll(devices: List<Teacher>) fun insertAll(teachers: List<Teacher>)
@Delete @Delete
fun deleteAll(devices: List<Teacher>) fun deleteAll(teachers: List<Teacher>)
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Teacher>> fun loadAll(studentId: Int, classId: Int): Maybe<List<Teacher>>

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity(tableName = "School")
data class School(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "class_id")
val classId: Int,
val name: String,
val address: String,
val contact: String,
val headmaster: String,
val pedagogue: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration18 : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS School (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
class_id INTEGER NOT NULL,
name TEXT NOT NULL,
address TEXT NOT NULL,
contact TEXT NOT NULL,
headmaster TEXT NOT NULL,
pedagogue TEXT NOT NULL
)
""")
}
}

View File

@ -12,52 +12,60 @@ class PreferencesRepository @Inject constructor(
val context: Context val context: Context
) { ) {
val startMenuIndex: Int val startMenuIndex: Int
get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0 get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
val isShowPresent: Boolean val isShowPresent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present)
val gradeAverageMode: String val gradeAverageMode: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester" get() = getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode)
val gradeAverageForceCalc: Boolean val gradeAverageForceCalc: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_grade_average_force_calc), false) get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
val isGradeExpandable: Boolean val isGradeExpandable: Boolean
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
val appThemeKey: String = context.getString(R.string.pref_key_app_theme) val appThemeKey = context.getString(R.string.pref_key_app_theme)
val appTheme: String val appTheme: String
get() = sharedPref.getString(appThemeKey, "light") ?: "light" get() = getString(appThemeKey, R.string.pref_default_app_theme)
val gradeColorTheme: String val gradeColorTheme: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan" get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme)
val serviceEnableKey: String = context.getString(R.string.pref_key_services_enable) val serviceEnableKey = context.getString(R.string.pref_key_services_enable)
val isServiceEnabled: Boolean val isServiceEnabled: Boolean
get() = sharedPref.getBoolean(serviceEnableKey, true) get() = getBoolean(serviceEnableKey, R.bool.pref_default_services_enable)
val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval) val servicesIntervalKey = context.getString(R.string.pref_key_services_interval)
val servicesInterval: Long val servicesInterval: Long
get() = sharedPref.getString(servicesIntervalKey, "60")?.toLongOrNull() ?: 60 get() = getString(servicesIntervalKey, R.string.pref_default_services_interval).toLong()
val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only) val servicesOnlyWifiKey = context.getString(R.string.pref_key_services_wifi_only)
val isServicesOnlyWifi: Boolean val isServicesOnlyWifi: Boolean
get() = sharedPref.getBoolean(servicesOnlyWifiKey, false) get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only)
val isNotificationsEnable: Boolean val isNotificationsEnable: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true) get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean val isDebugNotificationEnable: Boolean
get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false) get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
val gradePlusModifier: Double val gradePlusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 get() = getString(R.string.pref_key_grade_modifier_plus, R.string.pref_default_grade_modifier_plus).toDouble()
val gradeMinusModifier: Double val gradeMinusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0 get() = getString(R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus).toDouble()
val fillMessageContent: Boolean val fillMessageContent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default)
private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default))
} }

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
fun saveSchool(school: School) {
schoolDb.insert(school)
}
fun deleteSchool(school: School) {
schoolDb.delete(school)
}
fun getSchool(semester: Semester): Maybe<School> {
return schoolDb.load(semester.studentId, semester.classId)
}
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
import javax.inject.Inject
class SchoolRemote @Inject constructor(private val api: Api) {
fun getSchoolInfo(semester: Semester): Single<School> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getSchool() }
.map {
School(
studentId = semester.studentId,
classId = semester.classId,
name = it.name,
address = it.address,
contact = it.contact,
headmaster = it.headmaster,
pedagogue = it.pedagogue
)
}
}
}

View File

@ -0,0 +1,41 @@
package io.github.wulkanowy.data.repositories.school
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.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SchoolRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: SchoolLocal,
private val remote: SchoolRemote
) {
fun getSchoolInfo(semester: Semester, forceRefresh: Boolean = false): Maybe<School> {
return local.getSchool(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSchoolInfo(semester)
else Single.error(UnknownHostException())
}.flatMapMaybe { new ->
local.getSchool(semester)
.doOnSuccess { old ->
if (new != old) {
local.deleteSchool(old)
local.saveSchool(new)
}
}
.doOnComplete {
local.saveSchool(new)
}
}.flatMap({ local.getSchool(semester) }, { Maybe.error(it) },
{ local.getSchool(semester) })
)
}
}

View File

@ -6,7 +6,8 @@ import io.github.wulkanowy.data.db.entities.Teacher
import io.reactivex.Maybe import io.reactivex.Maybe
import javax.inject.Inject import javax.inject.Inject
class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) { class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
fun saveTeachers(teachers: List<Teacher>) { fun saveTeachers(teachers: List<Teacher>) {
teacherDb.insertAll(teachers) teacherDb.insertAll(teachers)
} }

View File

@ -16,6 +16,7 @@ class TeacherRepository @Inject constructor(
private val local: TeacherLocal, private val local: TeacherLocal,
private val remote: TeacherRemote private val remote: TeacherRemote
) { ) {
fun getTeachers(semester: Semester, forceRefresh: Boolean = false): Single<List<Teacher>> { fun getTeachers(semester: Semester, forceRefresh: Boolean = false): Single<List<Teacher>> {
return local.getTeachers(semester).filter { !forceRefresh } return local.getTeachers(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)

View File

@ -29,17 +29,15 @@ import javax.inject.Singleton
@Suppress("unused") @Suppress("unused")
@AssistedModule @AssistedModule
@Module(includes = [AssistedInject_ServicesModule::class]) @Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class])
abstract class ServicesModule { abstract class ServicesModule {
@Module @Module
companion object { object Static {
@JvmStatic
@Provides @Provides
fun provideWorkManager(context: Context) = WorkManager.getInstance(context) fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
@JvmStatic
@Singleton @Singleton
@Provides @Provides
fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context) fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context)

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.base
import android.content.pm.PackageManager.GET_ACTIVITIES import android.content.pm.PackageManager.GET_ACTIVITIES
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import io.github.wulkanowy.R import io.github.wulkanowy.R
@ -22,8 +23,12 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
fun applyDefaultTheme() { fun applyDefaultTheme() {
AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.setDefaultNightMode(
if (preferencesRepository.appTheme == "light") MODE_NIGHT_NO when (val theme = preferencesRepository.appTheme) {
else MODE_NIGHT_YES "light" -> MODE_NIGHT_NO
"dark", "black" -> MODE_NIGHT_YES
"system" -> MODE_NIGHT_FOLLOW_SYSTEM
else -> throw IllegalArgumentException("Wrong theme: $theme")
}
) )
} }

View File

@ -11,13 +11,12 @@ import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
@Suppress("unused") @Suppress("unused")
@Module @Module(includes = [GradeModule.Static::class])
abstract class GradeModule { abstract class GradeModule {
@Module @Module
companion object { object Static {
@JvmStatic
@PerFragment @PerFragment
@Provides @Provides
fun provideGradeAdapter(fragment: GradeFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) fun provideGradeAdapter(fragment: GradeFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager)

View File

@ -11,13 +11,12 @@ import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFrag
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@Suppress("unused") @Suppress("unused")
@Module @Module(includes = [LoginModule.Static::class])
internal abstract class LoginModule { internal abstract class LoginModule {
@Module @Module
companion object { object Static {
@JvmStatic
@PerActivity @PerActivity
@Provides @Provides
fun provideLoginAdapter(activity: LoginActivity) = BaseFragmentPagerAdapter(activity.supportFragmentManager) fun provideLoginAdapter(activity: LoginActivity) = BaseFragmentPagerAdapter(activity.supportFragmentManager)

View File

@ -9,7 +9,6 @@ import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.view.inputmethod.EditorInfo.IME_NULL import android.view.inputmethod.EditorInfo.IME_NULL
import android.widget.ArrayAdapter
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
@ -17,6 +16,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openEmail
import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.showSoftInput import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.fragment_login_form.* import kotlinx.android.synthetic.main.fragment_login_form.*
@ -54,26 +54,24 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
} }
override fun initView() { override fun initView() {
hostKeys = resources.getStringArray(R.array.endpoints_keys) hostKeys = resources.getStringArray(R.array.hosts_keys)
hostValues = resources.getStringArray(R.array.endpoints_values) hostValues = resources.getStringArray(R.array.hosts_values)
loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() }
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
loginFormContactDiscord.setOnClickListener { presenter.onDiscordClick() }
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
loginFormPass.setOnEditorActionListener { _, id, _ -> loginFormPass.setOnEditorActionListener { _, id, _ ->
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
} }
with(loginFormHost) { with(loginFormHost) {
//Bug with filter in ExposedDropdownMenu on restoring state
isSaveEnabled = false
setText(hostKeys.getOrElse(0) { "" }) setText(hostKeys.getOrElse(0) { "" })
setAdapter(ArrayAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)) setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
keyListener = null
} }
} }
@ -154,8 +152,25 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
} }
override fun showContact(show: Boolean) {
loginFormContact.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
presenter.onDetachView() presenter.onDetachView()
} }
override fun openDiscordInvite() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
override fun openEmail() {
context?.openEmail(
requireContext().getString(R.string.login_email_intent_title),
"wulkanowyinc@gmail.com",
requireContext().getString(R.string.login_email_subject),
requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName)
)
}
} }

View File

@ -22,6 +22,7 @@ class LoginFormPresenter @Inject constructor(
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
initView() initView()
showContact(false)
if (appInfo.isDebug) showVersion() else showPrivacyPolicy() if (appInfo.isDebug) showVersion() else showPrivacyPolicy()
loginErrorHandler.onBadCredentials = { loginErrorHandler.onBadCredentials = {
@ -86,9 +87,18 @@ class LoginFormPresenter @Inject constructor(
Timber.i("Login result: An exception occurred") Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.message.ifNullOrBlank { "No message" }) analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it) loginErrorHandler.dispatch(it)
view?.showContact(true)
})) }))
} }
fun onDiscordClick() {
view?.openDiscordInvite()
}
fun onEmailClick() {
view?.openEmail()
}
private fun validateCredentials(login: String, password: String): Boolean { private fun validateCredentials(login: String, password: String): Boolean {
var isCorrect = true var isCorrect = true

View File

@ -42,4 +42,10 @@ interface LoginFormView : BaseView {
fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>)
fun openPrivacyPolicyPage() fun openPrivacyPolicyPage()
fun showContact(show: Boolean)
fun openDiscordInvite()
fun openEmail()
} }

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.ui.modules.login.form
import android.content.Context
import android.widget.ArrayAdapter
import android.widget.Filter
class LoginSymbolAdapter(context: Context, resource: Int, objects: Array<out String>) :
ArrayAdapter<String>(context, resource, objects) {
override fun getFilter() = object : Filter() {
override fun performFiltering(constraint: CharSequence?) = null
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {}
}
}

View File

@ -13,6 +13,9 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.openEmail
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemClickListener import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_login_student_select.* import kotlinx.android.synthetic.main.fragment_login_student_select.*
import java.io.Serializable import java.io.Serializable
@ -26,6 +29,9 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
@Inject @Inject
lateinit var loginAdapter: FlexibleAdapter<AbstractFlexibleItem<*>> lateinit var loginAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject
lateinit var appInfo: AppInfo
companion object { companion object {
const val SAVED_STUDENTS = "STUDENTS" const val SAVED_STUDENTS = "STUDENTS"
@ -44,6 +50,8 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
override fun initView() { override fun initView() {
loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() }
loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() }
loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() }
loginStudentSelectRecycler.apply { loginStudentSelectRecycler.apply {
adapter = loginAdapter adapter = loginAdapter
@ -80,8 +88,25 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView {
outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable) outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable)
} }
override fun showContact(show: Boolean) {
loginStudentSelectContact.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() { override fun onDestroyView() {
presenter.onDetachView() presenter.onDetachView()
super.onDestroyView() super.onDestroyView()
} }
override fun openDiscordInvite() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
override fun openEmail() {
context?.openEmail(
requireContext().getString(R.string.login_email_intent_title),
"wulkanowyinc@gmail.com",
requireContext().getString(R.string.login_email_subject),
requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName)
)
}
} }

View File

@ -27,6 +27,7 @@ class LoginStudentSelectPresenter @Inject constructor(
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
initView() initView()
showContact(false)
enableSignIn(false) enableSignIn(false)
loginErrorHandler.onStudentDuplicate = { loginErrorHandler.onStudentDuplicate = {
showMessage(it) showMessage(it)
@ -88,7 +89,16 @@ class LoginStudentSelectPresenter @Inject constructor(
view?.apply { view?.apply {
showProgress(false) showProgress(false)
showContent(true) showContent(true)
showContact(true)
} }
})) }))
} }
fun onDiscordClick() {
view?.openDiscordInvite()
}
fun onEmailClick() {
view?.openEmail()
}
} }

View File

@ -15,4 +15,10 @@ interface LoginStudentSelectView : BaseView {
fun showContent(show: Boolean) fun showContent(show: Boolean)
fun enableSignIn(enable: Boolean) fun enableSignIn(enable: Boolean)
fun showContact(show: Boolean)
fun openDiscordInvite()
fun openEmail()
} }

View File

@ -14,7 +14,10 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openEmail
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.showSoftInput import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.fragment_login_symbol.* import kotlinx.android.synthetic.main.fragment_login_symbol.*
import javax.inject.Inject import javax.inject.Inject
@ -24,6 +27,9 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView {
@Inject @Inject
lateinit var presenter: LoginSymbolPresenter lateinit var presenter: LoginSymbolPresenter
@Inject
lateinit var appInfo: AppInfo
companion object { companion object {
private const val SAVED_LOGIN_DATA = "LOGIN_DATA" private const val SAVED_LOGIN_DATA = "LOGIN_DATA"
@ -44,6 +50,8 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView {
override fun initView() { override fun initView() {
loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) }
loginSymbolContactDiscord.setOnClickListener { presenter.onDiscordClick() }
loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() }
loginSymbolName.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } loginSymbolName.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
@ -109,8 +117,25 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView {
outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData) outState.putSerializable(SAVED_LOGIN_DATA, presenter.loginData)
} }
override fun showContact(show: Boolean) {
loginSymbolContact.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
presenter.onDetachView() presenter.onDetachView()
} }
override fun openDiscordInvite() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
override fun openEmail() {
context?.openEmail(
requireContext().getString(R.string.login_email_intent_title),
"wulkanowyinc@gmail.com",
requireContext().getString(R.string.login_email_subject),
requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName)
)
}
} }

View File

@ -23,7 +23,10 @@ class LoginSymbolPresenter @Inject constructor(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) { fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) {
super.onAttachView(view) super.onAttachView(view)
view.initView() view.run {
initView()
showContact(false)
}
if (savedLoginData is Triple<*, *, *>) { if (savedLoginData is Triple<*, *, *>) {
loginData = savedLoginData as Triple<String, String, String> loginData = savedLoginData as Triple<String, String, String>
} }
@ -64,6 +67,7 @@ class LoginSymbolPresenter @Inject constructor(
if (it.isEmpty()) { if (it.isEmpty()) {
Timber.i("Login with symbol result: Empty student list") Timber.i("Login with symbol result: Empty student list")
setErrorSymbolIncorrect() setErrorSymbolIncorrect()
view?.showContact(true)
} else { } else {
Timber.i("Login with symbol result: Success") Timber.i("Login with symbol result: Success")
notifyParentAccountLogged(it) notifyParentAccountLogged(it)
@ -73,6 +77,7 @@ class LoginSymbolPresenter @Inject constructor(
Timber.i("Login with symbol result: An exception occurred") Timber.i("Login with symbol result: An exception occurred")
analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" }) analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it) loginErrorHandler.dispatch(it)
view?.showContact(true)
})) }))
} }
@ -83,4 +88,12 @@ class LoginSymbolPresenter @Inject constructor(
showSoftKeyboard() showSoftKeyboard()
} }
} }
fun onDiscordClick() {
view?.openDiscordInvite()
}
fun onEmailClick() {
view?.openEmail()
}
} }

View File

@ -26,4 +26,10 @@ interface LoginSymbolView : BaseView {
fun showContent(show: Boolean) fun showContent(show: Boolean)
fun notifyParentAccountLogged(students: List<Student>) fun notifyParentAccountLogged(students: List<Student>)
fun showContact(show: Boolean)
fun openDiscordInvite()
fun openEmail()
} }

View File

@ -26,26 +26,25 @@ import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceModule
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersModule
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.teacher.TeacherFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
@Suppress("unused") @Suppress("unused")
@Module @Module(includes = [MainModule.Static::class])
abstract class MainModule { abstract class MainModule {
@Module @Module
companion object { object Static {
@JvmStatic
@Provides @Provides
fun provideFragNavController(activity: MainActivity): FragNavController { fun provideFragNavController(activity: MainActivity): FragNavController {
return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer)
} }
//In activities must be injected as Lazy //In activities must be injected as Lazy
@JvmStatic
@Provides @Provides
fun provideElevationOverlayProvider(activity: MainActivity) = ElevationOverlayProvider(activity) fun provideElevationOverlayProvider(activity: MainActivity) = ElevationOverlayProvider(activity)
} }
@ -123,6 +122,6 @@ abstract class MainModule {
abstract fun bindLicenseFragment(): LicenseFragment abstract fun bindLicenseFragment(): LicenseFragment
@PerFragment @PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector(modules = [SchoolAndTeachersModule::class])
abstract fun bindTeacherFragment(): TeacherFragment abstract fun bindSchoolAndTeachersFragment(): SchoolAndTeachersFragment
} }

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE
import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE
import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
@ -38,7 +39,7 @@ class MainPresenter @Inject constructor(
fun onViewChange(section: MainView.Section?) { fun onViewChange(section: MainView.Section?) {
view?.apply { view?.apply {
showActionBarElevation(section != GRADE && section != MESSAGE) showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL)
currentViewTitle?.let { setViewTitle(it) } currentViewTitle?.let { setViewTitle(it) }
currentStackSize?.let { currentStackSize?.let {
if (it > 1) showHomeArrow(true) if (it > 1) showHomeArrow(true)

View File

@ -51,6 +51,7 @@ interface MainView : BaseView {
NOTE(7), NOTE(7),
LUCKY_NUMBER(8), LUCKY_NUMBER(8),
SETTINGS(9), SETTINGS(9),
ABOUT(10) ABOUT(10),
SCHOOL(11)
} }
} }

View File

@ -9,13 +9,12 @@ import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment
@Suppress("unused") @Suppress("unused")
@Module @Module(includes = [MessageModule.Static::class])
abstract class MessageModule { abstract class MessageModule {
@Module @Module
companion object { object Static {
@JvmStatic
@PerFragment @PerFragment
@Provides @Provides
fun provideMessageAdapter(fragment: MessageFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager) fun provideMessageAdapter(fragment: MessageFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager)

View File

@ -143,7 +143,7 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
} }
override fun notifyParentMessageDeleted(message: Message) { override fun notifyParentMessageDeleted(message: Message) {
fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) } parentFragmentManager.fragments.forEach { if (it is MessageFragment) it.onDeleteMessage(message) }
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {

View File

@ -18,8 +18,9 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.teacher.TeacherFragment import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment
import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.setOnItemClickListener import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_more.* import kotlinx.android.synthetic.main.fragment_more.*
@ -55,8 +56,8 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
override val mobileDevicesRes: Pair<String, Drawable?>? override val mobileDevicesRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) }
override val teachersRes: Pair<String, Drawable?>? override val schoolAndTeachersRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.teachers_title) to getCompatDrawable((R.drawable.ic_more_teacher)) } get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
override val settingsRes: Pair<String, Drawable?>? override val settingsRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) } get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) }
@ -110,8 +111,8 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
(activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
} }
override fun openTeachersView() { override fun openSchoolAndTeachersView() {
(activity as? MainActivity)?.pushView(TeacherFragment.newInstance()) (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance())
} }
override fun openSettingsView() { override fun openSettingsView() {

View File

@ -31,7 +31,7 @@ class MorePresenter @Inject constructor(
noteRes?.first -> openNoteView() noteRes?.first -> openNoteView()
luckyNumberRes?.first -> openLuckyNumberView() luckyNumberRes?.first -> openLuckyNumberView()
mobileDevicesRes?.first -> openMobileDevicesView() mobileDevicesRes?.first -> openMobileDevicesView()
teachersRes?.first -> openTeachersView() schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
settingsRes?.first -> openSettingsView() settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView() aboutRes?.first -> openAboutView()
} }
@ -52,7 +52,7 @@ class MorePresenter @Inject constructor(
noteRes?.let { MoreItem(it.first, it.second) }, noteRes?.let { MoreItem(it.first, it.second) },
luckyNumberRes?.let { MoreItem(it.first, it.second) }, luckyNumberRes?.let { MoreItem(it.first, it.second) },
mobileDevicesRes?.let { MoreItem(it.first, it.second) }, mobileDevicesRes?.let { MoreItem(it.first, it.second) },
teachersRes?.let { MoreItem(it.first, it.second) }, schoolAndTeachersRes?.let { MoreItem(it.first, it.second) },
settingsRes?.let { MoreItem(it.first, it.second) }, settingsRes?.let { MoreItem(it.first, it.second) },
aboutRes?.let { MoreItem(it.first, it.second) }) aboutRes?.let { MoreItem(it.first, it.second) })
) )

View File

@ -15,7 +15,7 @@ interface MoreView : BaseView {
val mobileDevicesRes: Pair<String, Drawable?>? val mobileDevicesRes: Pair<String, Drawable?>?
val teachersRes: Pair<String, Drawable?>? val schoolAndTeachersRes: Pair<String, Drawable?>?
val settingsRes: Pair<String, Drawable?>? val settingsRes: Pair<String, Drawable?>?
@ -41,5 +41,5 @@ interface MoreView : BaseView {
fun openMobileDevicesView() fun openMobileDevicesView()
fun openTeachersView() fun openSchoolAndTeachersView()
} }

View File

@ -0,0 +1,8 @@
package io.github.wulkanowy.ui.modules.schoolandteachers
interface SchoolAndTeachersChildView {
fun notifyParentDataLoaded()
fun onParentLoadData(forceRefresh: Boolean)
}

View File

@ -0,0 +1,87 @@
package io.github.wulkanowy.ui.modules.schoolandteachers
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.setOnSelectPageListener
import kotlinx.android.synthetic.main.fragment_schoolandteachers.*
import javax.inject.Inject
class SchoolAndTeachersFragment : BaseFragment(), SchoolAndTeachersView, MainView.TitledView {
@Inject
lateinit var presenter: SchoolAndTeachersPresenter
@Inject
lateinit var pagerAdapter: BaseFragmentPagerAdapter
companion object {
fun newInstance() = SchoolAndTeachersFragment()
}
override val titleStringId: Int get() = R.string.schoolandteachers_title
override val currentPageIndex get() = schoolandteachersViewPager.currentItem
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_schoolandteachers, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
with(pagerAdapter) {
containerId = schoolandteachersViewPager.id
addFragmentsWithTitle(mapOf(
SchoolFragment.newInstance() to getString(R.string.school_title),
TeacherFragment.newInstance() to getString(R.string.teachers_title)
))
}
with(schoolandteachersViewPager) {
adapter = pagerAdapter
offscreenPageLimit = 2
setOnSelectPageListener(presenter::onPageSelected)
}
with(schoolandteachersTabLayout) {
setupWithViewPager(schoolandteachersViewPager)
setElevationCompat(context.dpToPx(4f))
}
}
override fun showContent(show: Boolean) {
schoolandteachersViewPager.visibility = if (show) VISIBLE else INVISIBLE
schoolandteachersTabLayout.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showProgress(show: Boolean) {
schoolandteachersProgress.visibility = if (show) VISIBLE else INVISIBLE
}
fun onChildFragmentLoaded() {
presenter.onChildViewLoaded()
}
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
(pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView)?.onParentLoadData(forceRefresh)
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.modules.schoolandteachers
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerChildFragment
import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment
@Suppress("unused")
@Module(includes = [SchoolAndTeachersModule.Static::class])
abstract class SchoolAndTeachersModule {
@Module
object Static {
@PerFragment
@Provides
fun provideSchoolAndTeachersAdapter(fragment: SchoolAndTeachersFragment) = BaseFragmentPagerAdapter(fragment.childFragmentManager)
}
@PerChildFragment
@ContributesAndroidInjector
abstract fun provideSchoolFragment(): SchoolFragment
@PerChildFragment
@ContributesAndroidInjector
abstract fun provideTeacherFragment(): TeacherFragment
}

View File

@ -0,0 +1,47 @@
package io.github.wulkanowy.ui.modules.schoolandteachers
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class SchoolAndTeachersPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<SchoolAndTeachersView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: SchoolAndTeachersView) {
super.onAttachView(view)
disposable.add(Completable.timer(150, TimeUnit.MILLISECONDS, schedulers.mainThread)
.subscribe {
view.initView()
Timber.i("Message view was initialized")
loadData()
})
}
fun onPageSelected(index: Int) {
loadChild(index)
}
private fun loadData() {
view?.run { loadChild(currentPageIndex) }
}
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
Timber.i("Load schoolandteachers child view index: $index")
view?.notifyChildLoadData(index, forceRefresh)
}
fun onChildViewLoaded() {
view?.apply {
showContent(true)
showProgress(false)
}
}
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.ui.modules.schoolandteachers
import io.github.wulkanowy.ui.base.BaseView
interface SchoolAndTeachersView : BaseView {
val currentPageIndex: Int
fun initView()
fun showContent(show: Boolean)
fun showProgress(show: Boolean)
fun notifyChildLoadData(index: Int, forceRefresh: Boolean)
}

View File

@ -0,0 +1,95 @@
package io.github.wulkanowy.ui.modules.schoolandteachers.school
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.School
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.utils.dialPhone
import io.github.wulkanowy.utils.openMapLocation
import kotlinx.android.synthetic.main.fragment_school.*
import javax.inject.Inject
class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAndTeachersChildView {
@Inject
lateinit var presenter: SchoolPresenter
override val titleStringId get() = R.string.school_title
companion object {
fun newInstance() = SchoolFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_school, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
schoolSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
schoolAddressButton.setOnClickListener { presenter.onAddressSelected() }
schoolTelephoneButton.setOnClickListener { presenter.onTelephoneSelected() }
}
override fun updateData(data: School) {
schoolName.text = data.name
schoolAddress.text = data.address.ifBlank { "-" }
schoolAddressButton.visibility = if (data.address.isNotBlank()) View.VISIBLE else View.GONE
schoolTelephone.text = data.contact.ifBlank { "-" }
schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) View.VISIBLE else View.GONE
schoolHeadmaster.text = data.headmaster
schoolPedagogue.text = data.pedagogue
}
override fun showEmpty(show: Boolean) {
schoolEmpty.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showProgress(show: Boolean) {
schoolProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun enableSwipe(enable: Boolean) {
schoolSwipe.isEnabled = enable
}
override fun showContent(show: Boolean) {
schoolContent.visibility = if (show) View.VISIBLE else View.GONE
}
override fun hideRefresh() {
schoolSwipe.isRefreshing = false
}
override fun notifyParentDataLoaded() {
(parentFragment as? SchoolAndTeachersFragment)?.onChildFragmentLoaded()
}
override fun onParentLoadData(forceRefresh: Boolean) {
presenter.onParentViewLoadData(forceRefresh)
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
override fun openMapsLocation(location: String) {
context?.openMapLocation(location)
}
override fun dialPhone(phone: String) {
context?.dialPhone(phone)
}
}

View File

@ -0,0 +1,84 @@
package io.github.wulkanowy.ui.modules.schoolandteachers.school
import io.github.wulkanowy.data.repositories.school.SchoolRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class SchoolPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schoolRepository: SchoolRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<SchoolView>(errorHandler, studentRepository, schedulers) {
private var address: String? = null
private var contact: String? = null
override fun onAttachView(view: SchoolView) {
super.onAttachView(view)
view.initView()
Timber.i("School view was initialized")
loadData()
}
fun onSwipeRefresh() {
loadData(true)
}
fun onParentViewLoadData(forceRefresh: Boolean) {
loadData(forceRefresh)
}
fun onAddressSelected() {
address?.let{ view?.openMapsLocation(it) }
}
fun onTelephoneSelected() {
contact?.let { view?.dialPhone(it) }
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading school info started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMapMaybe { schoolRepository.getSchoolInfo(it, forceRefresh) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded()
}
}.subscribe({
Timber.i("Loading teachers result: Success")
view?.run {
address = it.address.ifBlank { null }
contact = it.contact.ifBlank { null }
updateData(it)
showContent(true)
showEmpty(false)
}
analytics.logEvent("load_school", "force_refresh" to forceRefresh)
}, {
Timber.i("Loading school result: An exception occurred")
errorHandler.dispatch(it)
}, {
Timber.i("Loading school result: No school info found")
view?.run {
showContent(false)
showEmpty(true)
}
}))
}
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.ui.modules.schoolandteachers.school
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
interface SchoolView : BaseView, SchoolAndTeachersChildView {
fun initView()
fun updateData(data: School)
fun showEmpty(show: Boolean)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)
fun showContent(show: Boolean)
fun hideRefresh()
fun openMapsLocation(location: String)
fun dialPhone(phone: String)
}

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.teacher package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -11,10 +11,13 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import kotlinx.android.synthetic.main.fragment_teacher.* import kotlinx.android.synthetic.main.fragment_teacher.*
import javax.inject.Inject import javax.inject.Inject
class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView { class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, SchoolAndTeachersChildView {
@Inject @Inject
lateinit var presenter: TeacherPresenter lateinit var presenter: TeacherPresenter
@ -86,6 +89,14 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView {
teacherSwipe.isRefreshing = false teacherSwipe.isRefreshing = false
} }
override fun notifyParentDataLoaded() {
(parentFragment as? SchoolAndTeachersFragment)?.onChildFragmentLoaded()
}
override fun onParentLoadData(forceRefresh: Boolean) {
presenter.onParentViewLoadData(forceRefresh)
}
override fun onDestroyView() { override fun onDestroyView() {
presenter.onDetachView() presenter.onDetachView()
super.onDestroyView() super.onDestroyView()

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.teacher package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.View import android.view.View

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.ui.modules.teacher package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -14,7 +14,6 @@ class TeacherPresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val teacherRepository: TeacherRepository, private val teacherRepository: TeacherRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
@ -31,12 +30,16 @@ class TeacherPresenter @Inject constructor(
loadData(true) loadData(true)
} }
fun onParentViewLoadData(forceRefresh: Boolean) {
loadData(forceRefresh)
}
private fun loadData(forceRefresh: Boolean = false) { private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading teachers data started") Timber.i("Loading teachers data started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { teacherRepository.getTeachers(it, forceRefresh) } .flatMap { teacherRepository.getTeachers(it, forceRefresh) }
.map { it.filter { teacher -> teacher.name.isNotBlank() } } .map { it.filter { teacher -> teacher.name.isNotBlank() } }
.map { items -> items.map { TeacherItem(it, view?.noSubjectString.orEmpty()) } } .map { items -> items.map { TeacherItem(it, view?.noSubjectString.orEmpty()) } }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
@ -45,6 +48,7 @@ class TeacherPresenter @Inject constructor(
hideRefresh() hideRefresh()
showProgress(false) showProgress(false)
enableSwipe(true) enableSwipe(true)
notifyParentDataLoaded()
} }
}.subscribe({ }.subscribe({
Timber.i("Loading teachers result: Success") Timber.i("Loading teachers result: Success")

View File

@ -1,9 +1,10 @@
package io.github.wulkanowy.ui.modules.teacher package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildView
interface TeacherView : BaseView { interface TeacherView : BaseView, SchoolAndTeachersChildView {
val isViewEmpty: Boolean val isViewEmpty: Boolean

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.utils
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.util.DisplayMetrics.DENSITY_DEFAULT import android.util.DisplayMetrics.DENSITY_DEFAULT
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
@ -31,4 +32,26 @@ fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -
} }
} }
fun Context.openEmail(chooserTitle: String, email: String, subject: String?, body: String?) {
val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", email, null))
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
if (subject != null) emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
if (body != null) emailIntent.putExtra(Intent.EXTRA_TEXT, body)
startActivity(Intent.createChooser(emailIntent, chooserTitle))
}
fun Context.openMapLocation(location: String) {
val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}")
val intent = Intent(Intent.ACTION_VIEW, intentUri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}
fun Context.dialPhone(phone: String) {
val intentUri = Uri.parse("tel:$phone")
val intent = Intent(Intent.ACTION_DIAL, intentUri)
startActivity(intent)
}
fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT

View File

@ -11,6 +11,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
@ -27,6 +28,7 @@ fun Fragment.toSection(): MainView.Section? {
is LuckyNumberFragment -> MainView.Section.LUCKY_NUMBER is LuckyNumberFragment -> MainView.Section.LUCKY_NUMBER
is SettingsFragment -> MainView.Section.SETTINGS is SettingsFragment -> MainView.Section.SETTINGS
is AboutFragment -> MainView.Section.ABOUT is AboutFragment -> MainView.Section.ABOUT
is SchoolAndTeachersFragment -> MainView.Section.SCHOOL
else -> null else -> null
} }
} }

View File

@ -1,10 +1,9 @@
Wersja 0.11.0 Wersja 0.12.0
- ciemny motyw dla widżetów - wsparcie dla dziennika Lubelskiego Portalu Oświatowego
- lista nauczycieli - naprawa problemów z wyświetlaniem ocen punktowych
- statystyka ocen punktowych ucznia na tle klasy i lepsze wyświetlanie punktów na liście ocen - automatyczne przełączanie motywu na androidzie 10
- ładniejsza nawigacja między datami w planie lekcji i nie tylko - wyświetlanie pomocy przy problemach z logowaniem
- naprawiony szczęśliwy numerek - wyświetlanie informacji o szkole
- naprawione logowanie do dziennika Resman
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

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="#FFF"
android:pathData="M18,15H16V17H18M18,11H16V13H18M20,19H12V17H14V15H12V13H14V11H12V9H20M10,7H8V5H10M10,11H8V9H10M10,15H8V13H10M10,19H8V17H10M6,7H4V5H6M6,11H4V9H6M6,15H4V13H6M6,19H4V17H6M12,7V3H2V21H22V7H12Z" />
</vector>

View File

@ -1,7 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path android:fillColor="#fff" android:pathData="M16.36 12.76C18.31 13.42 20 14.5 20 16V21H4V16C4 14.5 5.69 13.42 7.65 12.76L8.27 14L8.5 14.5C7 14.96 5.9 15.62 5.9 16V19.1H10.12L11 14.03L10.06 12.15C10.68 12.08 11.33 12.03 12 12.03C12.67 12.03 13.32 12.08 13.94 12.15L13 14.03L13.88 19.1H18.1V16C18.1 15.62 17 14.96 15.5 14.5L15.73 14L16.36 12.76M12 5C10.9 5 10 5.9 10 7C10 8.1 10.9 9 12 9C13.1 9 14 8.1 14 7C14 5.9 13.1 5 12 5M12 11C9.79 11 8 9.21 8 7C8 4.79 9.79 3 12 3C14.21 3 16 4.79 16 7C16 9.21 14.21 11 12 11Z" />
</vector>

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="#FFF"
android:pathData="M22.43,10.59l-9.01,-9.01c-0.75,-0.75 -2.07,-0.76 -2.83,0l-9,9c-0.78,0.78 -0.78,2.04 0,2.82l9,9c0.39,0.39 0.9,0.58 1.41,0.58 0.51,0 1.02,-0.19 1.41,-0.58l8.99,-8.99c0.79,-0.76 0.8,-2.02 0.03,-2.82zM12.01,20.99l-9,-9 9,-9 9,9 -9,9zM8,11v4h2v-3h4v2.5l3.5,-3.5L14,7.5L14,10L9,10c-0.55,0 -1,0.45 -1,1z"/>
</vector>

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="#FFF"
android:pathData="M6.54,5c0.06,0.89 0.21,1.76 0.45,2.59l-1.2,1.2c-0.41,-1.2 -0.67,-2.47 -0.76,-3.79h1.51m9.86,12.02c0.85,0.24 1.72,0.39 2.6,0.45v1.49c-1.32,-0.09 -2.59,-0.35 -3.8,-0.75l1.2,-1.19M7.5,3H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.49c0,-0.55 -0.45,-1 -1,-1 -1.24,0 -2.45,-0.2 -3.57,-0.57 -0.1,-0.04 -0.21,-0.05 -0.31,-0.05 -0.26,0 -0.51,0.1 -0.71,0.29l-2.2,2.2c-2.83,-1.45 -5.15,-3.76 -6.59,-6.59l2.2,-2.2c0.28,-0.28 0.36,-0.67 0.25,-1.02C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>

View File

@ -10,7 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<FrameLayout <androidx.fragment.app.FragmentContainerView
android:id="@+id/mainFragmentContainer" android:id="@+id/mainFragmentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -26,7 +26,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="middle" android:ellipsize="middle"
android:entries="@array/endpoints_keys" android:entries="@array/hosts_keys"
android:paddingStart="10dp" android:paddingStart="10dp"
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingTop="10dp" android:paddingTop="10dp"

View File

@ -25,13 +25,78 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/loginFormContact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<TextView
android:id="@+id/loginFormContactHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:text="@string/login_contact_header"
android:textSize="14sp"
app:fontFamily="sans-serif-medium" />
<LinearLayout
android:id="@+id/loginFormContactButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormContactEmail"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:text="@string/login_contact_email"
app:icon="@drawable/ic_more_messages" />
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormContactDiscord"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:text="@string/login_contact_discord"
app:icon="@drawable/ic_about_discord" />
</LinearLayout>
<View
android:id="@+id/loginFormContactDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<TextView <TextView
android:id="@+id/loginFormHeader" android:id="@+id/loginFormHeader"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="32dp"
android:layout_marginLeft="32dp" android:layout_marginLeft="32dp"
android:layout_marginTop="32dp" android:layout_marginTop="48dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:layout_marginRight="32dp" android:layout_marginRight="32dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
@ -41,7 +106,7 @@
app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout" app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormContact"
app:layout_constraintVertical_chainStyle="packed" /> app:layout_constraintVertical_chainStyle="packed" />
@ -127,7 +192,8 @@
android:id="@+id/loginFormHost" android:id="@+id/loginFormHost"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:ignore="LabelFor" /> android:editable="false"
tools:ignore="Deprecated,LabelFor" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton

View File

@ -19,6 +19,76 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout
android:id="@+id/loginStudentSelectContact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
tools:visibility="visible">
<View
android:id="@+id/loginStudentSelectContactTopDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/loginStudentSelectContactHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:text="@string/login_contact_header"
android:textSize="14sp"
app:fontFamily="sans-serif-medium" />
<LinearLayout
android:id="@+id/loginStudentSelectContactButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:id="@+id/loginStudentSelectContactEmail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:text="@string/login_contact_email"
app:icon="@drawable/ic_more_messages" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:id="@+id/loginStudentSelectContactDiscord"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:text="@string/login_contact_discord"
app:icon="@drawable/ic_about_discord" />
</LinearLayout>
<View
android:id="@+id/loginStudentSelectContactBottomDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<TextView <TextView
android:id="@+id/loginStudentSelectHeader" android:id="@+id/loginStudentSelectHeader"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -27,6 +97,7 @@
android:layout_marginLeft="32dp" android:layout_marginLeft="32dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:layout_marginRight="32dp" android:layout_marginRight="32dp"
android:layout_marginTop="32dp"
android:layout_marginBottom="32dp" android:layout_marginBottom="32dp"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:text="@string/login_select_student" android:text="@string/login_select_student"
@ -35,7 +106,7 @@
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectRecycler" app:layout_constraintBottom_toTopOf="@id/loginStudentSelectRecycler"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@id/loginStudentSelectContact"
app:layout_constraintVertical_chainStyle="packed" /> app:layout_constraintVertical_chainStyle="packed" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView

View File

@ -24,12 +24,81 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/loginSymbolContact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
tools:visibility="visible">
<View
android:id="@+id/loginSymbolContactTopDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/loginSymbolContactHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:gravity="center_horizontal"
android:text="@string/login_contact_header"
android:textSize="14sp"
app:fontFamily="sans-serif-medium" />
<LinearLayout
android:id="@+id/loginSymbolContactButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:id="@+id/loginSymbolContactEmail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:text="@string/login_contact_email"
app:icon="@drawable/ic_more_messages" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:id="@+id/loginSymbolContactDiscord"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:text="@string/login_contact_discord"
app:icon="@drawable/ic_about_discord" />
</LinearLayout>
<View
android:id="@+id/loginSymbolContactBottomDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" />
</LinearLayout>
<TextView <TextView
android:id="@+id/loginSymbolHeader" android:id="@+id/loginSymbolHeader"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp" android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:layout_marginRight="32dp" android:layout_marginRight="32dp"
@ -41,7 +110,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginSymbolContact"
app:layout_constraintVertical_chainStyle="packed" /> app:layout_constraintVertical_chainStyle="packed" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout

View File

@ -27,7 +27,7 @@
android:layout_marginTop="48dp" android:layout_marginTop="48dp"
android:visibility="invisible" /> android:visibility="invisible" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/openSendMessageButton" android:id="@+id/openSendMessageButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -36,7 +36,8 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:tint="?colorOnSecondary" android:tint="?colorOnSecondary"
app:srcCompat="@drawable/ic_menu_message_write" /> android:text="@string/send_message_title"
app:icon="@drawable/ic_menu_message_write" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar <me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/messageProgress" android:id="@+id/messageProgress"

View File

@ -0,0 +1,235 @@
<FrameLayout 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"
tools:context=".ui.modules.schoolandteachers.school.SchoolFragment">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/schoolProgress"
style="@style/Widget.MaterialProgressBar.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/schoolSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="8dp"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="8dp">
<LinearLayout
android:id="@+id/schoolContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/school_name"
android:textColor="?android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/schoolName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="17sp"
tools:text="@tools:sample/full_names" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="7dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/school_address"
android:textColor="?android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/schoolAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="17sp"
tools:text="@tools:sample/full_names" />
</LinearLayout>
<ImageButton
android:id="@+id/schoolAddressButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_school_directions"
android:contentDescription="@string/school_address_button"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="?colorPrimary"
android:padding="4dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:layout_marginLeft="8dp"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="7dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/school_telephone"
android:textColor="?android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/schoolTelephone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="17sp"
tools:text="@tools:sample/full_names" />
</LinearLayout>
<ImageButton
android:id="@+id/schoolTelephoneButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_school_phone"
android:contentDescription="@string/school_telephone_button"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="?colorPrimary"
android:padding="4dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:layout_marginLeft="8dp"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="7dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/school_headmaster"
android:textColor="?android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/schoolHeadmaster"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="17sp"
tools:text="@tools:sample/full_names" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingLeft="12dp"
android:paddingTop="7dp"
android:paddingEnd="12dp"
android:paddingRight="12dp"
android:paddingBottom="7dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/school_pedagogue"
android:textColor="?android:textColorSecondary"
android:textSize="12sp" />
<TextView
android:id="@+id/schoolPedagogue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="17sp"
tools:text="@tools:sample/full_names" />
</LinearLayout>
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/schoolEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="gone"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_more_schoolandteachers"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/teacher_no_items"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,38 @@
<?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">
<io.github.wulkanowy.ui.widgets.MaterialTabLayout
android:id="@+id/schoolandteachersTabLayout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?colorSurface"
android:visibility="invisible"
app:tabGravity="fill"
app:tabIndicatorColor="?colorPrimary"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabSelectedTextColor="?colorPrimary"
app:tabTextColor="@color/mtrl_on_surface_emphasis_medium"
tools:ignore="UnusedAttribute"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/schoolandteachersViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:visibility="invisible" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/schoolandteachersProgress"
style="@style/Widget.MaterialProgressBar.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -18,6 +18,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
tools:listitem="@layout/item_teacher"
android:id="@+id/teacherRecycler" android:id="@+id/teacherRecycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
@ -36,7 +37,7 @@
<ImageView <ImageView
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="100dp" android:layout_height="100dp"
app:srcCompat="@drawable/ic_more_teacher" app:srcCompat="@drawable/ic_more_schoolandteachers"
app:tint="?colorOnBackground" app:tint="?colorOnBackground"
tools:ignore="contentDescription" /> tools:ignore="contentDescription" />

View File

@ -1,5 +1,4 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -10,7 +9,7 @@
android:paddingEnd="12dp" android:paddingEnd="12dp"
android:paddingRight="12dp" android:paddingRight="12dp"
android:paddingBottom="7dp" android:paddingBottom="7dp"
tools:context=".ui.modules.teacher.TeacherItem"> tools:context=".ui.modules.schoolandteachers.teacher.TeacherItem">
<TextView <TextView
android:id="@+id/teacherItemName" android:id="@+id/teacherItemName"

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
<item>Motyw systemu</item>
<item>Jasny</item>
<item>Ciemny</item>
<item>Czarny (AMOLED)</item>
</string-array>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="services_interval_entries"> <string-array name="services_interval_entries">
<item>15 minut</item> <item>15 minut</item>
<item>30 minut</item> <item>30 minut</item>
@ -10,7 +10,7 @@
<item>24 godziny</item> <item>24 godziny</item>
</string-array> </string-array>
<string-array name="app_theme_entries"> <string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
<item>Jasny</item> <item>Jasny</item>
<item>Ciemny</item> <item>Ciemny</item>
<item>Czarny (AMOLED)</item> <item>Czarny (AMOLED)</item>

View File

@ -36,6 +36,10 @@
<string name="login_symbol_helper">Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string> <string name="login_symbol_helper">Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string>
<string name="login_select_student">Wybierz uczniów do zalogowania w aplikacji</string> <string name="login_select_student">Wybierz uczniów do zalogowania w aplikacji</string>
<string name="login_privacy_policy">Polityka prywatności</string> <string name="login_privacy_policy">Polityka prywatności</string>
<string name="login_contact_header">Problemy z logowaniem? Napisz do nas!</string>
<string name="login_contact_email">Email</string>
<string name="login_contact_discord">Discord</string>
<string name="login_email_intent_title">Wyślij email</string>
<!--Main--> <!--Main-->
@ -217,6 +221,21 @@
<string name="mobile_device_pin">PIN</string> <string name="mobile_device_pin">PIN</string>
<!--School and teachers-->
<string name="schoolandteachers_title">Szkoła i nauczyciele</string>
<!--School-->
<string name="school_title">Szkoła</string>
<string name="school_name">Nazwa szkoły</string>
<string name="school_address">Adres szkoły</string>
<string name="school_telephone">Telefon</string>
<string name="school_headmaster">Imię i nazwisko dyrektora</string>
<string name="school_pedagogue">Imię i nazwisko pedagoga</string>
<string name="school_address_button">Pokaż na mpaie</string>
<string name="school_telephone_button">Zadzwoń</string>
<!--Teacher--> <!--Teacher-->
<string name="teachers_title">Nauczyciele</string> <string name="teachers_title">Nauczyciele</string>
<string name="teacher_no_items">Brak informacji o nauczycielach</string> <string name="teacher_no_items">Brak informacji o nauczycielach</string>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="pref_default_app_theme">system</string>
</resources>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
<item>System theme</item>
<item>Light</item>
<item>Dark</item>
<item>Black (AMOLED)</item>
</string-array>
<string-array name="app_theme_values" translatable="false" tools:ignore="InconsistentArrays">
<item>system</item>
<item>light</item>
<item>dark</item>
<item>black</item>
</string-array>
</resources>

View File

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string-array name="endpoints_keys"> <string-array name="hosts_keys">
<item>Vulcan</item> <item>Vulcan</item>
<item>Opolska eSzkoła</item> <item>Opolska eSzkoła</item>
<item>Gdańska Platforma Edukacyjna</item> <item>Gdańska Platforma Edukacyjna</item>
<item>Lubelski Portal Oświatowy</item>
<item>EduNet Miasta Tarnowa</item> <item>EduNet Miasta Tarnowa</item>
<item>ResMan Rzeszów</item> <item>ResMan Rzeszów</item>
<item>Fakelog</item> <item>Fakelog</item>
</string-array> </string-array>
<string-array name="endpoints_values"> <string-array name="hosts_values">
<item>https://vulcan.net.pl</item> <item>https://vulcan.net.pl</item>
<item>https://eszkola.opolskie.pl</item> <item>https://eszkola.opolskie.pl</item>
<item>https://edu.gdansk.pl</item> <item>https://edu.gdansk.pl</item>
<item>https://edu.lublin.eu</item>
<item>https://umt.tarnow.pl</item> <item>https://umt.tarnow.pl</item>
<item>https://resman.pl</item> <item>https://resman.pl</item>
<item>http://fakelog.cf</item> <item>http://fakelog.cf</item>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="pref_default_startup">0</string>
<bool name="pref_default_attendance_present">true</bool>
<string name="pref_default_grade_average_mode">only_one_semester</string>
<bool name="pref_default_grade_average_force_calc">false</bool>
<bool name="pref_default_expand_grade">false</bool>
<string name="pref_default_app_theme">light</string>
<string name="pref_default_grade_color_scheme">vulcan</string>
<bool name="pref_default_services_enable">true</bool>
<string name="pref_default_services_interval">60</string>
<bool name="pref_default_services_wifi_only">false</bool>
<bool name="pref_default_notifications_enable">true</bool>
<bool name="pref_default_notification_debug">false</bool>
<string name="pref_default_grade_modifier_plus">0.0</string>
<string name="pref_default_grade_modifier_minus">0.0</string>
<bool name="pref_default_fill_message_content">true</bool>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="startup_tab_entries" translatable="false"> <string-array name="startup_tab_entries" translatable="false">
<item>@string/grade_title</item> <item>@string/grade_title</item>
<item>@string/attendance_title</item> <item>@string/attendance_title</item>
@ -13,12 +13,12 @@
<item>3</item> <item>3</item>
</string-array> </string-array>
<string-array name="app_theme_entries"> <string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
<item>Light</item> <item>Light</item>
<item>Dark</item> <item>Dark</item>
<item>Black (AMOLED)</item> <item>Black (AMOLED)</item>
</string-array> </string-array>
<string-array name="app_theme_values" translatable="false"> <string-array name="app_theme_values" translatable="false" tools:ignore="InconsistentArrays">
<item>light</item> <item>light</item>
<item>dark</item> <item>dark</item>
<item>black</item> <item>black</item>

View File

@ -36,6 +36,12 @@
<string name="login_symbol_helper">The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string> <string name="login_symbol_helper">The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string>
<string name="login_select_student">Select students to log in to the application</string> <string name="login_select_student">Select students to log in to the application</string>
<string name="login_privacy_policy">Privacy policy</string> <string name="login_privacy_policy">Privacy policy</string>
<string name="login_contact_header">Trouble signing in? Contact us!</string>
<string name="login_contact_email">Email</string>
<string name="login_contact_discord">Discord</string>
<string name="login_email_intent_title">Send email</string>
<string name="login_email_subject" translatable="false">Zgłoszenie: Problemy z logowaniem</string>
<string name="login_email_text" translatable="false">Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nOpis problemu:</string>
<!--Main--> <!--Main-->
@ -201,6 +207,21 @@
<string name="mobile_device_pin">PIN</string> <string name="mobile_device_pin">PIN</string>
<!--School and teachers-->
<string name="schoolandteachers_title">School and teachers</string>
<!--School-->
<string name="school_title">School</string>
<string name="school_name">School name</string>
<string name="school_address">School address</string>
<string name="school_telephone">Telephone</string>
<string name="school_headmaster">Name of headmaster</string>
<string name="school_pedagogue">Name of pedagogue</string>
<string name="school_address_button">Show on map</string>
<string name="school_telephone_button">Call</string>
<!--Teacher--> <!--Teacher-->
<string name="teachers_title">Teachers</string> <string name="teachers_title">Teachers</string>
<string name="teacher_no_items">No info about teachers</string> <string name="teacher_no_items">No info about teachers</string>

View File

@ -4,7 +4,7 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:title="@string/pref_view_header"> app:title="@string/pref_view_header">
<ListPreference <ListPreference
app:defaultValue="0" app:defaultValue="@string/pref_default_startup"
app:entries="@array/startup_tab_entries" app:entries="@array/startup_tab_entries"
app:entryValues="@array/startup_tab_value" app:entryValues="@array/startup_tab_value"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -12,7 +12,7 @@
app:title="@string/pref_view_list" app:title="@string/pref_view_list"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
app:defaultValue="light" app:defaultValue="@string/pref_default_app_theme"
app:entries="@array/app_theme_entries" app:entries="@array/app_theme_entries"
app:entryValues="@array/app_theme_values" app:entryValues="@array/app_theme_values"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -20,17 +20,17 @@
app:title="@string/pref_view_app_theme" app:title="@string/pref_view_app_theme"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="@bool/pref_default_attendance_present"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_attendance_present" app:key="@string/pref_key_attendance_present"
app:title="@string/pref_view_present" /> app:title="@string/pref_view_present" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="@bool/pref_default_expand_grade"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_expand_grade" app:key="@string/pref_key_expand_grade"
app:title="@string/pref_view_expand_grade" /> app:title="@string/pref_view_expand_grade" />
<ListPreference <ListPreference
app:defaultValue="vulcan" app:defaultValue="@string/pref_default_grade_color_scheme"
app:entries="@array/grade_color_scheme_entries" app:entries="@array/grade_color_scheme_entries"
app:entryValues="@array/grade_color_scheme_values" app:entryValues="@array/grade_color_scheme_values"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -42,12 +42,12 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:title="@string/pref_services_header"> app:title="@string/pref_services_header">
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="@bool/pref_default_services_enable"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_services_enable" app:key="@string/pref_key_services_enable"
app:title="@string/pref_services_switch" /> app:title="@string/pref_services_switch" />
<ListPreference <ListPreference
app:defaultValue="60" app:defaultValue="@string/pref_default_services_interval"
app:dependency="services_enable" app:dependency="services_enable"
app:entries="@array/services_interval_entries" app:entries="@array/services_interval_entries"
app:entryValues="@array/services_interval_value" app:entryValues="@array/services_interval_value"
@ -56,7 +56,7 @@
app:title="@string/pref_services_interval" app:title="@string/pref_services_interval"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="@bool/pref_default_services_wifi_only"
app:dependency="services_enable" app:dependency="services_enable"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_services_wifi_only" app:key="@string/pref_key_services_wifi_only"
@ -66,13 +66,13 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:title="@string/pref_notify_header"> app:title="@string/pref_notify_header">
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="@bool/pref_default_notifications_enable"
app:dependency="services_enable" app:dependency="services_enable"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_enable" app:key="@string/pref_key_notifications_enable"
app:title="@string/pref_notify_switch" /> app:title="@string/pref_notify_switch" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="@bool/pref_default_notification_debug"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_notification_debug" app:key="@string/pref_key_notification_debug"
app:title="@string/pref_notify_debug_switch" /> app:title="@string/pref_notify_debug_switch" />
@ -81,7 +81,7 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:title="@string/pref_other_header"> app:title="@string/pref_other_header">
<ListPreference <ListPreference
app:defaultValue="0.0" app:defaultValue="@string/pref_default_grade_modifier_plus"
app:entries="@array/grade_modifier_entries" app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value" app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -89,7 +89,7 @@
app:title="@string/pref_other_grade_modifier_plus" app:title="@string/pref_other_grade_modifier_plus"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
app:defaultValue="0.0" app:defaultValue="@string/pref_default_grade_modifier_minus"
app:entries="@array/grade_modifier_entries" app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value" app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -97,7 +97,7 @@
app:title="@string/pref_other_grade_modifier_minus" app:title="@string/pref_other_grade_modifier_minus"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<ListPreference <ListPreference
app:defaultValue="only_one_semester" app:defaultValue="@string/pref_default_grade_average_mode"
app:entries="@array/grade_average_mode_entries" app:entries="@array/grade_average_mode_entries"
app:entryValues="@array/grade_average_mode_values" app:entryValues="@array/grade_average_mode_values"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
@ -105,12 +105,12 @@
app:title="@string/pref_view_grade_average_mode" app:title="@string/pref_view_grade_average_mode"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="@bool/pref_default_grade_average_force_calc"
app:key="@string/pref_key_grade_average_force_calc" app:key="@string/pref_key_grade_average_force_calc"
app:title="@string/pref_view_grade_average_force_calc" app:title="@string/pref_view_grade_average_force_calc"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="true" app:defaultValue="@bool/pref_default_fill_message_content"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="@string/pref_key_fill_message_content" app:key="@string/pref_key_fill_message_content"
app:title="@string/pref_other_fill_message_content" /> app:title="@string/pref_other_fill_message_content" />

View File

@ -11,7 +11,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.google.gms:google-services:4.3.2' classpath 'com.google.gms:google-services:4.3.2'
classpath "io.fabric.tools:gradle:1.31.1" classpath "io.fabric.tools:gradle:1.31.2"
classpath "com.github.triplet.gradle:play-publisher:2.4.2" classpath "com.github.triplet.gradle:play-publisher:2.4.2"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"