forked from github/szkolny
[APIv2/UI] Add new Timetable module. Implement in Mobidziennik.
This commit is contained in:
parent
01ac26e67b
commit
1b75424604
@ -166,6 +166,10 @@ dependencies {
|
||||
implementation "androidx.work:work-runtime-ktx:${versions.work}"
|
||||
|
||||
implementation 'com.hypertrack:hyperlog:0.0.10'
|
||||
|
||||
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
||||
|
||||
implementation 'com.linkedin.android.tachyon:tachyon:1.0.2'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
9
app/sampledata/check/ic_check.xml
Normal file
9
app/sampledata/check/ic_check.xml
Normal 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="#FF4caf50"
|
||||
android:pathData="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/>
|
||||
</vector>
|
@ -6,8 +6,13 @@ import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.util.forEach
|
||||
import com.google.gson.JsonArray
|
||||
@ -326,4 +331,64 @@ fun String.crc32(): Long {
|
||||
return crc.value
|
||||
}
|
||||
|
||||
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
||||
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
||||
|
||||
fun CharSequence?.asColoredSpannable(colorInt: Int): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(ForegroundColorSpan(colorInt), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence?.asStrikethroughSpannable(): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(StrikethroughSpan(), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new read-only list only of those given elements, that are not empty.
|
||||
* Applies for CharSequence and descendants.
|
||||
*/
|
||||
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||
|
||||
fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||
if (this.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (this.size == 1) {
|
||||
return this[0]
|
||||
}
|
||||
|
||||
var spanned = false
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var first = true
|
||||
if (spanned) {
|
||||
val ssb = SpannableStringBuilder()
|
||||
for (piece in this) {
|
||||
if (!first && delimiter != null)
|
||||
ssb.append(delimiter)
|
||||
first = false
|
||||
ssb.append(piece)
|
||||
}
|
||||
return SpannedString(ssb)
|
||||
} else {
|
||||
val sb = StringBuilder()
|
||||
for (piece in this) {
|
||||
if (!first && delimiter != null)
|
||||
sb.append(delimiter)
|
||||
first = false
|
||||
sb.append(piece)
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
|
||||
text = context.getString(resid, formatArgs)
|
||||
}
|
@ -62,7 +62,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
@ -5,15 +5,77 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
init {
|
||||
for (lessonStr in rows) {
|
||||
val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") }
|
||||
|
||||
for (lesson in lessons) {
|
||||
val date = Date.fromYmd(lesson[2])
|
||||
val startTime = Time.fromYmdHm(lesson[3])
|
||||
val endTime = Time.fromYmdHm(lesson[4])
|
||||
val id = date.combineWith(startTime) / 1000L
|
||||
|
||||
val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5] }?.id ?: -1
|
||||
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1
|
||||
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||
val classroom = lesson[11]
|
||||
|
||||
Lesson(data.profileId, id).also {
|
||||
when (lesson[1]) {
|
||||
"plan_lekcji", "lekcja" -> {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
"lekcja_odwolana" -> {
|
||||
it.type = Lesson.TYPE_CANCELLED
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.oldSubjectId = subjectId
|
||||
//it.oldTeacherId = teacherId
|
||||
it.oldTeamId = teamId
|
||||
//it.oldClassroom = classroom
|
||||
}
|
||||
"zastepstwo" -> {
|
||||
it.type = Lesson.TYPE_CHANGE
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
it.id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList += it
|
||||
}
|
||||
}
|
||||
/*for (lessonStr in rows) {
|
||||
if (lessonStr.isNotEmpty()) {
|
||||
val lesson = lessonStr.split("|")
|
||||
|
||||
@ -76,9 +138,9 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
if (originalLesson == null) {
|
||||
// original lesson doesn't exist, save a new addition
|
||||
// TODO
|
||||
/*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||
*//*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||
app.profile.timetable.addLessonAddition(registerLessonChange);
|
||||
}*/
|
||||
}*//*
|
||||
} else {
|
||||
// original lesson exists, so we need to compare them
|
||||
if (!lessonChange.matches(originalLesson)) {
|
||||
@ -108,6 +170,6 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -136,6 +136,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
var lessonsToRemove: DataRemoveModel? = null
|
||||
val lessonList = mutableListOf<Lesson>()
|
||||
val lessonChangeList = mutableListOf<LessonChange>()
|
||||
val lessonNewList = mutableListOf<pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson>()
|
||||
|
||||
var gradesToRemove: DataRemoveModel? = null
|
||||
val gradeList = mutableListOf<Grade>()
|
||||
@ -195,6 +196,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
|
||||
lessonList.clear()
|
||||
lessonChangeList.clear()
|
||||
lessonNewList.clear()
|
||||
gradeList.clear()
|
||||
noticeList.clear()
|
||||
attendanceList.clear()
|
||||
@ -282,6 +284,10 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
}
|
||||
if (lessonChangeList.isNotEmpty())
|
||||
db.lessonChangeDao().addAll(lessonChangeList)
|
||||
if (lessonNewList.isNotEmpty()) {
|
||||
db.timetableDao().clear(profile.id)
|
||||
db.timetableDao() += lessonNewList
|
||||
}
|
||||
if (gradeList.isNotEmpty()) {
|
||||
db.gradeDao().addAll(gradeList)
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceTypeDao
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherDao;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
@Database(entities = {
|
||||
@ -103,7 +104,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
Classroom.class,
|
||||
NoticeType.class,
|
||||
AttendanceType.class,
|
||||
Metadata.class}, version = 63)
|
||||
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
|
||||
Metadata.class}, version = 64)
|
||||
@TypeConverters({
|
||||
ConverterTime.class,
|
||||
ConverterDate.class,
|
||||
@ -141,6 +143,7 @@ public abstract class AppDb extends RoomDatabase {
|
||||
public abstract ClassroomDao classroomDao();
|
||||
public abstract NoticeTypeDao noticeTypeDao();
|
||||
public abstract AttendanceTypeDao attendanceTypeDao();
|
||||
public abstract TimetableDao timetableDao();
|
||||
public abstract MetadataDao metadataDao();
|
||||
|
||||
private static volatile AppDb INSTANCE;
|
||||
@ -729,6 +732,37 @@ public abstract class AppDb extends RoomDatabase {
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_63_64 = new Migration(63, 64) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
//database.execSQL("ALTER TABLE lessons RENAME TO lessonsOld;");
|
||||
database.execSQL("CREATE TABLE timetable (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"id INTEGER NOT NULL," +
|
||||
"type INTEGER NOT NULL," +
|
||||
|
||||
"date TEXT DEFAULT NULL," +
|
||||
"lessonNumber INTEGER DEFAULT NULL," +
|
||||
"startTime TEXT DEFAULT NULL," +
|
||||
"endTime TEXT DEFAULT NULL," +
|
||||
"subjectId INTEGER DEFAULT NULL," +
|
||||
"teacherId INTEGER DEFAULT NULL," +
|
||||
"teamId INTEGER DEFAULT NULL," +
|
||||
"classroom TEXT DEFAULT NULL," +
|
||||
|
||||
"oldDate TEXT DEFAULT NULL," +
|
||||
"oldLessonNumber INTEGER DEFAULT NULL," +
|
||||
"oldStartTime TEXT DEFAULT NULL," +
|
||||
"oldEndTime TEXT DEFAULT NULL," +
|
||||
"oldSubjectId INTEGER DEFAULT NULL," +
|
||||
"oldTeacherId INTEGER DEFAULT NULL," +
|
||||
"oldTeamId INTEGER DEFAULT NULL," +
|
||||
"oldClassroom TEXT DEFAULT NULL," +
|
||||
"PRIMARY KEY(id));");
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_date ON timetable (profileId, type, date);");
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static AppDb getDatabase(final Context context) {
|
||||
@ -789,7 +823,8 @@ public abstract class AppDb extends RoomDatabase {
|
||||
MIGRATION_59_60,
|
||||
MIGRATION_60_61,
|
||||
MIGRATION_61_62,
|
||||
MIGRATION_62_63
|
||||
MIGRATION_62_63,
|
||||
MIGRATION_63_64
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
//.fallbackToDestructiveMigration()
|
||||
|
@ -4,11 +4,18 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
open class Lesson(val profileId: Int) {
|
||||
@Entity(tableName = "timetable",
|
||||
indices = [
|
||||
Index(value = ["profileId", "type", "date"]),
|
||||
Index(value = ["profileId", "type", "oldDate"])
|
||||
])
|
||||
open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
companion object {
|
||||
const val TYPE_NORMAL = 0
|
||||
const val TYPE_CANCELLED = 1
|
||||
@ -17,15 +24,14 @@ open class Lesson(val profileId: Int) {
|
||||
const val TYPE_SHIFTED_TARGET = 4 /* target lesson */
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "lessonType")
|
||||
var type: Int = TYPE_NORMAL
|
||||
|
||||
var date: Date? = null
|
||||
var lessonNumber: Int? = null
|
||||
var startTime: Time? = null
|
||||
var endTime: Time? = null
|
||||
var teacherId: Long? = null
|
||||
var subjectId: Long? = null
|
||||
var teacherId: Long? = null
|
||||
var teamId: Long? = null
|
||||
var classroom: String? = null
|
||||
|
||||
@ -33,8 +39,58 @@ open class Lesson(val profileId: Int) {
|
||||
var oldLessonNumber: Int? = null
|
||||
var oldStartTime: Time? = null
|
||||
var oldEndTime: Time? = null
|
||||
var oldTeacherId: Long? = null
|
||||
var oldSubjectId: Long? = null
|
||||
var oldTeacherId: Long? = null
|
||||
var oldTeamId: Long? = null
|
||||
var oldClassroom: String? = null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Lesson(profileId=$profileId, " +
|
||||
"id=$id, " +
|
||||
"type=$type, " +
|
||||
"date=$date, " +
|
||||
"lessonNumber=$lessonNumber, " +
|
||||
"startTime=$startTime, " +
|
||||
"endTime=$endTime, " +
|
||||
"subjectId=$subjectId, " +
|
||||
"teacherId=$teacherId, " +
|
||||
"teamId=$teamId, " +
|
||||
"classroom=$classroom, " +
|
||||
"oldDate=$oldDate, " +
|
||||
"oldLessonNumber=$oldLessonNumber, " +
|
||||
"oldStartTime=$oldStartTime, " +
|
||||
"oldEndTime=$oldEndTime, " +
|
||||
"oldSubjectId=$oldSubjectId, " +
|
||||
"oldTeacherId=$oldTeacherId, " +
|
||||
"oldTeamId=$oldTeamId, " +
|
||||
"oldClassroom=$oldClassroom)"
|
||||
}
|
||||
}
|
||||
/*
|
||||
DROP TABLE lessons;
|
||||
DROP TABLE lessonChanges;
|
||||
CREATE TABLE lessons (
|
||||
profileId INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
|
||||
date TEXT DEFAULT NULL,
|
||||
lessonNumber INTEGER DEFAULT NULL,
|
||||
startTime TEXT DEFAULT NULL,
|
||||
endTime TEXT DEFAULT NULL,
|
||||
teacherId INTEGER DEFAULT NULL,
|
||||
subjectId INTEGER DEFAULT NULL,
|
||||
teamId INTEGER DEFAULT NULL,
|
||||
classroom TEXT DEFAULT NULL,
|
||||
|
||||
oldDate TEXT DEFAULT NULL,
|
||||
oldLessonNumber INTEGER DEFAULT NULL,
|
||||
oldStartTime TEXT DEFAULT NULL,
|
||||
oldEndTime TEXT DEFAULT NULL,
|
||||
oldTeacherId INTEGER DEFAULT NULL,
|
||||
oldSubjectId INTEGER DEFAULT NULL,
|
||||
oldTeamId INTEGER DEFAULT NULL,
|
||||
oldClassroom TEXT DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY(profileId)
|
||||
);
|
||||
*/
|
@ -0,0 +1,63 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
var subjectName: String? = null
|
||||
var teacherName: String? = null
|
||||
var teamName: String? = null
|
||||
var oldSubjectName: String? = null
|
||||
var oldTeacherName: String? = null
|
||||
var oldTeamName: String? = null
|
||||
|
||||
val displayDate: Date?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldDate
|
||||
return date ?: oldDate
|
||||
}
|
||||
val displayStartTime: Time?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldStartTime
|
||||
return startTime ?: oldStartTime
|
||||
}
|
||||
val displayEndTime: Time?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldEndTime
|
||||
return endTime ?: oldEndTime
|
||||
}
|
||||
|
||||
val displaySubjectName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldSubjectName
|
||||
return subjectName ?: oldSubjectName
|
||||
}
|
||||
val displayTeacherName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeacherName
|
||||
return teacherName ?: oldTeacherName
|
||||
}
|
||||
val displayTeamName: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldTeamName
|
||||
return teamName ?: oldTeamName
|
||||
}
|
||||
|
||||
val displayClassroom: String?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldClassroom
|
||||
return classroom ?: oldClassroom
|
||||
}
|
||||
|
||||
// metadata
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
var addedDate: Long = 0
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@Dao
|
||||
interface TimetableDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
operator fun plusAssign(lessonList: List<Lesson>)
|
||||
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
timetable.*,
|
||||
subjects.subjectLongName AS subjectName,
|
||||
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
|
||||
teams.teamName AS teamName,
|
||||
oldS.subjectLongName AS oldSubjectName,
|
||||
oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
|
||||
oldG.teamName AS oldTeamName,
|
||||
metadata.seen, metadata.notified, metadata.addedDate
|
||||
FROM timetable
|
||||
LEFT JOIN subjects USING(profileId, subjectId)
|
||||
LEFT JOIN teachers USING(profileId, teacherId)
|
||||
LEFT JOIN teams USING(profileId, teamId)
|
||||
LEFT JOIN subjects AS oldS ON timetable.profileId = oldS.profileId AND timetable.oldSubjectId = oldS.subjectId
|
||||
LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId
|
||||
LEFT JOIN teams AS oldG ON timetable.profileId = oldG.profileId AND timetable.oldTeamId = oldG.teamId
|
||||
LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId
|
||||
WHERE timetable.profileId = :profileId AND (type != 3 AND date = :date) OR (type = 3 AND oldDate = :date)
|
||||
""")
|
||||
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class TimetableFragment : Fragment() {
|
||||
companion object {
|
||||
private const val TAG = "TimetableFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentTimetableV2Binding
|
||||
private var fabShown = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
if (context == null)
|
||||
return null
|
||||
app = activity.application as App
|
||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false)
|
||||
// activity, context and profile is valid
|
||||
b = FragmentTimetableV2Binding.inflate(inflater)
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
val items = mutableListOf<Date>()
|
||||
|
||||
val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
|
||||
val today = Date.getToday().value
|
||||
val yearStart = app.profile.dateSemester1Start?.clone() ?: return
|
||||
val yearEnd = app.profile.dateYearEnd ?: return
|
||||
while (yearStart.value <= yearEnd.value) {
|
||||
items += yearStart.clone()
|
||||
var maxDays = monthDayCount[yearStart.month-1]
|
||||
if (yearStart.month == 2 && yearStart.isLeap)
|
||||
maxDays++
|
||||
yearStart.day++
|
||||
if (yearStart.day > maxDays) {
|
||||
yearStart.day = 1
|
||||
yearStart.month++
|
||||
}
|
||||
if (yearStart.month > 12) {
|
||||
yearStart.month = 1
|
||||
yearStart.year++
|
||||
}
|
||||
}
|
||||
|
||||
val pagerAdapter = TimetablePagerAdapter(fragmentManager ?: return, items)
|
||||
b.viewPager.adapter = pagerAdapter
|
||||
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
activity.navView.bottomBar.fabEnable = items[position].value != today
|
||||
if (activity.navView.bottomBar.fabEnable && !fabShown) {
|
||||
activity.gainAttentionFAB()
|
||||
fabShown = true
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
b.tabLayout.setUpWithViewPager(b.viewPager)
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, false)
|
||||
|
||||
//activity.navView.bottomBar.fabEnable = true
|
||||
activity.navView.bottomBar.fabExtendedText = getString(R.string.timetable_today)
|
||||
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon.cmd_calendar_today
|
||||
activity.navView.setFabOnClickListener(View.OnClickListener {
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: List<Date>) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
companion object {
|
||||
private const val TAG = "TimetablePagerAdapter"
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
return pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day.TimetableDayFragment(items[position])
|
||||
/*return TimetableDayFragment().apply {
|
||||
arguments = Bundle().also {
|
||||
it.putLong("date", items[position].value.toLong())
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return items[position].formattedStringShort
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import com.linkedin.android.tachyon.DayView
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
import java.util.*
|
||||
|
||||
class TimetableDayFragment(val date: Date) : Fragment() {
|
||||
companion object {
|
||||
private const val TAG = "TimetableDayFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentTimetableV2DayBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
if (context == null)
|
||||
return null
|
||||
app = activity.application as App
|
||||
b = FragmentTimetableV2DayBinding.inflate(inflater)
|
||||
Log.d(TAG, "onCreateView, date=$date")
|
||||
return b.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
Log.d(TAG, "onViewCreated, date=$date")
|
||||
b.date.text = date.formattedString
|
||||
|
||||
// Inflate a label view for each hour the day view will display
|
||||
val hourLabelViews = ArrayList<View>()
|
||||
for (i in b.day.startHour..b.day.endHour) {
|
||||
val hourLabelView = layoutInflater.inflate(R.layout.timetable_hour_label, b.day, false) as TextView
|
||||
hourLabelView.text = "$i:00"
|
||||
hourLabelViews.add(hourLabelView)
|
||||
}
|
||||
b.day.setHourLabelViews(hourLabelViews)
|
||||
|
||||
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer<List<LessonFull>> { lessons ->
|
||||
buildLessonViews(lessons)
|
||||
})
|
||||
}
|
||||
|
||||
private fun buildLessonViews(lessons: List<LessonFull>) {
|
||||
val eventViews = mutableListOf<View>()
|
||||
val eventTimeRanges = mutableListOf<DayView.EventTimeRange>()
|
||||
|
||||
// Reclaim all of the existing event views so we can reuse them if needed, this process
|
||||
// can be useful if your day view is hosted in a recycler view for example
|
||||
val recycled = b.day.removeEventViews()
|
||||
var remaining = recycled?.size ?: 0
|
||||
|
||||
val arrowRight = " → "
|
||||
val bullet = " • "
|
||||
val colorSecondary = getColorFromAttr(activity, android.R.attr.textColorSecondary)
|
||||
|
||||
for (lesson in lessons) {
|
||||
val startTime = lesson.displayStartTime ?: continue
|
||||
val endTime = lesson.displayEndTime ?: continue
|
||||
|
||||
// Try to recycle an existing event view if there are enough left, otherwise inflate
|
||||
// a new one
|
||||
val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, b.day, false))
|
||||
?: continue
|
||||
val lb = TimetableLessonBinding.bind(eventView)
|
||||
eventViews += eventView
|
||||
|
||||
eventView.tag = lesson
|
||||
|
||||
eventView.setOnClickListener {
|
||||
Log.d(TAG, "Clicked ${it.tag}")
|
||||
}
|
||||
|
||||
|
||||
val timeRange = "${startTime.stringHM} - ${endTime.stringHM}".asColoredSpannable(colorSecondary)
|
||||
|
||||
// teacher
|
||||
val teacherInfo = if (lesson.teacherId != null && lesson.teacherId == lesson.oldTeacherId)
|
||||
lesson.teacherName ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldTeacherName?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.teacherName?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
// team
|
||||
val teamInfo = if (lesson.teamId != null && lesson.teamId == lesson.oldTeamId)
|
||||
lesson.teamName ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldTeamName?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.teamName?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
// classroom
|
||||
val classroomInfo = if (lesson.classroom != null && lesson.classroom == lesson.oldClassroom)
|
||||
lesson.classroom ?: "?"
|
||||
else
|
||||
mutableListOf<CharSequence>().apply {
|
||||
lesson.oldClassroom?.let { add(it.asStrikethroughSpannable()) }
|
||||
lesson.classroom?.let { add(it) }
|
||||
}.concat(arrowRight)
|
||||
|
||||
|
||||
lb.subjectName.text = lesson.displaySubjectName?.let { if (lesson.type == Lesson.TYPE_CANCELLED) it.asStrikethroughSpannable().asColoredSpannable(colorSecondary) else it }
|
||||
lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
lb.annotation.visibility = View.GONE
|
||||
}
|
||||
Lesson.TYPE_CANCELLED -> {
|
||||
lb.annotation.visibility = View.VISIBLE
|
||||
lb.annotation.setText(R.string.timetable_lesson_cancelled)
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
//lb.subjectName.typeface = Typeface.DEFAULT
|
||||
}
|
||||
Lesson.TYPE_CHANGE -> {
|
||||
lb.annotation.visibility = View.VISIBLE
|
||||
if (lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
|
||||
)
|
||||
}
|
||||
else if (lesson.subjectId != lesson.oldSubjectId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
lesson.oldSubjectName ?: "?"
|
||||
)
|
||||
}
|
||||
else if (lesson.teacherId != lesson.oldTeacherId) {
|
||||
lb.annotation.setText(
|
||||
R.string.timetable_lesson_change_format,
|
||||
lesson.oldTeacherName ?: "?"
|
||||
)
|
||||
}
|
||||
else {
|
||||
lb.annotation.setText(R.string.timetable_lesson_change)
|
||||
}
|
||||
|
||||
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||
getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The day view needs the event time ranges in the start minute/end minute format,
|
||||
// so calculate those here
|
||||
val startMinute = 60 * (lesson.displayStartTime?.hour ?: 0) + (lesson.displayStartTime?.minute ?: 0)
|
||||
val endMinute = startMinute + 45
|
||||
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
|
||||
}
|
||||
|
||||
b.day.setEventViews(eventViews, eventTimeRanges)
|
||||
b.dayScroll.scrollTo(0, b.day.firstEventTop)
|
||||
}
|
||||
}
|
@ -182,6 +182,10 @@ public class Date implements Comparable<Date> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLeap() {
|
||||
return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
|
||||
}
|
||||
|
||||
public static Date getToday()
|
||||
{
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#000000" />
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
<item android:state_focused="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
<item android:state_selected="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||
</selector>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke android:color="@color/dividerColor" android:width="1dp" />
|
||||
<solid android:color="#DCDCDC" />
|
||||
</shape>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:shape="rectangle">
|
||||
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
|
||||
<solid android:color="#2196f3" tools:color="?timetable_lesson_cancelled_color" />
|
||||
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape android:shape="rectangle"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/colorSurface_4dp" />
|
||||
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape android:shape="rectangle"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="4dp" />
|
||||
<stroke android:width="1dp" android:color="#1e000000" />
|
||||
</shape>
|
45
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
45
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorSurface"
|
||||
style="@style/Widget.MaterialComponents.AppBarLayout.Surface">
|
||||
|
||||
<com.nshmura.recyclertablayout.RecyclerTabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:background="@color/colorSurface_6dp"
|
||||
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab"
|
||||
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||
app:rtl_tabMinWidth="90dp"
|
||||
app:rtl_tabMaxWidth="300dp"
|
||||
app:rtl_tabSelectedTextColor="?colorPrimary"
|
||||
app:rtl_tabPaddingStart="16dp"
|
||||
app:rtl_tabPaddingEnd="16dp"
|
||||
app:rtl_tabPaddingTop="12dp"
|
||||
app:rtl_tabPaddingBottom="12dp"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
40
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
40
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello_blank_fragment"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/dayScroll"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.linkedin.android.tachyon.DayView
|
||||
android:id="@+id/day"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
app:dividerHeight="1dp"
|
||||
app:endHour="18"
|
||||
app:eventMargin="2dp"
|
||||
app:halfHourDividerColor="#e0e0e0"
|
||||
app:halfHourHeight="60dp"
|
||||
app:hourDividerColor="#b0b0b0"
|
||||
app:hourLabelMarginEnd="10dp"
|
||||
app:hourLabelWidth="40dp"
|
||||
app:startHour="5" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2019 LinkedIn Corporation -->
|
||||
<!-- All Rights Reserved. -->
|
||||
<!-- -->
|
||||
<!-- Licensed under the BSD 2-Clause License (the "License"). See License in the project root -->
|
||||
<!-- for license information. -->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
style="@style/Base.TextAppearance.AppCompat.Small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAlignment="viewEnd"
|
||||
tools:text="1 PM" />
|
105
app/src/main/res/layout/timetable_lesson.xml
Normal file
105
app/src/main/res/layout/timetable_lesson.xml
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout 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">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:colorBackground"
|
||||
android:foreground="@drawable/bg_rounded_ripple_4dp"
|
||||
tools:padding="32dp">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout_height="90dp"
|
||||
android:background="?timetable_lesson_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/annotation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/timetable_lesson_annotation"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:textColor="#000"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
android:text="@string/timetable_lesson_cancelled"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subjectName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:maxLines="2"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="pracownia urządzeń techniki komputerowej nazwa przedmiotu jest bardzo długa i pewnie się tu nie zmieści w ogóle" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/attendanceIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_weight="0"
|
||||
tools:srcCompat="@sample/check"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView4"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_weight="0"
|
||||
app:srcCompat="@drawable/bg_circle"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="bottom"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailsFirst"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="8:10 - 8:55 • 015 językowa → 016 językowa" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailsSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
android:textSize="12sp"
|
||||
tools:text="Paweł Informatyczny • 2b3T n1" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</layout>
|
@ -3,4 +3,8 @@
|
||||
<attr name="colorSection" format="color" />
|
||||
<attr name="cardBackgroundDimmed" format="color" />
|
||||
<attr name="cardBackgroundHighlight" format="color" />
|
||||
<attr name="timetable_lesson_bg" format="reference" />
|
||||
<attr name="timetable_lesson_cancelled_color" format="color" />
|
||||
<attr name="timetable_lesson_change_color" format="color" />
|
||||
<attr name="timetable_lesson_shifted_color" format="color" />
|
||||
</resources>
|
@ -985,4 +985,10 @@
|
||||
<string name="app_manager_open_failed">Nie udało się otworzyć ustawień</string>
|
||||
<string name="edziennik_notification_api_notify_title">Tworzenie powiadomień</string>
|
||||
<string name="login_librus_captcha_title">Librus - logowanie</string>
|
||||
<string name="timetable_today">Dzisiaj</string>
|
||||
<string name="timetable_lesson_cancelled">Lekcja odwołana</string>
|
||||
<string name="timetable_lesson_change">Zastępstwo</string>
|
||||
<string name="timetable_lesson_change_format">Zastępstwo: zamiast %s</string>
|
||||
<string name="timetable_lesson_shifted_same_day">Lekcja przeniesiona na godz. %s</string>
|
||||
<string name="timetable_lesson_shifted_other_day">Lekcja przeniesiona na %s, godz. %s</string>
|
||||
</resources>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources>
|
||||
|
||||
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
@ -95,6 +95,11 @@
|
||||
<item name="mal_color_secondary">?android:textColorSecondary</item>
|
||||
<item name="mal_card_background">?colorSurface</item>
|
||||
<item name="mal_divider_color">@color/dividerColor</item>
|
||||
|
||||
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_light</item>
|
||||
<item name="timetable_lesson_cancelled_color">#9f9f9f</item>
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_color">#4caf50</item>
|
||||
</style>
|
||||
<style name="AppTheme.Dark" parent="NavView.Dark">
|
||||
<item name="colorPrimary">#64b5f6</item>
|
||||
@ -119,6 +124,11 @@
|
||||
<item name="mal_color_secondary">@color/secondaryTextDark</item>
|
||||
<item name="mal_card_background">?colorSurface</item>
|
||||
<item name="mal_divider_color">@color/dividerColor</item>
|
||||
|
||||
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_dark</item>
|
||||
<item name="timetable_lesson_cancelled_color">#838383</item>
|
||||
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||
<item name="timetable_lesson_shifted_color">#4caf50</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user