forked from github/szkolny
[UI] Add Notes feature. (#99)
* [DB] Add Room schema export location. * [DB] Add Note entity and migration 96. * Add correct database schema * [Notes] Implement basic note list UI. * [DB] Implement Noteable in Full entities. Add note relation and filtering. * [Notes] Make Note searchable. * [UI] Disable onClick listeners in adapters when null. * [UI] Implement showing note list in dialog. * [UI] Update note dialogs UI. * [Notes] Add note details dialog. * [Notes] Extract note dialogs header into a separate layout. * [Notes] Add note editor dialog. * [Notes] Show note icons in dialogs and lists. * [Notes] Add showing substitute text. * [Notes] Add replacing notes icon. * [Notes] Add sharing and receiving notes. * [Notes] Add notes list UI fragment. * [Notes] Implement adding notes without owner. * [Notes] Add color names. * [Notes] Add notes card on home screen. * [Notes] Add notes card migration.
This commit is contained in:
parent
8745d7d526
commit
7c925cb88a
@ -31,6 +31,12 @@ android {
|
||||
cppFlags "-std=c++11"
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
arguments {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
2293
app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/97.json
Normal file
2293
app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/97.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@ import okhttp3.OkHttpClient
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
@ -66,6 +67,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
var devMode = false
|
||||
}
|
||||
|
||||
val api by lazy { SzkolnyApi(this) }
|
||||
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
|
||||
val userActionManager by lazy { UserActionManager(this) }
|
||||
val gradesManager by lazy { GradesManager(this) }
|
||||
@ -77,6 +79,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
val availabilityManager by lazy { AvailabilityManager(this) }
|
||||
val textStylingManager by lazy { TextStylingManager(this) }
|
||||
val messageManager by lazy { MessageManager(this) }
|
||||
val noteManager by lazy { NoteManager(this) }
|
||||
|
||||
val db
|
||||
get() = App.db
|
||||
|
@ -4,8 +4,11 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.graphics.Paint
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.BindingAdapter
|
||||
import pl.szczodrzynski.edziennik.ext.dp
|
||||
|
||||
object Binding {
|
||||
@JvmStatic
|
||||
@ -17,4 +20,64 @@ object Binding {
|
||||
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:isVisible")
|
||||
fun isVisible(view: View, isVisible: Boolean) {
|
||||
view.isVisible = isVisible
|
||||
}
|
||||
|
||||
private fun resizeDrawable(textView: TextView, index: Int, size: Int) {
|
||||
val drawables = textView.compoundDrawables
|
||||
drawables[index]?.setBounds(0, 0, size, size)
|
||||
textView.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3])
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableLeftAutoSize")
|
||||
fun drawableLeftAutoSize(textView: TextView, enable: Boolean) = resizeDrawable(
|
||||
textView,
|
||||
index = 0,
|
||||
size = textView.textSize.toInt(),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableRightAutoSize")
|
||||
fun drawableRightAutoSize(textView: TextView, enable: Boolean) = resizeDrawable(
|
||||
textView,
|
||||
index = 2,
|
||||
size = textView.textSize.toInt(),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableLeftSize")
|
||||
fun drawableLeftSize(textView: TextView, sizeDp: Int) = resizeDrawable(
|
||||
textView,
|
||||
index = 0,
|
||||
size = sizeDp.dp,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableTopSize")
|
||||
fun drawableTopSize(textView: TextView, sizeDp: Int) = resizeDrawable(
|
||||
textView,
|
||||
index = 1,
|
||||
size = sizeDp.dp,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableRightSize")
|
||||
fun drawableRightSize(textView: TextView, sizeDp: Int) = resizeDrawable(
|
||||
textView,
|
||||
index = 2,
|
||||
size = sizeDp.dp,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:drawableBottomSize")
|
||||
fun drawableBottomSize(textView: TextView, sizeDp: Int) = resizeDrawable(
|
||||
textView,
|
||||
index = 3,
|
||||
size = sizeDp.dp,
|
||||
)
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ import pl.szczodrzynski.edziennik.ui.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.ui.messages.compose.MessagesComposeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.messages.single.MessageFragment
|
||||
import pl.szczodrzynski.edziennik.ui.notes.NotesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.notifications.NotificationsListFragment
|
||||
import pl.szczodrzynski.edziennik.ui.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.settings.SettingsFragment
|
||||
@ -118,6 +119,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
const val DRAWER_ITEM_NOTIFICATIONS = 20
|
||||
const val DRAWER_ITEM_MORE = 21
|
||||
const val DRAWER_ITEM_TEACHERS = 22
|
||||
const val DRAWER_ITEM_NOTES = 23
|
||||
const val DRAWER_ITEM_SETTINGS = 101
|
||||
const val DRAWER_ITEM_DEBUG = 102
|
||||
|
||||
@ -134,6 +136,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
val list: MutableList<NavTarget> = mutableListOf()
|
||||
val moreList: MutableList<NavTarget> = mutableListOf()
|
||||
|
||||
moreList += NavTarget(
|
||||
id = DRAWER_ITEM_NOTES,
|
||||
name = R.string.menu_notes,
|
||||
fragmentClass = NotesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon3.cmd_text_box_multiple_outline)
|
||||
.isStatic(true)
|
||||
|
||||
moreList += NavTarget(DRAWER_ITEM_TEACHERS,
|
||||
R.string.menu_teachers,
|
||||
TeachersListFragment::class)
|
||||
|
@ -18,7 +18,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
|
||||
companion object {
|
||||
const val DATA_VERSION = 2
|
||||
const val DATA_VERSION = 3
|
||||
}
|
||||
|
||||
private val job = Job()
|
||||
|
@ -7,6 +7,8 @@ package pl.szczodrzynski.edziennik.config.utils
|
||||
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCardModel
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
|
||||
|
||||
@ -33,5 +35,13 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
||||
|
||||
dataVersion = 2
|
||||
}
|
||||
|
||||
if (dataVersion < 3) {
|
||||
ui.homeCards = ui.homeCards.toMutableList().also {
|
||||
it.add(HomeCardModel(config.profileId, HomeCard.CARD_NOTES))
|
||||
}
|
||||
|
||||
dataVersion = 3
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -23,10 +23,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureIntercep
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.ext.keys
|
||||
import pl.szczodrzynski.edziennik.ext.md5
|
||||
@ -199,7 +196,12 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>, lastSyncTime: Long): List<EventFull> {
|
||||
fun getEvents(
|
||||
profiles: List<Profile>,
|
||||
notifications: List<Notification>,
|
||||
blacklistedIds: List<Long>,
|
||||
lastSyncTime: Long,
|
||||
): Pair<List<EventFull>, List<Note>> {
|
||||
val teams = app.db.teamDao().allNow
|
||||
|
||||
val users = profiles.mapNotNull { profile ->
|
||||
@ -225,7 +227,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
lastSync = lastSyncTime,
|
||||
notifications = notifications.map { ServerSyncRequest.Notification(it.profileName ?: "", it.type, it.text) }
|
||||
)).execute()
|
||||
val (events, hasBrowsers) = parseResponse(response, updateDeviceHash = true)
|
||||
val (events, notes, hasBrowsers) = parseResponse(response, updateDeviceHash = true)
|
||||
|
||||
hasBrowsers?.let {
|
||||
app.config.sync.webPushEnabled = it
|
||||
@ -237,6 +239,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
|
||||
val eventList = mutableListOf<EventFull>()
|
||||
val noteList = mutableListOf<Note>()
|
||||
|
||||
events.forEach { event ->
|
||||
// skip blacklisted events
|
||||
@ -247,9 +250,13 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
if (event.color == -1)
|
||||
event.color = null
|
||||
|
||||
val eventSharedBy = event.sharedBy
|
||||
|
||||
// create the event for every matching team and profile
|
||||
teams.filter { it.code == event.teamCode }.onEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
|
||||
eventList += EventFull(event).apply {
|
||||
profileId = team.profileId
|
||||
@ -261,42 +268,107 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
if (profile.userCode == event.sharedBy) {
|
||||
sharedBy = "self"
|
||||
addedManually = true
|
||||
} else {
|
||||
sharedBy = eventSharedBy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventList
|
||||
notes.forEach { note ->
|
||||
val noteSharedBy = note.sharedBy
|
||||
|
||||
// create the note for every matching team and profile
|
||||
teams.filter { it.code == note.teamCode }.onEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
note.profileId = team.profileId
|
||||
if (profile.userCode == note.sharedBy) {
|
||||
note.sharedBy = "self"
|
||||
} else {
|
||||
note.sharedBy = noteSharedBy
|
||||
}
|
||||
|
||||
if (app.noteManager.hasValidOwner(note))
|
||||
noteList += note
|
||||
}
|
||||
}
|
||||
return eventList to noteList
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun shareEvent(event: EventFull) {
|
||||
val profile = app.db.profileDao().getByIdNow(event.profileId)
|
||||
?: throw NullPointerException("Profile is not found")
|
||||
val team = app.db.teamDao().getByIdNow(event.profileId, event.teamId)
|
||||
?: throw NullPointerException("Team is not found")
|
||||
|
||||
val response = api.shareEvent(EventShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
sharedByName = event.sharedByName,
|
||||
shareTeamCode = team.code,
|
||||
event = event
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
userCode = profile.userCode,
|
||||
studentNameLong = profile.studentNameLong,
|
||||
shareTeamCode = team.code,
|
||||
event = event
|
||||
)).execute()
|
||||
parseResponse(response, updateDeviceHash = true)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun unshareEvent(event: Event) {
|
||||
val profile = app.db.profileDao().getByIdNow(event.profileId)
|
||||
?: throw NullPointerException("Profile is not found")
|
||||
val team = app.db.teamDao().getByIdNow(event.profileId, event.teamId)
|
||||
?: throw NullPointerException("Team is not found")
|
||||
|
||||
val response = api.shareEvent(EventShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
sharedByName = event.sharedByName,
|
||||
unshareTeamCode = team.code,
|
||||
eventId = event.id
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
userCode = profile.userCode,
|
||||
studentNameLong = profile.studentNameLong,
|
||||
unshareTeamCode = team.code,
|
||||
eventId = event.id
|
||||
)).execute()
|
||||
parseResponse(response, updateDeviceHash = true)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun shareNote(note: Note) {
|
||||
val profile = app.db.profileDao().getByIdNow(note.profileId)
|
||||
?: throw NullPointerException("Profile is not found")
|
||||
val team = app.db.teamDao().getClassNow(note.profileId)
|
||||
?: throw NullPointerException("TeamClass is not found")
|
||||
|
||||
val response = api.shareNote(NoteShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
userCode = profile.userCode,
|
||||
studentNameLong = profile.studentNameLong,
|
||||
shareTeamCode = team.code,
|
||||
note = note,
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun unshareNote(note: Note) {
|
||||
val profile = app.db.profileDao().getByIdNow(note.profileId)
|
||||
?: throw NullPointerException("Profile is not found")
|
||||
val team = app.db.teamDao().getClassNow(note.profileId)
|
||||
?: throw NullPointerException("TeamClass is not found")
|
||||
|
||||
val response = api.shareNote(NoteShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
device = getDevice(),
|
||||
userCode = profile.userCode,
|
||||
studentNameLong = profile.studentNameLong,
|
||||
unshareTeamCode = team.code,
|
||||
noteId = note.id,
|
||||
)).execute()
|
||||
parseResponse(response)
|
||||
}
|
||||
|
||||
/*fun eventEditRequest(requesterName: String, event: Event): ApiResponse<Nothing>? {
|
||||
|
||||
}*/
|
||||
|
@ -18,6 +18,9 @@ interface SzkolnyService {
|
||||
@POST("share")
|
||||
fun shareEvent(@Body request: EventShareRequest): Call<ApiResponse<Unit>>
|
||||
|
||||
@POST("share")
|
||||
fun shareNote(@Body request: NoteShareRequest): Call<ApiResponse<Unit>>
|
||||
|
||||
@POST("webPush")
|
||||
fun webPush(@Body request: WebPushRequest): Call<ApiResponse<WebPushResponse>>
|
||||
|
||||
|
@ -12,8 +12,9 @@ data class EventShareRequest (
|
||||
|
||||
val action: String = "event",
|
||||
|
||||
/* If null, the server shows an error */
|
||||
val sharedByName: String?,
|
||||
val userCode: String,
|
||||
val studentNameLong: String,
|
||||
|
||||
val shareTeamCode: String? = null,
|
||||
val unshareTeamCode: String? = null,
|
||||
val requesterName: String? = null,
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-26.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny.request
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
|
||||
data class NoteShareRequest (
|
||||
val deviceId: String,
|
||||
val device: Device? = null,
|
||||
|
||||
val action: String = "note",
|
||||
|
||||
val userCode: String,
|
||||
val studentNameLong: String,
|
||||
|
||||
val shareTeamCode: String? = null,
|
||||
val unshareTeamCode: String? = null,
|
||||
val requesterName: String? = null,
|
||||
|
||||
val noteId: Long? = null,
|
||||
val note: Note? = null
|
||||
)
|
||||
|
@ -4,9 +4,11 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny.response
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
|
||||
data class ServerSyncResponse(
|
||||
val events: List<EventFull>,
|
||||
val notes: List<Note>,
|
||||
val hasBrowsers: Boolean? = null
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
||||
*/
|
||||
fun run(lastSyncTime: Long, markAsSeen: Boolean = false): Int {
|
||||
val blacklistedIds = app.db.eventDao().blacklistedIds
|
||||
val events = try {
|
||||
val (events, notes) = try {
|
||||
api.getEvents(profiles, notifications, blacklistedIds, lastSyncTime)
|
||||
} catch (e: SzkolnyApiException) {
|
||||
if (e.toErrorCode() == ERROR_API_INVALID_SIGNATURE)
|
||||
@ -40,6 +40,10 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
||||
|
||||
app.config.sync.lastAppSync = System.currentTimeMillis()
|
||||
|
||||
if (notes.isNotEmpty()) {
|
||||
app.db.noteDao().addAll(notes)
|
||||
}
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
val today = Date.getToday()
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
@ -54,6 +58,6 @@ class AppSync(val app: App, val notifications: MutableList<Notification>, val pr
|
||||
})
|
||||
return app.db.eventDao().upsertAll(events).size
|
||||
}
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
|
||||
val notifications = Notifications(app, notificationList, profiles)
|
||||
notifications.run()
|
||||
|
||||
val appSyncProfiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
||||
val appSyncProfiles = profiles.filter { it.canShare }
|
||||
// App Sync conditions:
|
||||
// - every 24 hours && any profile is registered
|
||||
// - if there are new notifications && any browser is paired
|
||||
|
@ -42,8 +42,9 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
||||
ConfigEntry::class,
|
||||
LibrusLesson::class,
|
||||
TimetableManual::class,
|
||||
Note::class,
|
||||
Metadata::class
|
||||
], version = 96)
|
||||
], version = 97)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
@ -82,6 +83,7 @@ abstract class AppDb : RoomDatabase() {
|
||||
abstract fun configDao(): ConfigDao
|
||||
abstract fun librusLessonDao(): LibrusLessonDao
|
||||
abstract fun timetableManualDao(): TimetableManualDao
|
||||
abstract fun noteDao(): NoteDao
|
||||
abstract fun metadataDao(): MetadataDao
|
||||
|
||||
companion object {
|
||||
@ -182,6 +184,7 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration94(),
|
||||
Migration95(),
|
||||
Migration96(),
|
||||
Migration97(),
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,19 @@ import pl.szczodrzynski.edziennik.data.db.entity.Keepable
|
||||
|
||||
@Dao
|
||||
interface BaseDao<T : Keepable, F : T> {
|
||||
@Transaction
|
||||
@RawQuery
|
||||
fun getRaw(query: SupportSQLiteQuery): LiveData<List<F>>
|
||||
fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query))
|
||||
@Transaction
|
||||
@RawQuery
|
||||
fun getOne(query: SupportSQLiteQuery): LiveData<F?>
|
||||
fun getOne(query: String) = getOne(SimpleSQLiteQuery(query))
|
||||
@Transaction
|
||||
@RawQuery
|
||||
fun getRawNow(query: SupportSQLiteQuery): List<F>
|
||||
fun getRawNow(query: String) = getRawNow(SimpleSQLiteQuery(query))
|
||||
@Transaction
|
||||
@RawQuery
|
||||
fun getOneNow(query: SupportSQLiteQuery): F?
|
||||
fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query))
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
|
||||
@Dao
|
||||
interface NoteDao {
|
||||
companion object {
|
||||
private const val ORDER_BY = "ORDER BY addedDate DESC"
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(note: Note)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addAll(noteList: List<Note>)
|
||||
|
||||
@Delete
|
||||
fun delete(note: Note)
|
||||
|
||||
@Query("DELETE FROM notes WHERE profileId = :profileId AND noteId = :noteId")
|
||||
fun remove(profileId: Int, noteId: Long)
|
||||
|
||||
@Query("SELECT * FROM notes WHERE profileId = :profileId AND noteId = :noteId $ORDER_BY")
|
||||
fun get(profileId: Int, noteId: Long): LiveData<Note?>
|
||||
|
||||
@Query("SELECT * FROM notes WHERE profileId = :profileId AND noteId = :noteId $ORDER_BY")
|
||||
fun getNow(profileId: Int, noteId: Long): Note?
|
||||
|
||||
@Query("SELECT * FROM notes WHERE profileId = :profileId $ORDER_BY")
|
||||
fun getAll(profileId: Int): LiveData<List<Note>>
|
||||
|
||||
@Query("SELECT * FROM notes WHERE profileId = :profileId AND noteOwnerType = :ownerType AND noteOwnerId = :ownerId $ORDER_BY")
|
||||
fun getAllFor(profileId: Int, ownerType: Note.OwnerType, ownerId: Long): LiveData<List<Note>>
|
||||
|
||||
@Query("SELECT * FROM notes WHERE profileId = :profileId AND noteOwnerType IS NULL $ORDER_BY")
|
||||
fun getAllNoOwner(profileId: Int): LiveData<List<Note>>
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.*
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.ui.search.Searchable
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
|
||||
@Entity(
|
||||
tableName = "notes",
|
||||
indices = [
|
||||
Index(value = ["profileId", "noteOwnerType", "noteOwnerId"]),
|
||||
],
|
||||
)
|
||||
data class Note(
|
||||
var profileId: Int,
|
||||
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "noteId")
|
||||
val id: Long,
|
||||
|
||||
@ColumnInfo(name = "noteOwnerType")
|
||||
val ownerType: OwnerType?,
|
||||
@ColumnInfo(name = "noteOwnerId")
|
||||
val ownerId: Long?,
|
||||
@ColumnInfo(name = "noteReplacesOriginal")
|
||||
val replacesOriginal: Boolean = false,
|
||||
|
||||
@ColumnInfo(name = "noteTopic")
|
||||
val topic: String?,
|
||||
@ColumnInfo(name = "noteBody")
|
||||
val body: String,
|
||||
@ColumnInfo(name = "noteColor")
|
||||
val color: Long?,
|
||||
|
||||
@ColumnInfo(name = "noteSharedBy")
|
||||
var sharedBy: String? = null,
|
||||
@ColumnInfo(name = "noteSharedByName")
|
||||
val sharedByName: String? = null,
|
||||
|
||||
val addedDate: Long = System.currentTimeMillis(),
|
||||
) : Searchable<Note> {
|
||||
enum class OwnerType(
|
||||
val isShareable: Boolean,
|
||||
val canReplace: Boolean,
|
||||
) {
|
||||
/**
|
||||
* The [NONE] type is only for usage in the UI and should not be saved.
|
||||
*/
|
||||
NONE(isShareable = true, canReplace = false),
|
||||
EVENT(isShareable = true, canReplace = true),
|
||||
DAY(isShareable = true, canReplace = false),
|
||||
LESSON(isShareable = true, canReplace = true),
|
||||
MESSAGE(isShareable = true, canReplace = false),
|
||||
EVENT_SUBJECT(isShareable = true, canReplace = false),
|
||||
LESSON_SUBJECT(isShareable = true, canReplace = false),
|
||||
GRADE(isShareable = false, canReplace = true),
|
||||
ATTENDANCE(isShareable = false, canReplace = true),
|
||||
BEHAVIOR(isShareable = false, canReplace = false),
|
||||
ANNOUNCEMENT(isShareable = true, canReplace = false),
|
||||
}
|
||||
|
||||
enum class Color(val value: Long?, val stringRes: Int) {
|
||||
NONE(null, R.string.color_none),
|
||||
RED(0xffff1744, R.string.color_red),
|
||||
ORANGE(0xffff9100, R.string.color_orange),
|
||||
YELLOW(0xffffea00, R.string.color_yellow),
|
||||
GREEN(0xff00c853, R.string.color_green),
|
||||
TEAL(0xff00bfa5, R.string.color_teal),
|
||||
BLUE(0xff0091ea, R.string.color_blue),
|
||||
DARK_BLUE(0xff304ffe, R.string.color_dark_blue),
|
||||
PURPLE(0xff6200ea, R.string.color_purple),
|
||||
PINK(0xffd500f9, R.string.color_pink),
|
||||
BROWN(0xff795548, R.string.color_brown),
|
||||
GREY(0xff9e9e9e, R.string.color_grey),
|
||||
BLACK(0xff000000, R.string.color_black),
|
||||
}
|
||||
|
||||
val isShared
|
||||
get() = sharedBy != null && sharedByName != null
|
||||
val canEdit
|
||||
get() = !isShared || sharedBy == "self"
|
||||
|
||||
// used when receiving notes
|
||||
@Ignore
|
||||
var teamCode: String? = null
|
||||
|
||||
@delegate:Ignore
|
||||
@delegate:Transient
|
||||
val topicHtml by lazy {
|
||||
topic?.let {
|
||||
BetterHtml.fromHtml(context = null, it, nl2br = true)
|
||||
}
|
||||
}
|
||||
|
||||
@delegate:Ignore
|
||||
@delegate:Transient
|
||||
val bodyHtml by lazy {
|
||||
BetterHtml.fromHtml(context = null, body, nl2br = true)
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Transient
|
||||
var isCategoryItem = false
|
||||
|
||||
@Ignore
|
||||
@Transient
|
||||
override var searchPriority = 0
|
||||
|
||||
@Ignore
|
||||
@Transient
|
||||
override var searchHighlightText: String? = null
|
||||
|
||||
@delegate:Ignore
|
||||
@delegate:Transient
|
||||
override val searchKeywords by lazy {
|
||||
if (isCategoryItem)
|
||||
return@lazy emptyList()
|
||||
listOf(
|
||||
listOf(topicHtml?.toString(), bodyHtml.toString()),
|
||||
listOf(sharedByName),
|
||||
)
|
||||
}
|
||||
|
||||
override fun compareTo(other: Searchable<*>): Int {
|
||||
if (other !is Note)
|
||||
return 0
|
||||
val order = ownerType?.ordinal ?: -1
|
||||
val otherOrder = other.ownerType?.ordinal ?: -1
|
||||
return when {
|
||||
// custom ascending sorting
|
||||
order > otherOrder -> 1
|
||||
order < otherOrder -> -1
|
||||
// all category elements stay at their original position
|
||||
isCategoryItem -> 0
|
||||
other.isCategoryItem -> 0
|
||||
// ascending sorting
|
||||
searchPriority > other.searchPriority -> 1
|
||||
searchPriority < other.searchPriority -> -1
|
||||
// descending sorting
|
||||
addedDate > other.addedDate -> -1
|
||||
addedDate < other.addedDate -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-17.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
interface Noteable {
|
||||
|
||||
fun getNoteType(): Note.OwnerType
|
||||
fun getNoteOwnerProfileId(): Int
|
||||
fun getNoteOwnerId(): Long
|
||||
|
||||
var notes: MutableList<Note>
|
||||
|
||||
fun filterNotes() {
|
||||
val type = getNoteType()
|
||||
val profileId = getNoteOwnerProfileId()
|
||||
notes.removeAll {
|
||||
it.profileId != profileId || it.ownerType != type
|
||||
}
|
||||
}
|
||||
|
||||
fun hasNotes() = notes.isNotEmpty()
|
||||
fun hasReplacingNotes() = notes.any { it.replacesOriginal }
|
||||
|
||||
fun getNoteSubstituteText(showNotes: Boolean): CharSequence? {
|
||||
if (!showNotes)
|
||||
return null
|
||||
val note = notes.firstOrNull {
|
||||
it.replacesOriginal
|
||||
}
|
||||
return note?.topicHtml ?: note?.bodyHtml
|
||||
}
|
||||
}
|
@ -56,6 +56,7 @@ data class Notification(
|
||||
const val TYPE_FEEDBACK_MESSAGE = 16
|
||||
const val TYPE_AUTO_ARCHIVING = 17
|
||||
const val TYPE_TEACHER_ABSENCE = 19
|
||||
const val TYPE_NEW_SHARED_NOTE = 20
|
||||
|
||||
fun buildId(profileId: Int, type: Int, itemId: Long): Long {
|
||||
return 1000000000000 + profileId*10000000000 + type*100000000 + itemId;
|
||||
@ -112,6 +113,7 @@ data class Notification(
|
||||
TYPE_NEW_ATTENDANCE -> CommunityMaterial.Icon.cmd_calendar_remove_outline
|
||||
TYPE_LUCKY_NUMBER -> CommunityMaterial.Icon.cmd_emoticon_excited_outline
|
||||
TYPE_NEW_ANNOUNCEMENT -> CommunityMaterial.Icon.cmd_bullhorn_outline
|
||||
TYPE_NEW_SHARED_NOTE -> CommunityMaterial.Icon3.cmd_playlist_edit
|
||||
else -> CommunityMaterial.Icon.cmd_bell_ring_outline
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +145,9 @@ open class Profile(
|
||||
else -> "unknown"
|
||||
}
|
||||
|
||||
val canShare
|
||||
get() = registration == REGISTRATION_ENABLED && !archived
|
||||
|
||||
override fun getImageDrawable(context: Context): Drawable {
|
||||
if (archived) {
|
||||
return context.getDrawableFromRes(pl.szczodrzynski.edziennik.R.drawable.profile_archived).also {
|
||||
|
@ -3,7 +3,10 @@
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Announcement
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class AnnouncementFull(
|
||||
@ -16,10 +19,16 @@ class AnnouncementFull(
|
||||
subject, text,
|
||||
startDate, endDate,
|
||||
teacherId, addedDate
|
||||
) {
|
||||
), Noteable {
|
||||
var teacherName: String? = null
|
||||
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
|
||||
@Relation(parentColumn = "announcementId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.ANNOUNCEMENT
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -3,7 +3,10 @@
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@ -17,7 +20,7 @@ class AttendanceFull(
|
||||
baseType, typeName, typeShort, typeSymbol, typeColor,
|
||||
date, startTime, semester,
|
||||
teacherId, subjectId, addedDate
|
||||
) {
|
||||
), Noteable {
|
||||
var teacherName: String? = null
|
||||
var subjectLongName: String? = null
|
||||
var subjectShortName: String? = null
|
||||
@ -26,4 +29,10 @@ class AttendanceFull(
|
||||
var seen = false
|
||||
get() = field || baseType == TYPE_PRESENT
|
||||
var notified = false
|
||||
|
||||
@Relation(parentColumn = "attendanceId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.ATTENDANCE
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -4,8 +4,11 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.ui.search.Searchable
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -19,7 +22,7 @@ class EventFull(
|
||||
profileId, id, date, time,
|
||||
topic, color, type,
|
||||
teacherId, subjectId, teamId, addedDate
|
||||
), Searchable<EventFull> {
|
||||
), Searchable<EventFull>, Noteable {
|
||||
constructor(event: Event, metadata: Metadata? = null) : this(
|
||||
event.profileId, event.id, event.date, event.time,
|
||||
event.topic, event.color, event.type,
|
||||
@ -109,4 +112,10 @@ class EventFull(
|
||||
|
||||
val eventColor
|
||||
get() = color ?: typeColor ?: 0xff2196f3.toInt()
|
||||
|
||||
@Relation(parentColumn = "eventId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.EVENT
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -3,7 +3,10 @@
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
|
||||
class GradeFull(
|
||||
profileId: Int, id: Long, name: String, type: Int,
|
||||
@ -15,7 +18,7 @@ class GradeFull(
|
||||
value, weight, color,
|
||||
category, description, comment,
|
||||
semester, teacherId, subjectId, addedDate
|
||||
) {
|
||||
), Noteable {
|
||||
var teacherName: String? = null
|
||||
var subjectLongName: String? = null
|
||||
var subjectShortName: String? = null
|
||||
@ -23,4 +26,10 @@ class GradeFull(
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
|
||||
@Relation(parentColumn = "gradeId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.GRADE
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -4,15 +4,18 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LessonFull(
|
||||
profileId: Int, id: Long
|
||||
) : Lesson(
|
||||
profileId, id
|
||||
) {
|
||||
), Noteable {
|
||||
var subjectName: String? = null
|
||||
var teacherName: String? = null
|
||||
var teamName: String? = null
|
||||
@ -133,4 +136,10 @@ class LessonFull(
|
||||
// metadata
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
|
||||
@Relation(parentColumn = "id", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.LESSON
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import androidx.room.Ignore
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.ui.search.Searchable
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
|
||||
@ -18,7 +20,7 @@ class MessageFull(
|
||||
profileId, id, type,
|
||||
subject, body, senderId,
|
||||
addedDate
|
||||
), Searchable<MessageFull> {
|
||||
), Searchable<MessageFull>, Noteable {
|
||||
var senderName: String? = null
|
||||
@Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class)
|
||||
var recipients: MutableList<MessageRecipientFull>? = null
|
||||
@ -83,4 +85,10 @@ class MessageFull(
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
|
||||
@Relation(parentColumn = "messageId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.MESSAGE
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
*/
|
||||
package pl.szczodrzynski.edziennik.data.db.full
|
||||
|
||||
import androidx.room.Relation
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
||||
|
||||
class NoticeFull(
|
||||
@ -13,10 +16,16 @@ class NoticeFull(
|
||||
profileId, id, type, semester,
|
||||
text, category, points,
|
||||
teacherId, addedDate
|
||||
) {
|
||||
), Noteable {
|
||||
var teacherName: String? = null
|
||||
|
||||
// metadata
|
||||
var seen = false
|
||||
var notified = false
|
||||
|
||||
@Relation(parentColumn = "noticeId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.BEHAVIOR
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration97 : Migration(96, 97) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// notes
|
||||
database.execSQL("""CREATE TABLE notes (
|
||||
profileId INTEGER NOT NULL,
|
||||
noteId INTEGER NOT NULL,
|
||||
noteOwnerType TEXT,
|
||||
noteOwnerId INTEGER,
|
||||
noteReplacesOriginal INTEGER NOT NULL,
|
||||
noteTopic TEXT,
|
||||
noteBody TEXT NOT NULL,
|
||||
noteColor INTEGER,
|
||||
noteSharedBy TEXT,
|
||||
noteSharedByName TEXT,
|
||||
addedDate INTEGER NOT NULL,
|
||||
PRIMARY KEY(noteId)
|
||||
);""")
|
||||
database.execSQL("CREATE INDEX IF NOT EXISTS index_notes_profileId_noteOwnerType_noteOwnerId ON notes (profileId, noteOwnerType, noteOwnerId);")
|
||||
}
|
||||
}
|
@ -47,6 +47,15 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
message.data.getLong("eventId") ?: return@run,
|
||||
message.data.getString("message") ?: return@run
|
||||
)
|
||||
"sharedNote" -> sharedNote(
|
||||
message.data.getString("shareTeamCode") ?: return@run,
|
||||
message.data.getString("note") ?: return@run,
|
||||
message.data.getString("message") ?: return@run,
|
||||
)
|
||||
"unsharedNote" -> unsharedNote(
|
||||
message.data.getString("unshareTeamCode") ?: return@run,
|
||||
message.data.getLong("noteId") ?: return@run,
|
||||
)
|
||||
"serverMessage",
|
||||
"unpairedBrowser" -> serverMessage(
|
||||
message.data.getString("title") ?: "",
|
||||
@ -119,7 +128,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
val event = Event(
|
||||
profileId = team.profileId,
|
||||
@ -186,7 +195,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (profile.registration != Profile.REGISTRATION_ENABLED)
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
||||
|
||||
@ -209,4 +218,71 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
PostNotifications(app, notificationList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sharedNote(teamCode: String, jsonStr: String, message: String) {
|
||||
val note = app.gson.fromJson(jsonStr, Note::class.java)
|
||||
val noteSharedBy = note.sharedBy
|
||||
val teams = app.db.teamDao().allNow
|
||||
// not used, as the server provides a sharing message
|
||||
//val eventTypes = app.db.eventTypeDao().allNow
|
||||
|
||||
val notes = mutableListOf<Note>()
|
||||
val notificationList = mutableListOf<Notification>()
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
note.profileId = team.profileId
|
||||
if (profile.userCode == note.sharedBy) {
|
||||
note.sharedBy = "self"
|
||||
} else {
|
||||
note.sharedBy = noteSharedBy
|
||||
}
|
||||
|
||||
if (!app.noteManager.hasValidOwner(note))
|
||||
return@forEach
|
||||
|
||||
notes += note
|
||||
|
||||
val hadNote = app.db.noteDao().getNow(note.profileId, note.id) != null
|
||||
// skip creating notifications
|
||||
if (hadNote)
|
||||
return@forEach
|
||||
|
||||
val type = Notification.TYPE_NEW_SHARED_NOTE
|
||||
val notificationFilter = app.config.getFor(note.profileId).sync.notificationFilter
|
||||
|
||||
if (!notificationFilter.contains(type) && note.sharedBy != "self") {
|
||||
val notification = Notification(
|
||||
id = Notification.buildId(note.profileId, type, note.id),
|
||||
title = app.getNotificationTitle(type),
|
||||
text = message,
|
||||
type = type,
|
||||
profileId = profile.id,
|
||||
profileName = profile.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_HOME,
|
||||
addedDate = note.addedDate
|
||||
).addExtra("noteId", note.id)
|
||||
notificationList += notification
|
||||
}
|
||||
}
|
||||
app.db.noteDao().addAll(notes)
|
||||
if (notificationList.isNotEmpty()) {
|
||||
app.db.notificationDao().addAll(notificationList)
|
||||
PostNotifications(app, notificationList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unsharedNote(teamCode: String, noteId: Long) {
|
||||
val teams = app.db.teamDao().allNow
|
||||
|
||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
|
||||
app.db.noteDao().remove(team.profileId, noteId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ fun Context.getNotificationTitle(type: Int): String {
|
||||
Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving
|
||||
Notification.TYPE_TEACHER_ABSENCE -> R.string.notification_type_new_teacher_absence
|
||||
Notification.TYPE_GENERAL -> R.string.notification_type_general
|
||||
Notification.TYPE_NEW_SHARED_NOTE -> R.string.notification_type_new_shared_note
|
||||
else -> R.string.notification_type_general
|
||||
})
|
||||
}
|
||||
|
@ -4,13 +4,22 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ext
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.dialog.MaterialDialogs
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEditTextBinding
|
||||
|
||||
fun MaterialAlertDialogBuilder.input(
|
||||
@ -20,7 +29,7 @@ fun MaterialAlertDialogBuilder.input(
|
||||
value: CharSequence? = null,
|
||||
changeListener: ((editText: TextInputEditText, input: String) -> Boolean)? = null,
|
||||
positiveButton: Int? = null,
|
||||
positiveListener: ((editText: TextInputEditText, input: String) -> Boolean)? = null
|
||||
positiveListener: ((editText: TextInputEditText, input: String) -> Boolean)? = null,
|
||||
): MaterialAlertDialogBuilder {
|
||||
val b = DialogEditTextBinding.inflate(LayoutInflater.from(context), null, false)
|
||||
b.title.text = message
|
||||
@ -43,12 +52,53 @@ fun MaterialAlertDialogBuilder.input(
|
||||
return this
|
||||
}
|
||||
|
||||
fun MaterialAlertDialogBuilder.setTitle(@StringRes resId: Int, vararg formatArgs: Any): MaterialAlertDialogBuilder {
|
||||
fun MaterialAlertDialogBuilder.setTitle(
|
||||
@StringRes resId: Int,
|
||||
vararg formatArgs: Any,
|
||||
): MaterialAlertDialogBuilder {
|
||||
setTitle(context.getString(resId, *formatArgs))
|
||||
return this
|
||||
}
|
||||
|
||||
fun MaterialAlertDialogBuilder.setMessage(@StringRes resId: Int, vararg formatArgs: Any): MaterialAlertDialogBuilder {
|
||||
fun MaterialAlertDialogBuilder.setMessage(
|
||||
@StringRes resId: Int,
|
||||
vararg formatArgs: Any,
|
||||
): MaterialAlertDialogBuilder {
|
||||
setMessage(context.getString(resId, *formatArgs))
|
||||
return this
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun AlertDialog.overlayBackgroundColor(color: Int, alpha: Int) {
|
||||
// this is absolutely horrible
|
||||
val colorSurface16dp = ColorUtils.compositeColors(
|
||||
R.color.colorSurface_16dp.resolveColor(context),
|
||||
MaterialColors.getColor(
|
||||
context,
|
||||
R.attr.colorSurface,
|
||||
javaClass.canonicalName,
|
||||
)
|
||||
)
|
||||
val colorDialogBackground = MaterialColors.layer(colorSurface16dp, color, alpha / 255f)
|
||||
val backgroundInsets = MaterialDialogs.getDialogBackgroundInsets(
|
||||
context,
|
||||
R.attr.alertDialogStyle,
|
||||
R.style.MaterialAlertDialog_MaterialComponents,
|
||||
)
|
||||
val background = MaterialShapeDrawable(
|
||||
context,
|
||||
null,
|
||||
R.attr.alertDialogStyle,
|
||||
R.style.MaterialAlertDialog_MaterialComponents
|
||||
)
|
||||
with(background) {
|
||||
initializeElevationOverlay(context)
|
||||
fillColor = ColorStateList.valueOf(colorDialogBackground)
|
||||
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
setCornerSize(android.R.attr.dialogCornerRadius.resolveDimenAttr(context))
|
||||
}*/
|
||||
elevation = ViewCompat.getElevation(window?.decorView ?: return@with)
|
||||
}
|
||||
val insetDrawable = MaterialDialogs.insetDrawable(background, backgroundInsets)
|
||||
window?.setBackgroundDrawable(insetDrawable)
|
||||
}
|
||||
|
@ -10,10 +10,7 @@ import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.*
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import pl.szczodrzynski.navlib.ImageHolder
|
||||
@ -60,6 +57,12 @@ fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int {
|
||||
context?.theme?.resolveAttribute(this, typedValue, true)
|
||||
return typedValue.data
|
||||
}
|
||||
@Dimension
|
||||
fun @receiver:AttrRes Int.resolveDimenAttr(context: Context): Float {
|
||||
val typedValue = TypedValue()
|
||||
context.theme?.resolveAttribute(this, typedValue, true)
|
||||
return typedValue.getDimension(context.resources.displayMetrics)
|
||||
}
|
||||
@ColorInt
|
||||
fun @receiver:ColorRes Int.resolveColor(context: Context): Int {
|
||||
return ResourcesCompat.getColor(context.resources, this, context.theme)
|
||||
|
@ -26,7 +26,9 @@ import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
@ -36,6 +38,7 @@ class DayDialog(
|
||||
private val profileId: Int,
|
||||
private val date: Date,
|
||||
private val eventTypeId: Long? = null,
|
||||
private val showNotes: Boolean = true,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<DialogDayBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -151,7 +154,7 @@ class DayDialog(
|
||||
showTime = true,
|
||||
showSubject = true,
|
||||
markAsSeen = true,
|
||||
onItemClick = {
|
||||
onEventClick = {
|
||||
EventDetailsDialog(
|
||||
activity,
|
||||
it,
|
||||
@ -171,6 +174,10 @@ class DayDialog(
|
||||
)
|
||||
|
||||
app.db.eventDao().getAllByDate(profileId, date).observe(activity) { events ->
|
||||
events.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
adapter.setAllItems(
|
||||
if (eventTypeId != null)
|
||||
events.filter { it.type == eventTypeId }
|
||||
@ -197,5 +204,15 @@ class DayDialog(
|
||||
b.eventsNoData.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
b.notesButton.isVisible = showNotes
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = date,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
if (showNotes)
|
||||
NoteManager.setLegendText(date, b.legend)
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,13 @@ import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
|
||||
class LessonChangesAdapter(
|
||||
val context: Context,
|
||||
private val onItemClick: ((lesson: LessonFull) -> Unit)? = null
|
||||
private val showNotes: Boolean = true,
|
||||
private val onLessonClick: ((lesson: LessonFull) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<LessonChangesAdapter.ViewHolder>() {
|
||||
|
||||
var items = listOf<LessonFull>()
|
||||
@ -39,8 +41,10 @@ class LessonChangesAdapter(
|
||||
val lesson = items[position]
|
||||
val b = holder.b
|
||||
|
||||
b.root.onClick {
|
||||
onItemClick?.invoke(lesson)
|
||||
if (onLessonClick != null) {
|
||||
b.root.onClick {
|
||||
onLessonClick.invoke(lesson)
|
||||
}
|
||||
}
|
||||
|
||||
val startTime = lesson.displayStartTime ?: return
|
||||
@ -84,7 +88,8 @@ class LessonChangesAdapter(
|
||||
|
||||
|
||||
b.lessonNumber = lesson.displayLessonNumber
|
||||
b.subjectName.text = lesson.displaySubjectName?.let {
|
||||
val lessonText = lesson.getNoteSubstituteText(showNotes) ?: lesson.displaySubjectName
|
||||
b.subjectName.text = lessonText?.let {
|
||||
if (lesson.type == Lesson.TYPE_CANCELLED || lesson.type == Lesson.TYPE_SHIFTED_SOURCE)
|
||||
it.asStrikethroughSpannable().asColoredSpannable(colorSecondary)
|
||||
else
|
||||
@ -93,6 +98,9 @@ class LessonChangesAdapter(
|
||||
b.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
b.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
if (showNotes)
|
||||
NoteManager.prependIcon(lesson, b.subjectName)
|
||||
|
||||
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
|
@ -35,7 +35,7 @@ class LessonChangesDialog(
|
||||
|
||||
val adapter = LessonChangesAdapter(
|
||||
activity,
|
||||
onItemClick = {
|
||||
onLessonClick = {
|
||||
LessonDetailsDialog(
|
||||
activity,
|
||||
it,
|
||||
|
@ -10,6 +10,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@ -24,13 +25,14 @@ public class AnnouncementsAdapter extends RecyclerView.Adapter<AnnouncementsAdap
|
||||
|
||||
private Context context;
|
||||
public List<AnnouncementFull> announcementList;
|
||||
@Nullable
|
||||
public OnAnnouncementClickListener onClick;
|
||||
|
||||
public interface OnAnnouncementClickListener {
|
||||
void onClick(View v, AnnouncementFull announcement);
|
||||
}
|
||||
|
||||
public AnnouncementsAdapter(Context context, List<AnnouncementFull> announcementList, OnAnnouncementClickListener onClick) {
|
||||
public AnnouncementsAdapter(Context context, List<AnnouncementFull> announcementList, @Nullable OnAnnouncementClickListener onClick) {
|
||||
//setHasStableIds(true);
|
||||
|
||||
this.context = context;
|
||||
@ -54,11 +56,12 @@ public class AnnouncementsAdapter extends RecyclerView.Adapter<AnnouncementsAdap
|
||||
AnnouncementFull item = announcementList.get(groupPosition);
|
||||
RowAnnouncementsItemBinding b = holder.b;
|
||||
|
||||
b.announcementsItem.setOnClickListener((v -> {
|
||||
if (onClick != null) {
|
||||
onClick.onClick(v, item);
|
||||
}
|
||||
}));
|
||||
if (onClick != null) {
|
||||
b.announcementsItem.setOnClickListener(v -> onClick.onClick(v, item));
|
||||
}
|
||||
else {
|
||||
b.announcementsItem.setOnClickListener(null);
|
||||
}
|
||||
b.announcementsItemSender.setText(item.getTeacherName());
|
||||
b.announcementsItemTitle.setText(item.getSubject());
|
||||
b.announcementsItemText.setText(item.getText());
|
||||
|
@ -107,6 +107,10 @@ public class AnnouncementsFragment extends Fragment {
|
||||
if (app == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
for (AnnouncementFull it : announcements) {
|
||||
it.filterNotes();
|
||||
}
|
||||
|
||||
if (announcements == null) {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
b.announcementsNoData.setVisibility(View.VISIBLE);
|
||||
|
@ -28,6 +28,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
class AttendanceAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
val type: Int,
|
||||
val showNotes: Boolean = true,
|
||||
var onAttendanceClick: ((item: AttendanceFull) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope {
|
||||
companion object {
|
||||
@ -175,7 +176,10 @@ class AttendanceAdapter(
|
||||
holder is EmptyViewHolder && item is AttendanceEmpty -> holder.onBind(activity, app, item, position, this)
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(onClickListener)
|
||||
if (item !is AttendanceFull || onAttendanceClick != null)
|
||||
holder.itemView.setOnClickListener(onClickListener)
|
||||
else
|
||||
holder.itemView.setOnClickListener(null)
|
||||
}
|
||||
|
||||
fun notifyItemChanged(model: Any) {
|
||||
|
@ -7,17 +7,21 @@ package pl.szczodrzynski.edziennik.ui.attendance
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
|
||||
import pl.szczodrzynski.edziennik.ext.setTintColor
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
|
||||
class AttendanceDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val attendance: AttendanceFull,
|
||||
private val showNotes: Boolean = true,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<AttendanceDetailsDialogBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -48,5 +52,15 @@ class AttendanceDetailsDialog(
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
|
||||
b.notesButton.isVisible = showNotes
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = attendance,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
if (showNotes)
|
||||
NoteManager.setLegendText(attendance, b.legend)
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceListFragment, Observer { items -> this@AttendanceListFragment.launch {
|
||||
if (!isAdded) return@launch
|
||||
|
||||
items.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
// load & configure the adapter
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance(items) }
|
||||
if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
|
@ -75,6 +75,10 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceSummaryFragment, Observer { items -> this@AttendanceSummaryFragment.launch {
|
||||
if (!isAdded) return@launch
|
||||
|
||||
items.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
// load & configure the adapter
|
||||
attendance = items
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance() }
|
||||
|
@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceDayRange
|
||||
import pl.szczodrzynski.edziennik.ui.attendance.models.AttendanceMonth
|
||||
import pl.szczodrzynski.edziennik.ui.grades.models.ExpandableItemModel
|
||||
import pl.szczodrzynski.edziennik.ui.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class AttendanceViewHolder(
|
||||
@ -38,7 +39,10 @@ class AttendanceViewHolder(
|
||||
b.attendanceView.setAttendance(item, manager, bigView = true)
|
||||
|
||||
b.type.text = item.typeName
|
||||
b.subjectName.text = item.subjectLongName ?: item.lessonTopic
|
||||
b.subjectName.text = item.getNoteSubstituteText(adapter.showNotes) ?: item.subjectLongName
|
||||
?: item.lessonTopic
|
||||
if (adapter.showNotes)
|
||||
NoteManager.prependIcon(item, b.subjectName)
|
||||
b.dateTime.text = listOf(
|
||||
Week.getFullDayName(item.date.weekDay),
|
||||
item.date.formattedStringShort,
|
||||
|
@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_APP_CRASH
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.ext.resolveColor
|
||||
import pl.szczodrzynski.edziennik.utils.Themes.appTheme
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
@ -116,7 +115,7 @@ class CrashActivity : AppCompatActivity(), CoroutineScope {
|
||||
content = content.replace(packageName.toRegex(), "<font color='#4caf50'>$packageName</font>")
|
||||
content = content.replace("\n".toRegex(), "<br>")
|
||||
contentPlain += "\n" + Build.MANUFACTURER + "\n" + Build.BRAND + "\n" + Build.MODEL + "\n" + Build.DEVICE + "\n"
|
||||
if (app.profile.registration == Profile.REGISTRATION_ENABLED) {
|
||||
if (!app.profile.canShare) {
|
||||
contentPlain += "U: " + app.profile.userCode + "\nS: " + app.profile.studentNameLong + "\n"
|
||||
}
|
||||
contentPlain += BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE
|
||||
|
@ -9,7 +9,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
|
||||
import pl.szczodrzynski.edziennik.ext.onChange
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
|
||||
@ -40,9 +39,9 @@ class AgendaConfigDialog(
|
||||
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT
|
||||
|
||||
b.eventSharingEnabled.isChecked =
|
||||
app.profile.enableSharedEvents && app.profile.registration == REGISTRATION_ENABLED
|
||||
app.profile.enableSharedEvents && app.profile.canShare
|
||||
b.eventSharingEnabled.onChange { _, isChecked ->
|
||||
if (isChecked && app.profile.registration != REGISTRATION_ENABLED) {
|
||||
if (isChecked && !app.profile.canShare) {
|
||||
b.eventSharingEnabled.isChecked = false
|
||||
val dialog = RegistrationConfigDialog(
|
||||
activity,
|
||||
|
@ -19,7 +19,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
class RegistrationConfigDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val profile: Profile,
|
||||
val onChangeListener: ((enabled: Boolean) -> Unit)? = null,
|
||||
val onChangeListener: (suspend (enabled: Boolean) -> Unit)? = null,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
@ -57,6 +57,21 @@ class RegistrationConfigDialog(
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showNoteShareDialog() {
|
||||
onShowListener?.invoke(TAG + "NoteShare")
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.registration_config_note_sharing_title)
|
||||
.setMessage(R.string.registration_config_note_sharing_text)
|
||||
.setPositiveButton(R.string.i_agree) { _, _ ->
|
||||
enableRegistration()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG + "NoteShare")
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showEnableDialog() {
|
||||
onShowListener?.invoke(TAG + "Enable")
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
|
@ -27,6 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -36,6 +37,7 @@ class EventDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
// this event is observed for changes
|
||||
private var event: EventFull,
|
||||
private val showNotes: Boolean = true,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<DialogEventDetailsBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -96,6 +98,8 @@ class EventDetailsDialog(
|
||||
manager.markAsSeen(event)
|
||||
}
|
||||
|
||||
event.filterNotes()
|
||||
|
||||
val bullet = " • "
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
|
||||
@ -104,7 +108,7 @@ class EventDetailsDialog(
|
||||
}
|
||||
catch (_: Exception) {}
|
||||
|
||||
manager.setLegendText(b.legend, event)
|
||||
manager.setLegendText(b.legend, event, showNotes)
|
||||
|
||||
b.typeColor.background?.setTintColor(event.eventColor)
|
||||
|
||||
@ -257,6 +261,14 @@ class EventDetailsDialog(
|
||||
it.putStringArray("attachmentNames", event.attachmentNames!!.toTypedArray())
|
||||
}, owner = event)
|
||||
}
|
||||
|
||||
b.notesButton.isVisible = showNotes
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = event,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
|
@ -22,12 +22,14 @@ class EventListAdapter(
|
||||
val simpleMode: Boolean = false,
|
||||
val showWeekDay: Boolean = false,
|
||||
val showDate: Boolean = false,
|
||||
val showColor: Boolean = true,
|
||||
val showType: Boolean = true,
|
||||
val showTime: Boolean = true,
|
||||
val showSubject: Boolean = true,
|
||||
val markAsSeen: Boolean = true,
|
||||
val showNotes: Boolean = true,
|
||||
isReversed: Boolean = false,
|
||||
val onItemClick: ((event: EventFull) -> Unit)? = null,
|
||||
val onEventClick: ((event: EventFull) -> Unit)? = null,
|
||||
val onEventEditClick: ((event: EventFull) -> Unit)? = null,
|
||||
) : SearchableAdapter<EventFull>(isReversed), CoroutineScope {
|
||||
companion object {
|
||||
|
@ -13,9 +13,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -31,7 +28,6 @@ import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.StyledTextDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS
|
||||
@ -123,20 +119,13 @@ class EventManualDialog(
|
||||
}
|
||||
}
|
||||
|
||||
b.topicLayout.endIconDrawable = IconicsDrawable(activity, CommunityMaterial.Icon3.cmd_open_in_new).apply {
|
||||
sizeDp = 24
|
||||
}
|
||||
b.topicLayout.setEndIconOnClickListener {
|
||||
StyledTextDialog(
|
||||
activity,
|
||||
initialText = b.topic.text,
|
||||
onSuccess = {
|
||||
b.topic.text = it
|
||||
},
|
||||
onShowListener,
|
||||
onDismissListener
|
||||
).show()
|
||||
}
|
||||
textStylingManager.attachToField(
|
||||
activity = activity,
|
||||
textLayout = b.topicLayout,
|
||||
textEdit = b.topic,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
|
||||
stylingConfig = StylingConfigBase(editText = b.topic, htmlMode = SIMPLE)
|
||||
|
||||
@ -414,7 +403,7 @@ class EventManualDialog(
|
||||
|
||||
val share = b.shareSwitch.isChecked
|
||||
|
||||
if (share && profile.registration != Profile.REGISTRATION_ENABLED) {
|
||||
if (share && !profile.canShare) {
|
||||
RegistrationConfigDialog(activity, profile, onChangeListener = { enabled ->
|
||||
if (enabled)
|
||||
saveEvent()
|
||||
|
@ -36,14 +36,16 @@ class EventViewHolder(
|
||||
) {
|
||||
val manager = app.eventManager
|
||||
|
||||
b.root.onClick {
|
||||
adapter.onItemClick?.invoke(item)
|
||||
if (!item.seen) {
|
||||
manager.markAsSeen(item)
|
||||
}
|
||||
if (item.showAsUnseen == true) {
|
||||
item.showAsUnseen = false
|
||||
adapter.notifyItemChanged(item)
|
||||
if (adapter.onEventClick != null) {
|
||||
b.root.onClick {
|
||||
adapter.onEventClick.invoke(item)
|
||||
if (!item.seen) {
|
||||
manager.markAsSeen(item)
|
||||
}
|
||||
if (item.showAsUnseen == true) {
|
||||
item.showAsUnseen = false
|
||||
adapter.notifyItemChanged(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +54,7 @@ class EventViewHolder(
|
||||
|
||||
b.simpleMode = adapter.simpleMode
|
||||
|
||||
manager.setEventTopic(b.topic, item, showType = false)
|
||||
manager.setEventTopic(b.topic, item, showType = false, showNotes = adapter.showNotes)
|
||||
b.topic.text = adapter.highlightSearchText(
|
||||
item = item,
|
||||
text = b.topic.text,
|
||||
@ -67,13 +69,13 @@ class EventViewHolder(
|
||||
if (adapter.showDate)
|
||||
item.date.getRelativeString(activity, 7) ?: item.date.formattedStringShort
|
||||
else null,
|
||||
if (adapter.showType)
|
||||
if (adapter.showType && item.typeName != null)
|
||||
item.typeName
|
||||
else null,
|
||||
if (adapter.showTime)
|
||||
item.time?.stringHM ?: app.getString(R.string.event_all_day)
|
||||
else null,
|
||||
if (adapter.showSubject)
|
||||
if (adapter.showSubject && item.subjectLongName != null)
|
||||
adapter.highlightSearchText(
|
||||
item = item,
|
||||
text = item.subjectLongName ?: "",
|
||||
@ -111,13 +113,19 @@ class EventViewHolder(
|
||||
b.attachmentIcon.isVisible = item.hasAttachments
|
||||
|
||||
b.typeColor.background?.setTintColor(item.eventColor)
|
||||
b.typeColor.isVisible = adapter.showType
|
||||
b.typeColor.isVisible = adapter.showType && adapter.showColor
|
||||
|
||||
b.editButton.isVisible = !adapter.simpleMode && item.addedManually && !item.isDone
|
||||
b.editButton.onClick {
|
||||
adapter.onEventEditClick?.invoke(item)
|
||||
b.editButton.isVisible = !adapter.simpleMode
|
||||
&& item.addedManually
|
||||
&& !item.isDone
|
||||
&& adapter.onEventEditClick != null
|
||||
|
||||
if (adapter.onEventEditClick != null) {
|
||||
b.editButton.onClick {
|
||||
adapter.onEventEditClick.invoke(item)
|
||||
}
|
||||
b.editButton.attachToastHint(R.string.hint_edit_event)
|
||||
}
|
||||
b.editButton.attachToastHint(R.string.hint_edit_event)
|
||||
|
||||
if (item.showAsUnseen == null)
|
||||
item.showAsUnseen = !item.seen
|
||||
|
@ -15,12 +15,15 @@ import pl.szczodrzynski.edziennik.ext.onClick
|
||||
import pl.szczodrzynski.edziennik.ext.setTintColor
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
|
||||
class GradeDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val grade: GradeFull,
|
||||
private val showNotes: Boolean = true,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<DialogGradeDetailsBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -71,23 +74,34 @@ class GradeDetailsDialog(
|
||||
val historyList = withContext(Dispatchers.Default) {
|
||||
app.db.gradeDao().getByParentIdNow(App.profileId, grade.id)
|
||||
}
|
||||
if (historyList.isEmpty()) {
|
||||
b.historyVisible = false
|
||||
return
|
||||
}
|
||||
b.historyVisible = true
|
||||
//b.gradeHistoryNest.isNestedScrollingEnabled = false
|
||||
|
||||
b.gradeHistoryList.adapter = GradesAdapter(activity, {
|
||||
GradeDetailsDialog(activity, it).show()
|
||||
}).also {
|
||||
it.items = historyList.toMutableList()
|
||||
historyList.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
b.gradeHistoryList.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
b.historyVisible = historyList.isNotEmpty()
|
||||
if (historyList.isNotEmpty()) {
|
||||
b.gradeHistoryList.adapter = GradesAdapter(activity, onGradeClick = {
|
||||
GradeDetailsDialog(activity, it).show()
|
||||
}).also {
|
||||
it.items = historyList.toMutableList()
|
||||
}
|
||||
|
||||
b.gradeHistoryList.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
|
||||
b.notesButton.isVisible = showNotes
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = grade,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
if (showNotes)
|
||||
NoteManager.setLegendText(grade, b.legend)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class GradesAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
val showNotes: Boolean = true,
|
||||
var onGradeClick: ((item: GradeFull) -> Unit)? = null,
|
||||
var onGradesEditorClick: ((subject: GradesSubject, semester: GradesSemester) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), CoroutineScope {
|
||||
@ -218,7 +219,10 @@ class GradesAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(onClickListener)
|
||||
if (item !is GradeFull || onGradeClick != null)
|
||||
holder.itemView.setOnClickListener(onClickListener)
|
||||
else
|
||||
holder.itemView.setOnClickListener(null)
|
||||
}
|
||||
|
||||
fun notifyItemChanged(model: Any) {
|
||||
|
@ -76,6 +76,10 @@ class GradesListFragment : Fragment(), CoroutineScope {
|
||||
app.db.gradeDao().getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()).observe(viewLifecycleOwner, Observer { grades -> this@GradesListFragment.launch {
|
||||
if (!isAdded) return@launch
|
||||
|
||||
grades.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
val items = when {
|
||||
app.config.forProfile().grades.hideSticksFromOld && App.devMode -> grades.filter { it.value != 1.0f }
|
||||
else -> grades
|
||||
|
@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.data.db.full.GradeFull
|
||||
import pl.szczodrzynski.edziennik.databinding.GradesItemGradeBinding
|
||||
import pl.szczodrzynski.edziennik.ui.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.grades.models.GradesSubject
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class GradeViewHolder(
|
||||
@ -33,14 +34,16 @@ class GradeViewHolder(
|
||||
b.gradeName.setGrade(grade, manager, bigView = true)
|
||||
|
||||
if (grade.description.isNullOrBlank()) {
|
||||
b.gradeDescription.text = grade.category
|
||||
b.gradeDescription.text =
|
||||
grade.getNoteSubstituteText(adapter.showNotes) ?: grade.category
|
||||
b.gradeCategory.text =
|
||||
if (grade.isImprovement)
|
||||
app.getString(R.string.grades_improvement_category_format, "")
|
||||
else
|
||||
""
|
||||
} else {
|
||||
b.gradeDescription.text = grade.description
|
||||
b.gradeDescription.text =
|
||||
grade.getNoteSubstituteText(adapter.showNotes) ?: grade.description
|
||||
b.gradeCategory.text =
|
||||
if (grade.isImprovement)
|
||||
app.getString(R.string.grades_improvement_category_format, grade.category)
|
||||
@ -48,6 +51,9 @@ class GradeViewHolder(
|
||||
grade.category
|
||||
}
|
||||
|
||||
if (adapter.showNotes)
|
||||
NoteManager.prependIcon(grade, b.gradeDescription)
|
||||
|
||||
val weightText = manager.getWeightString(activity, grade, showClassAverage = true)
|
||||
b.gradeWeight.text = weightText
|
||||
b.gradeWeight.isVisible = weightText != null
|
||||
|
@ -24,6 +24,7 @@ interface HomeCard {
|
||||
const val CARD_TIMETABLE = 2
|
||||
const val CARD_GRADES = 3
|
||||
const val CARD_EVENTS = 4
|
||||
const val CARD_NOTES = 5
|
||||
}
|
||||
|
||||
val id: Int
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_EVENTS
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_GRADES
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_NOTES
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_TIMETABLE
|
||||
|
||||
class HomeConfigDialog(
|
||||
@ -32,6 +33,7 @@ class HomeConfigDialog(
|
||||
R.string.card_type_timetable to CARD_TIMETABLE,
|
||||
R.string.card_type_grades to CARD_GRADES,
|
||||
R.string.card_type_events to CARD_EVENTS,
|
||||
R.string.card_type_notes to CARD_NOTES,
|
||||
).mapKeys { (resId, _) -> activity.getString(resId) }
|
||||
|
||||
override fun getDefaultSelectedItems() =
|
||||
|
@ -144,7 +144,8 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_LUCKY_NUMBER),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_TIMETABLE),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_EVENTS),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_GRADES)
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_GRADES),
|
||||
HomeCardModel(app.profile.id, HomeCard.CARD_NOTES),
|
||||
)
|
||||
app.config.forProfile().ui.homeCards = app.config.forProfile().ui.homeCards.toMutableList().also { it.addAll(cards) }
|
||||
}
|
||||
@ -157,6 +158,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||
HomeCard.CARD_TIMETABLE -> HomeTimetableCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_GRADES -> HomeGradesCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_EVENTS -> HomeEventsCard(it.cardId, app, activity, this, app.profile)
|
||||
HomeCard.CARD_NOTES -> HomeNotesCard(it.cardId, app, activity, this, app.profile)
|
||||
else -> null
|
||||
} as HomeCard?
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class HomeEventsCard(
|
||||
showTime = false,
|
||||
showSubject = false,
|
||||
markAsSeen = false,
|
||||
onItemClick = {
|
||||
onEventClick = {
|
||||
EventDetailsDialog(
|
||||
activity,
|
||||
it
|
||||
@ -82,6 +82,10 @@ class HomeEventsCard(
|
||||
)
|
||||
|
||||
app.db.eventDao().getNearestNotDone(profile.id, Date.getToday(), 4).observe(activity, Observer { events ->
|
||||
events.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
adapter.setAllItems(events)
|
||||
if (b.eventsView.adapter == null) {
|
||||
b.eventsView.adapter = adapter
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.home.cards
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeNotesBinding
|
||||
import pl.szczodrzynski.edziennik.ext.dp
|
||||
import pl.szczodrzynski.edziennik.ext.onClick
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.notes.NoteDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.NoteEditorDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.NoteListAdapter
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeNotesCard(
|
||||
override val id: Int,
|
||||
val app: App,
|
||||
val activity: MainActivity,
|
||||
val fragment: HomeFragment,
|
||||
val profile: Profile,
|
||||
) : HomeCard, CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeNotesCard"
|
||||
}
|
||||
|
||||
private var job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val manager
|
||||
get() = app.noteManager
|
||||
|
||||
private lateinit var adapter: NoteListAdapter
|
||||
|
||||
private fun onNoteClick(note: Note) = launch {
|
||||
val owner = withContext(Dispatchers.IO) {
|
||||
manager.getOwner(note)
|
||||
} as? Noteable
|
||||
|
||||
NoteDetailsDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
note = note,
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun onNoteAddClick(view: View?) {
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = null,
|
||||
editingNote = null,
|
||||
profileId = profile.id,
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) { launch {
|
||||
holder.root.removeAllViews()
|
||||
val b = CardHomeNotesBinding.inflate(LayoutInflater.from(holder.root.context))
|
||||
b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
setMargins(8.dp)
|
||||
}
|
||||
holder.root += b.root
|
||||
|
||||
adapter = NoteListAdapter(
|
||||
activity = activity,
|
||||
onNoteClick = this@HomeNotesCard::onNoteClick,
|
||||
onNoteEditClick = null,
|
||||
)
|
||||
|
||||
app.db.noteDao().getAllNoOwner(profileId = profile.id).observe(activity) { notes ->
|
||||
|
||||
// show/hide relevant views
|
||||
b.list.isVisible = notes.isNotEmpty()
|
||||
b.noData.isVisible = notes.isEmpty()
|
||||
if (notes.isEmpty()) {
|
||||
return@observe
|
||||
}
|
||||
|
||||
// apply the new note list
|
||||
adapter.setAllItems(notes.take(4))
|
||||
|
||||
// configure the adapter & recycler view
|
||||
if (b.list.adapter == null) {
|
||||
b.list.adapter = adapter
|
||||
b.list.apply {
|
||||
//setHasFixedSize(true)
|
||||
isNestedScrollingEnabled = false
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
} else {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
b.addNote.onClick(this@HomeNotesCard::onNoteAddClick)
|
||||
|
||||
holder.root.onClick {
|
||||
activity.loadTarget(MainActivity.DRAWER_ITEM_AGENDA)
|
||||
}
|
||||
}}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||
}
|
@ -64,7 +64,7 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
|
||||
showSubject = true,
|
||||
markAsSeen = true,
|
||||
isReversed = homeworkDate == HomeworkDate.PAST,
|
||||
onItemClick = {
|
||||
onEventClick = {
|
||||
EventDetailsDialog(
|
||||
activity,
|
||||
it
|
||||
@ -82,6 +82,10 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
|
||||
app.db.eventDao().getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter).observe(this@HomeworkListFragment, Observer { events ->
|
||||
if (!isAdded) return@Observer
|
||||
|
||||
events.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
// show/hide relevant views
|
||||
setSwipeToRefresh(events.isEmpty())
|
||||
b.progressBar.isVisible = false
|
||||
|
@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.ext.onClick
|
||||
import pl.szczodrzynski.edziennik.ext.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.ui.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.ui.messages.MessagesUtils
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class MessageViewHolder(
|
||||
@ -73,7 +74,10 @@ class MessageViewHolder(
|
||||
color = colorHighlight
|
||||
)
|
||||
|
||||
adapter.onItemClick?.let { listener ->
|
||||
if (adapter.showNotes)
|
||||
NoteManager.prependIcon(item, b.messageSubject)
|
||||
|
||||
adapter.onMessageClick?.let { listener ->
|
||||
b.root.onClick { listener(item) }
|
||||
}
|
||||
adapter.onStarClick?.let { listener ->
|
||||
|
@ -13,7 +13,8 @@ import pl.szczodrzynski.edziennik.ui.search.SearchableAdapter
|
||||
class MessagesAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
val teachers: List<Teacher>,
|
||||
val onItemClick: ((item: MessageFull) -> Unit)? = null,
|
||||
val showNotes: Boolean = true,
|
||||
val onMessageClick: ((item: MessageFull) -> Unit)? = null,
|
||||
val onStarClick: ((item: MessageFull) -> Unit)? = null,
|
||||
) : SearchableAdapter<MessageFull>() {
|
||||
companion object {
|
||||
|
@ -62,7 +62,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
app.db.teacherDao().getAllNow(App.profileId)
|
||||
}
|
||||
|
||||
adapter = MessagesAdapter(activity, teachers, onItemClick = {
|
||||
adapter = MessagesAdapter(activity, teachers, onMessageClick = {
|
||||
val (target, args) =
|
||||
if (it.isDraft) {
|
||||
TARGET_MESSAGES_COMPOSE to Bundle("message" to app.gson.toJson(it))
|
||||
@ -81,6 +81,8 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
|
||||
return@Observer
|
||||
|
||||
messages.forEach { message ->
|
||||
message.filterNotes()
|
||||
|
||||
// uh oh, so these are the workarounds ??
|
||||
message.recipients?.removeAll { it.profileId != message.profileId }
|
||||
message.recipients?.forEach { recipient ->
|
||||
|
@ -30,6 +30,7 @@ import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MessagesConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.messages.MessagesUtils
|
||||
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
@ -266,6 +267,13 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
b.progress.visibility = View.GONE
|
||||
Anim.fadeIn(b.content, 200, null)
|
||||
MessagesFragment.pageSelection = min(message.type, 1)
|
||||
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = message,
|
||||
onShowListener = null,
|
||||
onDismissListener = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun showAttachments() {
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.Binding
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteListCategoryItemBinding
|
||||
import pl.szczodrzynski.edziennik.ext.resolveDrawable
|
||||
import pl.szczodrzynski.edziennik.ui.grades.viewholder.BindableViewHolder
|
||||
|
||||
class NoteCategoryViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: NoteListCategoryItemBinding = NoteListCategoryItemBinding.inflate(
|
||||
inflater,
|
||||
parent,
|
||||
false,
|
||||
),
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<Note, NoteListAdapter> {
|
||||
companion object {
|
||||
private const val TAG = "NoteCategoryViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(
|
||||
activity: AppCompatActivity,
|
||||
app: App,
|
||||
item: Note,
|
||||
position: Int,
|
||||
adapter: NoteListAdapter,
|
||||
) {
|
||||
val manager = app.noteManager
|
||||
val title = b.root as? TextView ?: return
|
||||
val ownerType = item.ownerType ?: return
|
||||
|
||||
title.setText(manager.getOwnerTypeText(ownerType))
|
||||
title.setCompoundDrawables(
|
||||
manager.getOwnerTypeImage(ownerType).resolveDrawable(activity),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
Binding.drawableLeftAutoSize(title, enable = true)
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteDetailsDialogBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NoteDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val owner: Noteable?,
|
||||
private var note: Note,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<NoteDetailsDialogBinding>(activity, onShowListener, onDismissListener) {
|
||||
|
||||
override val TAG = "NoteDetailsDialog"
|
||||
|
||||
override fun getTitleRes(): Int? = null
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
NoteDetailsDialogBinding.inflate(layoutInflater)
|
||||
|
||||
override fun getPositiveButtonText() = R.string.close
|
||||
override fun getNeutralButtonText() = if (note.canEdit) R.string.homework_edit else null
|
||||
|
||||
private val manager
|
||||
get() = app.noteManager
|
||||
|
||||
override suspend fun onNeutralClick(): Boolean {
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
editingNote = note,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
return NO_DISMISS
|
||||
}
|
||||
|
||||
override suspend fun onShow() {
|
||||
manager.configureHeader(activity, owner, b.header)
|
||||
|
||||
b.idsLayout.isVisible = App.devMode
|
||||
|
||||
// watch the note for changes
|
||||
app.db.noteDao().get(note.profileId, note.id).observe(activity) {
|
||||
if (it == null) {
|
||||
dismiss()
|
||||
return@observe
|
||||
}
|
||||
note = it
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
private fun update() {
|
||||
b.note = note
|
||||
|
||||
if (note.color != null) {
|
||||
dialog.overlayBackgroundColor(note.color!!.toInt(), 0x50)
|
||||
} else {
|
||||
dialog.overlayBackgroundColor(0, 0)
|
||||
}
|
||||
|
||||
b.addedBy.setText(
|
||||
when (note.sharedBy) {
|
||||
null -> R.string.notes_added_by_you_format
|
||||
"self" -> R.string.event_details_shared_by_self_format
|
||||
else -> R.string.event_details_shared_by_format
|
||||
},
|
||||
Date.fromMillis(note.addedDate).formattedString,
|
||||
note.sharedByName ?: "",
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteEditorDialogBinding
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
|
||||
import pl.szczodrzynski.edziennik.ext.resolveString
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode
|
||||
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfigBase
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class NoteEditorDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val owner: Noteable?,
|
||||
private val editingNote: Note?,
|
||||
private val profileId: Int =
|
||||
owner?.getNoteOwnerProfileId()
|
||||
?: editingNote?.profileId
|
||||
?: 0,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<NoteEditorDialogBinding>(activity, onShowListener, onDismissListener) {
|
||||
|
||||
override val TAG = "NoteEditorDialog"
|
||||
|
||||
override fun getTitleRes(): Int? = null
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
NoteEditorDialogBinding.inflate(layoutInflater)
|
||||
|
||||
override fun isCancelable() = false
|
||||
override fun getPositiveButtonText() = R.string.save
|
||||
override fun getNeutralButtonText() = if (editingNote != null) R.string.remove else null
|
||||
override fun getNegativeButtonText() = R.string.cancel
|
||||
|
||||
private lateinit var topicStylingConfig: StylingConfigBase
|
||||
private lateinit var bodyStylingConfig: StylingConfigBase
|
||||
private val manager
|
||||
get() = app.noteManager
|
||||
private val textStylingManager
|
||||
get() = app.textStylingManager
|
||||
|
||||
private var progressDialog: AlertDialog? = null
|
||||
|
||||
override suspend fun onPositiveClick(): Boolean {
|
||||
val profile = withContext(Dispatchers.IO) {
|
||||
app.db.profileDao().getByIdNow(profileId)
|
||||
} ?: return NO_DISMISS
|
||||
|
||||
val note = buildNote(profile) ?: return NO_DISMISS
|
||||
|
||||
if (note.isShared && !profile.canShare) {
|
||||
RegistrationConfigDialog(activity, profile, onChangeListener = { enabled ->
|
||||
if (enabled)
|
||||
onPositiveClick()
|
||||
}).showNoteShareDialog()
|
||||
return NO_DISMISS
|
||||
}
|
||||
|
||||
if (note.isShared || editingNote?.isShared == true) {
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(when (note.isShared) {
|
||||
true -> R.string.notes_editor_progress_sharing
|
||||
false -> R.string.notes_editor_progress_unsharing
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
val success = manager.saveNote(activity, note, wasShared = editingNote?.isShared ?: false)
|
||||
progressDialog?.dismiss()
|
||||
return success
|
||||
}
|
||||
|
||||
override suspend fun onNeutralClick(): Boolean {
|
||||
// editingNote cannot be null, as the button is visible
|
||||
|
||||
val confirmation = suspendCoroutine<Boolean> { cont ->
|
||||
var result = false
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.are_you_sure)
|
||||
.setMessage(R.string.notes_editor_confirmation_text)
|
||||
.setPositiveButton(R.string.yes) { _, _ -> result = true }
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.setOnDismissListener { cont.resume(result) }
|
||||
.show()
|
||||
}
|
||||
if (!confirmation)
|
||||
return NO_DISMISS
|
||||
|
||||
if (editingNote?.isShared == true) {
|
||||
progressDialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.notes_editor_progress_unsharing)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
||||
val success = manager.deleteNote(activity, editingNote ?: return NO_DISMISS)
|
||||
progressDialog?.dismiss()
|
||||
return success
|
||||
}
|
||||
|
||||
override suspend fun onShow() {
|
||||
manager.configureHeader(activity, owner, b.header)
|
||||
|
||||
topicStylingConfig = StylingConfigBase(editText = b.topic, htmlMode = HtmlMode.SIMPLE)
|
||||
bodyStylingConfig = StylingConfigBase(editText = b.body, htmlMode = HtmlMode.SIMPLE)
|
||||
|
||||
b.ownerType = owner?.getNoteType() ?: Note.OwnerType.NONE
|
||||
b.editingNote = editingNote
|
||||
|
||||
b.color.clear().append(Note.Color.values().map { color ->
|
||||
TextInputDropDown.Item(
|
||||
id = color.value ?: 0L,
|
||||
text = color.stringRes.resolveString(activity),
|
||||
tag = color,
|
||||
icon = if (color.value != null)
|
||||
IconicsDrawable(activity).apply {
|
||||
icon = CommunityMaterial.Icon.cmd_circle
|
||||
sizeDp = 24
|
||||
colorInt = color.value.toInt()
|
||||
} else null,
|
||||
)
|
||||
})
|
||||
b.color.select(id = editingNote?.color ?: 0L)
|
||||
|
||||
textStylingManager.attachToField(
|
||||
activity = activity,
|
||||
textLayout = b.topicLayout,
|
||||
textEdit = b.topic,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
textStylingManager.attachToField(
|
||||
activity = activity,
|
||||
textLayout = b.bodyLayout,
|
||||
textEdit = b.body,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildNote(profile: Profile): Note? {
|
||||
val ownerType = owner?.getNoteType() ?: Note.OwnerType.NONE
|
||||
val topic = b.topic.text?.toString()
|
||||
val body = b.body.text?.toString()
|
||||
val color = b.color.selected?.tag as? Note.Color
|
||||
|
||||
val share = b.shareSwitch.isChecked && ownerType.isShareable
|
||||
val replace = b.replaceSwitch.isChecked && ownerType.canReplace
|
||||
|
||||
if (body.isNullOrBlank()) {
|
||||
b.bodyLayout.error = app.getString(R.string.notes_editor_body_error)
|
||||
b.body.requestFocus()
|
||||
return null
|
||||
}
|
||||
|
||||
val topicHtml = if (topic.isNotNullNorBlank())
|
||||
textStylingManager.getHtmlText(topicStylingConfig)
|
||||
else null
|
||||
val bodyHtml = textStylingManager.getHtmlText(bodyStylingConfig)
|
||||
|
||||
return Note(
|
||||
profileId = profile.id,
|
||||
id = editingNote?.id ?: System.currentTimeMillis(),
|
||||
ownerType = owner?.getNoteType(),
|
||||
ownerId = owner?.getNoteOwnerId(),
|
||||
replacesOriginal = replace,
|
||||
topic = topicHtml,
|
||||
body = bodyHtml,
|
||||
color = color?.value,
|
||||
sharedBy = if (share) "self" else null,
|
||||
sharedByName = if (share) profile.studentNameLong else null,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.ui.search.SearchableAdapter
|
||||
|
||||
class NoteListAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
val onNoteClick: ((note: Note) -> Unit)? = null,
|
||||
val onNoteEditClick: ((note: Note) -> Unit)? = null,
|
||||
) : SearchableAdapter<Note>() {
|
||||
companion object {
|
||||
private const val TAG = "NoteListAdapter"
|
||||
private const val ITEM_TYPE_NOTE = 0
|
||||
private const val ITEM_TYPE_CATEGORY = 1
|
||||
}
|
||||
|
||||
private val app = activity.applicationContext as App
|
||||
|
||||
override fun getItemViewType(item: Note) = when {
|
||||
item.isCategoryItem -> ITEM_TYPE_CATEGORY
|
||||
else -> ITEM_TYPE_NOTE
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
item: Note,
|
||||
) {
|
||||
when (holder) {
|
||||
is NoteViewHolder -> holder.onBind(activity, app, item, position, this)
|
||||
is NoteCategoryViewHolder -> holder.onBind(activity, app, item, position, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder = when (viewType) {
|
||||
ITEM_TYPE_CATEGORY -> NoteCategoryViewHolder(inflater, parent)
|
||||
else -> NoteViewHolder(inflater, parent)
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteListDialogBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
|
||||
class NoteListDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val owner: Noteable,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<NoteListDialogBinding>(activity, onShowListener, onDismissListener) {
|
||||
|
||||
override val TAG = "NoteListDialog"
|
||||
|
||||
override fun getTitleRes(): Int? = null
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
NoteListDialogBinding.inflate(layoutInflater)
|
||||
|
||||
override fun getPositiveButtonText() = R.string.close
|
||||
override fun getNeutralButtonText() = R.string.add
|
||||
|
||||
private val manager
|
||||
get() = app.noteManager
|
||||
|
||||
private lateinit var adapter: NoteListAdapter
|
||||
|
||||
override suspend fun onNeutralClick(): Boolean {
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
editingNote = null,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
return NO_DISMISS
|
||||
}
|
||||
|
||||
override suspend fun onShow() {
|
||||
manager.configureHeader(activity, owner, b.header)
|
||||
|
||||
adapter = NoteListAdapter(
|
||||
activity = activity,
|
||||
onNoteClick = {
|
||||
NoteDetailsDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
note = it,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
},
|
||||
onNoteEditClick = {
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
editingNote = it,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
},
|
||||
)
|
||||
|
||||
app.db.noteDao().getAllFor(
|
||||
profileId = owner.getNoteOwnerProfileId(),
|
||||
ownerType = owner.getNoteType(),
|
||||
ownerId = owner.getNoteOwnerId()
|
||||
).observe(activity) { notes ->
|
||||
|
||||
// show/hide relevant views
|
||||
b.noteListLayout.isVisible = notes.isNotEmpty()
|
||||
b.noData.isVisible = notes.isEmpty()
|
||||
if (notes.isEmpty()) {
|
||||
return@observe
|
||||
}
|
||||
|
||||
// apply the new note list
|
||||
adapter.setAllItems(notes)
|
||||
|
||||
// configure the adapter & recycler view
|
||||
if (b.noteList.adapter == null) {
|
||||
b.noteList.adapter = adapter
|
||||
b.noteList.apply {
|
||||
//setHasFixedSize(true)
|
||||
isNestedScrollingEnabled = false
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
} else {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteListItemBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NoteViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: NoteListItemBinding = NoteListItemBinding.inflate(inflater, parent, false),
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<Note, NoteListAdapter> {
|
||||
companion object {
|
||||
private const val TAG = "NoteViewHolder"
|
||||
}
|
||||
|
||||
override fun onBind(
|
||||
activity: AppCompatActivity,
|
||||
app: App,
|
||||
item: Note,
|
||||
position: Int,
|
||||
adapter: NoteListAdapter,
|
||||
) {
|
||||
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
|
||||
val addedDate = Date.fromMillis(item.addedDate).formattedString
|
||||
|
||||
b.topic.text = adapter.highlightSearchText(
|
||||
item = item,
|
||||
text = item.topicHtml ?: item.bodyHtml,
|
||||
color = colorHighlight,
|
||||
)
|
||||
|
||||
if (item.color != null) {
|
||||
b.colorLayout.background =
|
||||
ColorDrawable(ColorUtils.setAlphaComponent(item.color.toInt(), 0x50))
|
||||
} else {
|
||||
b.colorLayout.background = null
|
||||
}
|
||||
|
||||
if (item.sharedBy != null && item.sharedByName != null) {
|
||||
b.addedBy.text = listOf<CharSequence>(
|
||||
"{cmd-share-variant}",
|
||||
item.sharedByName,
|
||||
"•",
|
||||
addedDate,
|
||||
).concat(" ")
|
||||
|
||||
// workaround for the span data lost during setText above
|
||||
val sharedBySpanned = adapter.highlightSearchText(
|
||||
item = item,
|
||||
text = item.sharedByName,
|
||||
color = colorHighlight,
|
||||
)
|
||||
b.addedBy.text = b.addedBy.text.replaceSpanned(item.sharedByName, sharedBySpanned)
|
||||
} else {
|
||||
b.addedBy.setText(R.string.notes_added_by_you_format, addedDate)
|
||||
}
|
||||
|
||||
b.editButton.isVisible = item.canEdit && adapter.onNoteEditClick != null
|
||||
|
||||
if (adapter.onNoteClick != null)
|
||||
b.root.onClick {
|
||||
adapter.onNoteClick.invoke(item)
|
||||
}
|
||||
if (adapter.onNoteEditClick != null)
|
||||
b.editButton.onClick {
|
||||
adapter.onNoteEditClick.invoke(item)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.view.Gravity
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.ext.dp
|
||||
import pl.szczodrzynski.edziennik.ext.onClick
|
||||
|
||||
fun MaterialButton.setupNotesButton(
|
||||
activity: AppCompatActivity,
|
||||
owner: Noteable,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) {
|
||||
if (!isVisible)
|
||||
return
|
||||
icon = IconicsDrawable(activity, CommunityMaterial.Icon3.cmd_playlist_edit)
|
||||
setText(R.string.notes_button)
|
||||
iconPadding = 8.dp
|
||||
iconSize = 24.dp
|
||||
|
||||
updateLayoutParams<LinearLayout.LayoutParams> {
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
}
|
||||
updatePadding(left = 12.dp)
|
||||
|
||||
onClick {
|
||||
NoteListDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.notes
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.databinding.NotesFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class NotesFragment : Fragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "NotesFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: NotesFragmentBinding
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private val manager
|
||||
get() = app.noteManager
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
activity = getActivity() as? MainActivity ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
b = NotesFragmentBinding.inflate(inflater)
|
||||
return b.root
|
||||
}
|
||||
|
||||
private fun onNoteClick(note: Note) = launch {
|
||||
val owner = withContext(Dispatchers.IO) {
|
||||
manager.getOwner(note)
|
||||
} as? Noteable
|
||||
|
||||
NoteDetailsDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
note = note,
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun onNoteEditClick(note: Note) = launch {
|
||||
val owner = withContext(Dispatchers.IO) {
|
||||
manager.getOwner(note)
|
||||
} as? Noteable
|
||||
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = owner,
|
||||
editingNote = note,
|
||||
profileId = App.profileId,
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun onNoteAddClick(view: View?) {
|
||||
NoteEditorDialog(
|
||||
activity = activity,
|
||||
owner = null,
|
||||
editingNote = null,
|
||||
profileId = App.profileId,
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
if (!isAdded) return
|
||||
|
||||
activity.navView.apply {
|
||||
bottomBar.apply {
|
||||
fabEnable = true
|
||||
fabExtendedText = getString(R.string.notes_action_add)
|
||||
fabIcon = CommunityMaterial.Icon3.cmd_text_box_plus_outline
|
||||
}
|
||||
|
||||
setFabOnClickListener(this@NotesFragment::onNoteAddClick)
|
||||
}
|
||||
activity.gainAttentionFAB()
|
||||
|
||||
val adapter = NoteListAdapter(
|
||||
activity = activity,
|
||||
onNoteClick = this::onNoteClick,
|
||||
onNoteEditClick = this::onNoteEditClick,
|
||||
)
|
||||
|
||||
app.db.noteDao().getAll(profileId = App.profileId).observe(activity) { allNotes ->
|
||||
if (!isAdded) return@observe
|
||||
|
||||
// show/hide relevant views
|
||||
b.progressBar.isVisible = false
|
||||
b.list.isVisible = allNotes.isNotEmpty()
|
||||
b.noData.isVisible = allNotes.isEmpty()
|
||||
if (allNotes.isEmpty()) {
|
||||
return@observe
|
||||
}
|
||||
|
||||
val notes = allNotes.groupBy { it.ownerType }.flatMap { (ownerType, notes) ->
|
||||
if (ownerType != null) {
|
||||
// construct a dummy note, used as the category separator
|
||||
val categoryItem = Note(
|
||||
profileId = 0,
|
||||
id = 0,
|
||||
ownerType = ownerType,
|
||||
ownerId = 0,
|
||||
topic = null,
|
||||
body = "",
|
||||
color = null,
|
||||
)
|
||||
categoryItem.isCategoryItem = true
|
||||
val mutableNotes = notes.toMutableList()
|
||||
mutableNotes.add(0, categoryItem)
|
||||
return@flatMap mutableNotes
|
||||
}
|
||||
return@flatMap notes
|
||||
}
|
||||
|
||||
// apply the new note list
|
||||
adapter.setAllItems(notes, addSearchField = true)
|
||||
|
||||
// configure the adapter & recycler view
|
||||
if (b.list.adapter == null) {
|
||||
b.list.adapter = adapter
|
||||
b.list.apply {
|
||||
//setHasFixedSize(true)
|
||||
isNestedScrollingEnabled = false
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
|
||||
// reapply the filter
|
||||
adapter.getSearchField()?.applyTo(adapter)
|
||||
}
|
||||
}
|
||||
}
|
@ -59,14 +59,15 @@ class SearchFilter<T : Searchable<T>>(
|
||||
}
|
||||
|
||||
val newItems = allItems.mapNotNull { item ->
|
||||
if (item is SearchField) {
|
||||
// get all keyword sets from the entity
|
||||
val searchKeywords = item.searchKeywords
|
||||
// keep the SearchField and items having no keywords
|
||||
if (item is SearchField || searchKeywords.isEmpty()) {
|
||||
return@mapNotNull item
|
||||
}
|
||||
item.searchPriority = NO_MATCH
|
||||
item.searchHighlightText = null
|
||||
|
||||
// get all keyword sets from the entity
|
||||
val searchKeywords = item.searchKeywords
|
||||
// a temporary variable for the loops below
|
||||
var matchWeight: Int
|
||||
|
||||
|
@ -10,7 +10,6 @@ import eu.szkolny.font.SzkolnyFont
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
|
||||
import pl.szczodrzynski.edziennik.ext.after
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.*
|
||||
import pl.szczodrzynski.edziennik.ui.settings.SettingsCard
|
||||
@ -83,36 +82,39 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
|
||||
AttendanceConfigDialog(activity, reloadOnDismiss = false).show()
|
||||
},
|
||||
|
||||
util.createPropertyItem(
|
||||
text = R.string.settings_register_allow_registration_text,
|
||||
subText = R.string.settings_register_allow_registration_subtext,
|
||||
icon = CommunityMaterial.Icon.cmd_account_circle_outline,
|
||||
value = app.profile.registration == REGISTRATION_ENABLED,
|
||||
beforeChange = { item, value ->
|
||||
if (app.profile.registration == REGISTRATION_ENABLED == value)
|
||||
// allow the switch to change - needed for util.refresh() to change the visual state
|
||||
return@createPropertyItem true
|
||||
val dialog =
|
||||
RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
|
||||
if (item.isChecked == enabled)
|
||||
return@RegistrationConfigDialog
|
||||
item.isChecked = enabled
|
||||
if (value) {
|
||||
card.items.after(item, sharedEventsItem)
|
||||
} else {
|
||||
card.items.remove(sharedEventsItem)
|
||||
}
|
||||
util.refresh()
|
||||
})
|
||||
if (value)
|
||||
dialog.showEnableDialog()
|
||||
else
|
||||
dialog.showDisableDialog()
|
||||
false
|
||||
}
|
||||
) { _, _ -> },
|
||||
if (app.profile.archived)
|
||||
null
|
||||
else
|
||||
util.createPropertyItem(
|
||||
text = R.string.settings_register_allow_registration_text,
|
||||
subText = R.string.settings_register_allow_registration_subtext,
|
||||
icon = CommunityMaterial.Icon.cmd_account_circle_outline,
|
||||
value = app.profile.canShare,
|
||||
beforeChange = { item, value ->
|
||||
if (app.profile.canShare == value)
|
||||
// allow the switch to change - needed for util.refresh() to change the visual state
|
||||
return@createPropertyItem true
|
||||
val dialog =
|
||||
RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
|
||||
if (item.isChecked == enabled)
|
||||
return@RegistrationConfigDialog
|
||||
item.isChecked = enabled
|
||||
if (value) {
|
||||
card.items.after(item, sharedEventsItem)
|
||||
} else {
|
||||
card.items.remove(sharedEventsItem)
|
||||
}
|
||||
util.refresh()
|
||||
})
|
||||
if (value)
|
||||
dialog.showEnableDialog()
|
||||
else
|
||||
dialog.showDisableDialog()
|
||||
false
|
||||
}
|
||||
) { _, _ -> },
|
||||
|
||||
if (app.profile.registration == REGISTRATION_ENABLED)
|
||||
if (app.profile.canShare)
|
||||
sharedEventsItem
|
||||
else
|
||||
null
|
||||
|
@ -26,8 +26,10 @@ import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
|
||||
import pl.szczodrzynski.edziennik.ui.notes.setupNotesButton
|
||||
import pl.szczodrzynski.edziennik.utils.BetterLink
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
@ -35,6 +37,7 @@ class LessonDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
private val lesson: LessonFull,
|
||||
private val attendance: AttendanceFull? = null,
|
||||
private val showNotes: Boolean = true,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<DialogLessonDetailsBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -186,7 +189,7 @@ class LessonDetailsDialog(
|
||||
showTime = true,
|
||||
showSubject = true,
|
||||
markAsSeen = true,
|
||||
onItemClick = {
|
||||
onEventClick = {
|
||||
EventDetailsDialog(
|
||||
activity,
|
||||
it,
|
||||
@ -210,6 +213,10 @@ class LessonDetailsDialog(
|
||||
lessonDate,
|
||||
lessonTime
|
||||
).observe(activity) { events ->
|
||||
events.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
adapter.setAllItems(events)
|
||||
if (b.eventsView.adapter == null) {
|
||||
b.eventsView.adapter = adapter
|
||||
@ -244,5 +251,15 @@ class LessonDetailsDialog(
|
||||
onActionSelected = dialog::dismiss
|
||||
)
|
||||
}
|
||||
|
||||
b.notesButton.isVisible = showNotes
|
||||
b.notesButton.setupNotesButton(
|
||||
activity = activity,
|
||||
owner = lesson,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
)
|
||||
if (showNotes)
|
||||
NoteManager.setLegendText(lesson, b.legend)
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
|
||||
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
|
||||
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
|
||||
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.util.*
|
||||
@ -103,6 +104,10 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
// observe lesson database
|
||||
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this) { lessons ->
|
||||
launch {
|
||||
lessons.forEach {
|
||||
it.filterNotes()
|
||||
}
|
||||
|
||||
val events = withContext(Dispatchers.Default) {
|
||||
app.db.eventDao().getAllByDateNow(App.profileId, date)
|
||||
}
|
||||
@ -281,7 +286,9 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
|
||||
lb.lessonNumber = lesson.displayLessonNumber
|
||||
lb.subjectName.text = lesson.displaySubjectName?.let {
|
||||
val lessonText =
|
||||
lesson.getNoteSubstituteText(showNotes = true) ?: lesson.displaySubjectName
|
||||
lb.subjectName.text = lessonText?.let {
|
||||
if (lesson.type == Lesson.TYPE_CANCELLED || lesson.type == Lesson.TYPE_SHIFTED_SOURCE)
|
||||
it.asStrikethroughSpannable().asColoredSpannable(colorSecondary)
|
||||
else
|
||||
@ -290,6 +297,8 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
|
||||
lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
NoteManager.prependIcon(lesson, lb.subjectName)
|
||||
|
||||
lb.attendanceIcon.isVisible = attendance?.let {
|
||||
val icon = attendanceManager.getAttendanceIcon(it) ?: return@let false
|
||||
val color = attendanceManager.getAttendanceColor(it)
|
||||
|
@ -48,12 +48,16 @@ class EventManager(val app: App) : CoroutineScope {
|
||||
title: TextView,
|
||||
event: EventFull,
|
||||
showType: Boolean = true,
|
||||
showNotes: Boolean = true,
|
||||
doneIconColor: Int? = null
|
||||
) {
|
||||
val topicSpan = event.topicHtml
|
||||
val topicSpan = event.getNoteSubstituteText(showNotes) ?: event.topicHtml
|
||||
val hasReplacingNotes = event.hasReplacingNotes()
|
||||
|
||||
title.text = listOfNotNull(
|
||||
if (event.addedManually) "{cmd-clipboard-edit-outline} " else null,
|
||||
if (event.hasNotes() && hasReplacingNotes && showNotes) "{cmd-swap-horizontal} " else null,
|
||||
if (event.hasNotes() && !hasReplacingNotes && showNotes) "{cmd-playlist-edit} " else null,
|
||||
if (showType) "${event.typeName ?: "wydarzenie"} - " else null,
|
||||
topicSpan,
|
||||
).concat()
|
||||
@ -70,10 +74,11 @@ class EventManager(val app: App) : CoroutineScope {
|
||||
)
|
||||
}
|
||||
|
||||
fun setLegendText(legend: IconicsTextView, event: EventFull) {
|
||||
fun setLegendText(legend: IconicsTextView, event: EventFull, showNotes: Boolean = true) {
|
||||
legend.text = listOfNotNull(
|
||||
if (event.addedManually) R.string.legend_event_added_manually else null,
|
||||
if (event.isDone) R.string.legend_event_is_done else null
|
||||
if (event.isDone) R.string.legend_event_is_done else null,
|
||||
if (showNotes) NoteManager.getLegendText(event) else null,
|
||||
).map { legend.context.getString(it) }.join("\n")
|
||||
legend.isVisible = legend.text.isNotBlank()
|
||||
}
|
||||
|
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-10-17.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils.managers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.SpannableStringBuilder
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.iconics.view.IconicsTextView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note.OwnerType
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.data.db.full.*
|
||||
import pl.szczodrzynski.edziennik.databinding.NoteDialogHeaderBinding
|
||||
import pl.szczodrzynski.edziennik.ext.resolveDrawable
|
||||
import pl.szczodrzynski.edziennik.ui.agenda.DayDialog
|
||||
import pl.szczodrzynski.edziennik.ui.agenda.lessonchanges.LessonChangesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.announcements.AnnouncementsAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.attendance.AttendanceAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.attendance.AttendanceDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.attendance.AttendanceFragment
|
||||
import pl.szczodrzynski.edziennik.ui.behaviour.NoticesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.grades.GradeDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.ui.grades.GradesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.timetable.LessonDetailsDialog
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NoteManager(private val app: App) {
|
||||
companion object {
|
||||
private const val TAG = "NoteManager"
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun prependIcon(owner: Noteable, textView: IconicsTextView) {
|
||||
if (owner.hasNotes())
|
||||
textView.text = SpannableStringBuilder(
|
||||
if (owner.hasReplacingNotes())
|
||||
"{cmd-swap-horizontal} "
|
||||
else
|
||||
"{cmd-playlist-edit} "
|
||||
).append(textView.text)
|
||||
}
|
||||
|
||||
fun getLegendText(owner: Noteable): Int? = when {
|
||||
owner.hasReplacingNotes() -> R.string.legend_notes_added_replaced
|
||||
owner.hasNotes() -> R.string.legend_notes_added
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun setLegendText(owner: Noteable, textView: IconicsTextView) {
|
||||
textView.isVisible = owner.hasNotes()
|
||||
textView.setText(getLegendText(owner) ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
fun getOwner(note: Note): Any? {
|
||||
if (note.ownerId == null)
|
||||
return null
|
||||
return when (note.ownerType) {
|
||||
OwnerType.ANNOUNCEMENT ->
|
||||
app.db.announcementDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.ATTENDANCE ->
|
||||
app.db.attendanceDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.BEHAVIOR ->
|
||||
app.db.noticeDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.EVENT ->
|
||||
app.db.eventDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.EVENT_SUBJECT, OwnerType.LESSON_SUBJECT ->
|
||||
app.db.subjectDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.GRADE ->
|
||||
app.db.gradeDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.LESSON ->
|
||||
app.db.timetableDao().getByIdNow(note.profileId, note.ownerId)
|
||||
OwnerType.MESSAGE ->
|
||||
app.db.messageDao().getByIdNow(note.profileId, note.ownerId)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun hasValidOwner(note: Note): Boolean {
|
||||
if (note.ownerType == null || note.ownerType == OwnerType.DAY)
|
||||
return true
|
||||
return getOwner(note) != null
|
||||
}
|
||||
|
||||
suspend fun saveNote(activity: AppCompatActivity, note: Note, wasShared: Boolean): Boolean {
|
||||
val success = when {
|
||||
!note.isShared && wasShared -> unshareNote(activity, note)
|
||||
note.isShared -> shareNote(activity, note)
|
||||
else -> true
|
||||
}
|
||||
|
||||
if (!success)
|
||||
return false
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
app.db.noteDao().add(note)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
suspend fun deleteNote(activity: AppCompatActivity, note: Note): Boolean {
|
||||
val success = when {
|
||||
note.isShared -> unshareNote(activity, note)
|
||||
else -> true
|
||||
}
|
||||
|
||||
if (!success)
|
||||
return false
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
app.db.noteDao().delete(note)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun shareNote(activity: AppCompatActivity, note: Note): Boolean {
|
||||
return app.api.runCatching(activity) {
|
||||
shareNote(note)
|
||||
} != null
|
||||
}
|
||||
|
||||
private suspend fun unshareNote(activity: AppCompatActivity, note: Note): Boolean {
|
||||
return app.api.runCatching(activity) {
|
||||
unshareNote(note)
|
||||
} != null
|
||||
}
|
||||
|
||||
private fun getAdapterForItem(
|
||||
activity: AppCompatActivity,
|
||||
item: Noteable,
|
||||
): RecyclerView.Adapter<*>? {
|
||||
return when (item) {
|
||||
is AnnouncementFull -> AnnouncementsAdapter(activity, mutableListOf(item), null)
|
||||
|
||||
is AttendanceFull -> AttendanceAdapter(
|
||||
activity,
|
||||
showNotes = false,
|
||||
onAttendanceClick = {
|
||||
showItemDetailsDialog(activity, it)
|
||||
},
|
||||
type = AttendanceFragment.VIEW_LIST
|
||||
).also {
|
||||
it.items = mutableListOf(item)
|
||||
}
|
||||
|
||||
is NoticeFull -> {
|
||||
NoticesAdapter(activity, listOf(item))
|
||||
}
|
||||
|
||||
is Date -> {
|
||||
TODO("Date adapter is not yet implemented.")
|
||||
}
|
||||
|
||||
is EventFull -> EventListAdapter(
|
||||
activity = activity,
|
||||
simpleMode = true,
|
||||
showDate = true,
|
||||
showColor = false,
|
||||
showTime = false,
|
||||
markAsSeen = false,
|
||||
showNotes = false,
|
||||
onEventClick = {
|
||||
showItemDetailsDialog(activity, it)
|
||||
},
|
||||
).also {
|
||||
it.setAllItems(listOf(item))
|
||||
}
|
||||
|
||||
is GradeFull -> GradesAdapter(activity, showNotes = false, onGradeClick = {
|
||||
showItemDetailsDialog(activity, it)
|
||||
}).also {
|
||||
it.items = mutableListOf(item)
|
||||
}
|
||||
|
||||
is LessonFull -> LessonChangesAdapter(activity, showNotes = false, onLessonClick = {
|
||||
showItemDetailsDialog(activity, it)
|
||||
}).also {
|
||||
it.items = listOf(item)
|
||||
}
|
||||
|
||||
is MessageFull -> MessagesAdapter(
|
||||
activity = activity,
|
||||
teachers = listOf(),
|
||||
showNotes = false,
|
||||
onMessageClick = null,
|
||||
).also {
|
||||
it.setAllItems(listOf(item))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun showItemDetailsDialog(
|
||||
activity: AppCompatActivity,
|
||||
item: Noteable,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) {
|
||||
when (item) {
|
||||
is AnnouncementFull -> return
|
||||
is AttendanceFull -> AttendanceDetailsDialog(
|
||||
activity = activity,
|
||||
attendance = item,
|
||||
showNotes = false,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
is NoticeFull -> return
|
||||
is Date -> DayDialog(
|
||||
activity = activity,
|
||||
profileId = App.profileId,
|
||||
date = item,
|
||||
showNotes = false,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
is EventFull -> EventDetailsDialog(
|
||||
activity = activity,
|
||||
event = item,
|
||||
showNotes = false,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
is GradeFull -> GradeDetailsDialog(
|
||||
activity = activity,
|
||||
grade = item,
|
||||
showNotes = false,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
is LessonFull -> LessonDetailsDialog(
|
||||
activity = activity,
|
||||
lesson = item,
|
||||
showNotes = false,
|
||||
onShowListener = onShowListener,
|
||||
onDismissListener = onDismissListener,
|
||||
).show()
|
||||
is MessageFull -> return
|
||||
}
|
||||
}
|
||||
|
||||
fun getOwnerTypeText(owner: OwnerType) = when (owner) {
|
||||
OwnerType.ANNOUNCEMENT -> R.string.notes_type_announcement
|
||||
OwnerType.ATTENDANCE -> R.string.notes_type_attendance
|
||||
OwnerType.BEHAVIOR -> R.string.notes_type_behavior
|
||||
OwnerType.DAY -> R.string.notes_type_day
|
||||
OwnerType.EVENT -> R.string.notes_type_event
|
||||
OwnerType.EVENT_SUBJECT -> TODO()
|
||||
OwnerType.GRADE -> R.string.notes_type_grade
|
||||
OwnerType.LESSON -> R.string.notes_type_lesson
|
||||
OwnerType.LESSON_SUBJECT -> TODO()
|
||||
OwnerType.MESSAGE -> R.string.notes_type_message
|
||||
OwnerType.NONE -> throw Exception("NONE is not a valid OwnerType.")
|
||||
}
|
||||
|
||||
fun getOwnerTypeImage(owner: OwnerType) = when (owner) {
|
||||
OwnerType.ANNOUNCEMENT -> R.drawable.ic_announcement
|
||||
OwnerType.ATTENDANCE -> R.drawable.ic_attendance
|
||||
OwnerType.BEHAVIOR -> R.drawable.ic_behavior
|
||||
OwnerType.DAY -> R.drawable.ic_calendar_day
|
||||
OwnerType.EVENT -> R.drawable.ic_calendar_event
|
||||
OwnerType.EVENT_SUBJECT -> TODO()
|
||||
OwnerType.GRADE -> R.drawable.ic_grade
|
||||
OwnerType.LESSON -> R.drawable.ic_timetable
|
||||
OwnerType.LESSON_SUBJECT -> TODO()
|
||||
OwnerType.MESSAGE -> R.drawable.ic_message
|
||||
OwnerType.NONE -> throw Exception("NONE is not a valid OwnerType.")
|
||||
}
|
||||
|
||||
fun configureHeader(
|
||||
activity: AppCompatActivity,
|
||||
noteOwner: Noteable?,
|
||||
b: NoteDialogHeaderBinding,
|
||||
) {
|
||||
if (noteOwner == null) {
|
||||
b.title.isVisible = false
|
||||
b.divider.isVisible = false
|
||||
b.ownerItemList.isVisible = false
|
||||
return
|
||||
}
|
||||
b.ownerItemList.apply {
|
||||
adapter = getAdapterForItem(activity, noteOwner)
|
||||
isNestedScrollingEnabled = false
|
||||
//setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
}
|
||||
|
||||
b.title.setText(getOwnerTypeText(noteOwner.getNoteType()))
|
||||
b.title.setCompoundDrawables(
|
||||
getOwnerTypeImage(noteOwner.getNoteType()).resolveDrawable(activity),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
}
|
||||
}
|
@ -13,16 +13,22 @@ import android.text.style.UnderlineSpan
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.ext.attachToastHint
|
||||
import pl.szczodrzynski.edziennik.ext.hasSet
|
||||
import pl.szczodrzynski.edziennik.ext.replaceSpan
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.StyledTextDialog
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.*
|
||||
@ -271,4 +277,28 @@ class TextStylingManager(private val app: App) {
|
||||
it.button.isEnabled = enable
|
||||
}
|
||||
}
|
||||
|
||||
fun attachToField(
|
||||
activity: AppCompatActivity,
|
||||
textLayout: TextInputLayout,
|
||||
textEdit: TextInputKeyboardEdit,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) {
|
||||
textLayout.endIconDrawable = IconicsDrawable(
|
||||
activity,
|
||||
CommunityMaterial.Icon3.cmd_open_in_new
|
||||
).apply { sizeDp = 24 }
|
||||
textLayout.setEndIconOnClickListener {
|
||||
StyledTextDialog(
|
||||
activity,
|
||||
initialText = textEdit.text,
|
||||
onSuccess = {
|
||||
textEdit.text = it
|
||||
},
|
||||
onShowListener,
|
||||
onDismissListener
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,18 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable;
|
||||
import pl.szczodrzynski.edziennik.ext.TextExtensionsKt;
|
||||
|
||||
public class Date implements Comparable<Date> {
|
||||
public class Date implements Comparable<Date>, Noteable {
|
||||
public int year = 0;
|
||||
public int month = 0;
|
||||
public int day = 0;
|
||||
@ -374,4 +378,51 @@ public class Date implements Comparable<Date> {
|
||||
result = 31 * result + day;
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Note.OwnerType getNoteType() {
|
||||
return Note.OwnerType.DAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNoteOwnerProfileId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNoteOwnerId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getNoteSubstituteText(boolean showNotes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Note> getNotes() {
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotes(@NonNull List<Note> notes) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filterNotes() {
|
||||
Noteable.DefaultImpls.filterNotes(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNotes() {
|
||||
return Noteable.DefaultImpls.hasNotes(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReplacingNotes() {
|
||||
return Noteable.DefaultImpls.hasReplacingNotes(this);
|
||||
}
|
||||
}
|
||||
|
21
app/src/main/res/drawable/ic_announcement.xml
Normal file
21
app/src/main/res/drawable/ic_announcement.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M17.4,33H15v-4h4l0.4,1.5C19.7,31.8 18.7,33 17.4,33zM37,36c0,0 -11.8,-7 -18,-7V15c5.8,0 18,-7 18,-7V36z"
|
||||
android:fillColor="#90CAF9"/>
|
||||
<path
|
||||
android:pathData="M9,17A5,5 0,1 0,9 27,5 5,0 1,0 9,17zM40,19h-3v6h3c1.7,0 3,-1.3 3,-3S41.7,19 40,19z"
|
||||
android:fillColor="#283593"/>
|
||||
<path
|
||||
android:pathData="M18.6,41.2c-0.9,0.6 -2.5,1.2 -4.6,1.4c-0.6,0.1 -1.2,-0.3 -1.4,-1L8.2,27.9c0,0 8.8,-6.2 8.8,1.1c0,5.5 1.5,8.4 2.2,9.5c0.5,0.7 0.5,1.6 0,2.3C19,41 18.8,41.1 18.6,41.2z"
|
||||
android:fillColor="#283593"/>
|
||||
<path
|
||||
android:pathData="M9,29h10V15H9c-1.1,0 -2,0.9 -2,2v10C7,28.1 7.9,29 9,29z"
|
||||
android:fillColor="#3F51B5"/>
|
||||
<path
|
||||
android:pathData="M38,38L38,38c-1.1,0 -2,-0.9 -2,-2V8c0,-1.1 0.9,-2 2,-2h0c1.1,0 2,0.9 2,2v28C40,37.1 39.1,38 38,38z"
|
||||
android:fillColor="#42A5F5"/>
|
||||
</vector>
|
33
app/src/main/res/drawable/ic_attendance.xml
Normal file
33
app/src/main/res/drawable/ic_attendance.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M40,45L8,45 8,3 30,3 40,13z"
|
||||
android:fillColor="#90CAF9"/>
|
||||
<path
|
||||
android:pathData="M38.5,14L29,14 29,4.5z"
|
||||
android:fillColor="#E1F5FE"/>
|
||||
<path
|
||||
android:pathData="M13,28H25V30H13zM13,20H25V22H13zM13,36H25V38H13z"
|
||||
android:fillColor="#1976D2"/>
|
||||
<path
|
||||
android:pathData="M29,40h6v-6h-6V40z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
<path
|
||||
android:pathData="M31,36H33V38H31z"
|
||||
android:fillColor="#BBDEFB"/>
|
||||
<path
|
||||
android:pathData="M32.91,30.59L31.5,32 28,28.5 29.41,27.09 31.5,29.18z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
<path
|
||||
android:pathData="M35.972,27.528L31.5,32 30.09,30.59 31.5,29.18 34.562,26.118z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
<path
|
||||
android:pathData="M28.9997,19.414L30.4139,17.9998L34.9996,22.5853L33.5854,23.9995z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
<path
|
||||
android:pathData="M28.9998,22.5861L33.5853,18.0004L34.9995,19.4146L30.414,24.0003z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
</vector>
|
18
app/src/main/res/drawable/ic_behavior.xml
Normal file
18
app/src/main/res/drawable/ic_behavior.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M44,24c0,11 -9,20 -20,20S4,35 4,24S13,4 24,4S44,13 44,24z"
|
||||
android:fillColor="#FFCA28"/>
|
||||
<path
|
||||
android:pathData="M33,27c0,0.86 -0.27,4.11 -1.69,7.01H16.65c-1.42,-2.9 -1.7,-6.15 -1.65,-7.01H33z"
|
||||
android:fillColor="#5D4037"/>
|
||||
<path
|
||||
android:pathData="M13.8,21.6l-1.6,-1.2C12.3,20.3 14,18 17,18s4.7,2.3 4.8,2.4l-1.6,1.2c0,0 -1.3,-1.6 -3.2,-1.6S13.8,21.6 13.8,21.6zM27.8,21.6l-1.6,-1.2C26.3,20.3 28,18 31,18s4.7,2.3 4.8,2.4l-1.6,1.2c0,0 -1.3,-1.6 -3.2,-1.6S27.8,21.6 27.8,21.6z"
|
||||
android:fillColor="#B76C09"/>
|
||||
<path
|
||||
android:pathData="M31.31,34.01C30.04,36.65 27.81,39 24,39s-6.05,-2.35 -7.35,-4.99c0,0 1.975,-2.01 7.35,-2.01S31.31,34.01 31.31,34.01z"
|
||||
android:fillColor="#C62828"/>
|
||||
</vector>
|
15
app/src/main/res/drawable/ic_calendar_day.xml
Normal file
15
app/src/main/res/drawable/ic_calendar_day.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M43,38c0,2.209 -1.791,4 -4,4H9c-2.209,0 -4,-1.791 -4,-4V10c0,-2.209 1.791,-4 4,-4h30c2.209,0 4,1.791 4,4V38z"
|
||||
android:fillColor="#CFD8DC"/>
|
||||
<path
|
||||
android:pathData="M10,10H38V14H10z"
|
||||
android:fillColor="#F44336"/>
|
||||
<path
|
||||
android:pathData="M16,22H38V26H16zM16,28H38V32H16zM16,34H38V38H16zM10,22H14V26H10zM10,28H14V32H10zM10,34H14V38H10zM16,16H38V20H16zM10,16H14V20H10z"
|
||||
android:fillColor="#90A4AE"/>
|
||||
</vector>
|
27
app/src/main/res/drawable/ic_calendar_event.xml
Normal file
27
app/src/main/res/drawable/ic_calendar_event.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M5,39V15h38v24c0,2.2 -1.8,4 -4,4H9C6.8,43 5,41.2 5,39"
|
||||
android:fillColor="#CFD8DC"/>
|
||||
<path
|
||||
android:pathData="M5,25h38v8H5V25z"
|
||||
android:fillColor="#ECEFF1"/>
|
||||
<path
|
||||
android:pathData="M13,27h4v4h-4V27zM19,27h4v4h-4V27zM31,27h4v4h-4V27z"
|
||||
android:fillColor="#B0BEC5"/>
|
||||
<path
|
||||
android:pathData="M13,21h4v4h-4V21zM19,21h4v4h-4V21zM25,21h4v4h-4V21zM31,21h4v4h-4V21zM13,33h4v4h-4V33zM19,33h4v4h-4V33zM25,33h4v4h-4V33zM31,33h4v4h-4V33z"
|
||||
android:fillColor="#90A4AE"/>
|
||||
<path
|
||||
android:pathData="M25,27h4v4h-4V27zM43,11v6H5v-6c0,-2.2 1.8,-4 4,-4h30C41.2,7 43,8.8 43,11"
|
||||
android:fillColor="#F44336"/>
|
||||
<path
|
||||
android:pathData="M36,11c0,1.7 -1.3,3 -3,3s-3,-1.3 -3,-3s1.3,-3 3,-3C34.7,8 36,9.3 36,11M18,11c0,1.7 -1.3,3 -3,3s-3,-1.3 -3,-3s1.3,-3 3,-3S18,9.3 18,11"
|
||||
android:fillColor="#B71C1C"/>
|
||||
<path
|
||||
android:pathData="M33,4c-1.1,0 -2,0.9 -2,2v5c0,1.1 0.9,2 2,2s2,-0.9 2,-2V6C35,4.9 34.1,4 33,4M15,4c-1.1,0 -2,0.9 -2,2v5c0,1.1 0.9,2 2,2s2,-0.9 2,-2V6C17,4.9 16.1,4 15,4"
|
||||
android:fillColor="#B0BEC5"/>
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_grade.xml
Normal file
12
app/src/main/res/drawable/ic_grade.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M44,24c0,11.045 -8.955,20 -20,20S4,35.045 4,24S12.955,4 24,4S44,12.955 44,24z"
|
||||
android:fillColor="#F44336"/>
|
||||
<path
|
||||
android:pathData="M24,11l3.898,7.898l8.703,1.301l-6.301,6.102l1.5,8.699L24,30.898L16.199,35l1.5,-8.699l-6.301,-6.102l8.703,-1.301L24,11z"
|
||||
android:fillColor="#FFCA28"/>
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_message.xml
Normal file
12
app/src/main/res/drawable/ic_message.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M40,10H8c-2.209,0 -4,1.791 -4,4v20c0,2.209 1.791,4 4,4h32c2.209,0 4,-1.791 4,-4V14C44,11.791 42.209,10 40,10z"
|
||||
android:fillColor="#2196F3"/>
|
||||
<path
|
||||
android:pathData="M44,14.025c0,-0.465 -0.095,-0.904 -0.24,-1.32L24,27.025L4.241,12.705C4.095,13.121 4,13.561 4,14.025V15l20,14.495L44,15V14.025z"
|
||||
android:fillColor="#0D47A1"/>
|
||||
</vector>
|
18
app/src/main/res/drawable/ic_note.xml
Normal file
18
app/src/main/res/drawable/ic_note.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M8,39.001v-30c0,-2.2 1.8,-4 4,-4h24c2.2,0 4,1.8 4,4v30c0,2.2 -1.8,4 -4,4H12C9.8,43.001 8,41.201 8,39.001z"
|
||||
android:fillColor="#42a5f5"/>
|
||||
<path
|
||||
android:pathData="M14,8.001c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2s0.895,2 2,2S14,9.105 14,8.001zM22,8.001c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2s0.895,2 2,2S22,9.105 22,8.001zM30,8.001c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2s0.895,2 2,2S30,9.105 30,8.001zM38,8.001c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2s0.895,2 2,2S38,9.105 38,8.001z"
|
||||
android:fillColor="#1e88e5"/>
|
||||
<path
|
||||
android:pathData="M11,8.001v-4c0,-0.6 0.4,-1 1,-1l0,0c0.6,0 1,0.4 1,1v4c0,0.6 -0.4,1 -1,1l0,0C11.4,9.001 11,8.601 11,8.001zM19,8.001v-4c0,-0.6 0.4,-1 1,-1l0,0c0.6,0 1,0.4 1,1v4c0,0.6 -0.4,1 -1,1l0,0C19.4,9.001 19,8.601 19,8.001zM27,8.001v-4c0,-0.6 0.4,-1 1,-1l0,0c0.6,0 1,0.4 1,1v4c0,0.6 -0.4,1 -1,1l0,0C27.4,9.001 27,8.601 27,8.001zM35,8.001v-4c0,-0.6 0.4,-1 1,-1l0,0c0.6,0 1,0.4 1,1v4c0,0.6 -0.4,1 -1,1l0,0C35.4,9.001 35,8.601 35,8.001z"
|
||||
android:fillColor="#cfd8dc"/>
|
||||
<path
|
||||
android:pathData="M14,19.001h19.993v2H14V19.001zM14.007,15.001H34v2H14.007V15.001zM14,30.001h17v2H14V30.001zM27,34.001h6.993v2H27V34.001zM14,34.001h11v2H14V34.001zM21,23.001h-7v2h7V23.001z"
|
||||
android:fillColor="#1565c0"/>
|
||||
</vector>
|
@ -1,30 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="m18,100v-60h92v60c0,5.523 -4.477,10 -10,10h-72c-5.523,0 -10,-4.477 -10,-10z"
|
||||
android:fillColor="#ffc662"/>
|
||||
android:pathData="M42,38c0,2.209 -1.791,4 -4,4H10c-2.209,0 -4,-1.791 -4,-4V10c0,-2.209 1.791,-4 4,-4h28c2.209,0 4,1.791 4,4V38z"
|
||||
android:fillColor="#CFD8DC"/>
|
||||
<path
|
||||
android:pathData="m110,29.602v16.398h-92v-16.398c0,-5.309 4.332,-9.602 9.684,-9.602h72.633c5.352,0 9.684,4.293 9.684,9.602"
|
||||
android:fillColor="#ff634f"/>
|
||||
android:pathData="M11,18H16V23H11zM18,18H23V23H18zM25,18H30V23H25zM32,18H37V23H32zM32,25H37V30H32zM11,25H16V30H11zM11,32H16V37H11zM18,25H23V30H18zM25,25H30V30H25zM18,32H23V37H18zM25,32H30V37H25zM32,32H37V37H32z"
|
||||
android:fillColor="#90A4AE"/>
|
||||
<path
|
||||
android:pathData="m43,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z"
|
||||
android:fillColor="#888"/>
|
||||
android:pathData="M11,11H16V16H11zM18,11H23V16H18zM25,11H30V16H25zM32,11H37V16H32z"
|
||||
android:fillColor="#F44336"/>
|
||||
<path
|
||||
android:pathData="m85,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z"
|
||||
android:fillColor="#888"/>
|
||||
android:pathData="M38,26c-0.338,0 -0.669,0.023 -1,0.05V30h-5v-2.382c-1.817,1.052 -3.329,2.565 -4.382,4.382H30v5h-3.95C26.023,37.331 26,37.662 26,38c0,1.405 0.254,2.747 0.697,4H38c2.209,0 4,-1.791 4,-4V26.697C40.747,26.254 39.405,26 38,26z"
|
||||
android:fillColor="#ECEFF1"/>
|
||||
<path
|
||||
android:pathData="m48,74h-8c-2.211,0 -4,-1.789 -4,-4v-8c0,-2.211 1.789,-4 4,-4h8c2.211,0 4,1.789 4,4v8c0,2.211 -1.789,4 -4,4zM72,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM52,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM72,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4z"
|
||||
android:fillColor="#ffe79f"/>
|
||||
android:pathData="M37,30v-3.95c-1.812,0.15 -3.506,0.703 -5,1.568V30H37zM30,32h-2.382c-0.865,1.494 -1.418,3.188 -1.568,5H30V32z"
|
||||
android:fillColor="#CFD8DC"/>
|
||||
<path
|
||||
android:pathData="m124,104c0,11.047 -8.953,20 -20,20 -11.047,0 -20,-8.953 -20,-20 0,-11.047 8.953,-20 20,-20 11.047,0 20,8.953 20,20z"
|
||||
android:fillColor="#fff"/>
|
||||
android:pathData="M48,38c0,5.5 -4.5,10 -10,10s-10,-4.5 -10,-10s4.5,-10 10,-10S48,32.5 48,38"
|
||||
android:fillColor="#F44336"/>
|
||||
<path
|
||||
android:pathData="m104,80c-13.254,0 -24,10.746 -24,24 0,13.254 10.746,24 24,24 13.254,0 24,-10.746 24,-24 0,-13.254 -10.746,-24 -24,-24zM104,120c-8.836,0 -16,-7.164 -16,-16 0,-8.836 7.164,-16 16,-16 8.836,0 16,7.164 16,16 0,8.836 -7.164,16 -16,16z"
|
||||
android:fillColor="#1f80e5"/>
|
||||
android:pathData="M45,38c0,3.9 -3.1,7 -7,7s-7,-3.1 -7,-7s3.1,-7 7,-7S45,34.1 45,38"
|
||||
android:fillColor="#EEEEEE"/>
|
||||
<path
|
||||
android:pathData="m113.41,110.59 l-5.563,-5.563c0.086,-0.328 0.148,-0.668 0.148,-1.023 0,-1.477 -0.809,-2.754 -2,-3.445v-6.555c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2v6.555c-1.191,0.691 -2,1.969 -2,3.445 0,2.211 1.789,4 4,4 0.355,0 0.695,-0.063 1.023,-0.148l5.563,5.563c0.391,0.391 0.902,0.586 1.414,0.586s1.023,-0.195 1.414,-0.586c0.781,-0.781 0.781,-2.047 0,-2.828z"
|
||||
android:fillColor="#919191"/>
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M42.4,41.1l-2.9,-2.9c0,-0.1 0,-0.1 0,-0.2c0,-0.4 -0.2,-0.8 -0.5,-1.1V33h-2v3.9c-0.3,0.3 -0.5,0.7 -0.5,1.1c0,0.8 0.7,1.5 1.5,1.5h0.1l2.9,2.9L42.4,41.1z"/>
|
||||
</vector>
|
||||
|
@ -86,6 +86,14 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/legend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="[ - ] dodano notatki" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -230,6 +238,15 @@
|
||||
tools:text="12345" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
||||
|
@ -52,7 +52,7 @@
|
||||
tools:text="Nieobecność nieusprawiedliwiona" />
|
||||
|
||||
|
||||
<TextView
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/subjectName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
45
app/src/main/res/layout/card_home_notes.xml
Normal file
45
app/src/main/res/layout/card_home_notes.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-10-28.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:layout_margin="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/card_notes_header_title"
|
||||
android:textAppearance="@style/NavView.TextView.Title" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/noData"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_margin="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/notes_no_data"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/note_list_item"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/addNote"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/notes_action_add" />
|
||||
</LinearLayout>
|
@ -31,13 +31,24 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
android:textIsSelectable="true"
|
||||
android:visibility="gone"
|
||||
tools:text="8:00 - 14:20 (7 lekcji, 6 godzin, 20 minut)"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/legend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="[ - ] dodano notatki" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/lessonChangesFrame"
|
||||
android:layout_width="match_parent"
|
||||
@ -74,7 +85,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
@ -106,5 +118,13 @@
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/event_list_item"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
@ -156,7 +156,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
android:text="@string/dialog_event_details_topic"/>
|
||||
|
||||
@ -172,7 +172,7 @@
|
||||
android:id="@+id/bodyTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/dialog_event_details_body"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
@ -197,7 +197,7 @@
|
||||
android:id="@+id/attachmentsTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/dialog_event_details_attachments"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
@ -279,6 +279,14 @@
|
||||
android:text="\uf436"
|
||||
android:textSize="20sp" />
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</layout>
|
||||
|
@ -113,6 +113,14 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/legend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="[ - ] dodano notatki" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -307,6 +315,15 @@
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/configure" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
||||
|
@ -155,6 +155,14 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/legend"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="[ - ] dodano notatki" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -356,8 +364,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
@ -390,6 +399,13 @@
|
||||
tools:visibility="visible"
|
||||
tools:listitem="@layout/event_list_item" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</layout>
|
||||
|
@ -36,7 +36,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/gradeDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -198,7 +198,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible"
|
||||
tools:visibility="visible">
|
||||
@ -260,6 +260,14 @@
|
||||
android:visibility="gone"
|
||||
tools:drawableTop="@android:drawable/stat_sys_download" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/notesButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/notes_button" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
|
@ -34,7 +34,7 @@
|
||||
tools:text="JP"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/messageSubject"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
143
app/src/main/res/layout/note_details_dialog.xml
Normal file
143
app/src/main/res/layout/note_details_dialog.xml
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-10-23.
|
||||
-->
|
||||
|
||||
<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">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="pl.szczodrzynski.edziennik.data.db.entity.Note" />
|
||||
|
||||
<variable
|
||||
name="note"
|
||||
type="Note" />
|
||||
</data>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/header"
|
||||
layout="@layout/note_dialog_header" />
|
||||
|
||||
<include
|
||||
layout="@layout/note_dialog_subtitle"
|
||||
app:text="@{@string/notes_details_dialog_title}" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/dialog_event_details_added_by"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsTextView
|
||||
android:id="@+id/addedBy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="18 grudnia, 23:17 przez Janósz Kowalski" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/idsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/notes_details_id"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{String.valueOf(note.id)}"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="1635073956543" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/notes_details_owner_id"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{String.valueOf(note.ownerId)}"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="3875623" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:isVisible="@{note.topic != null}"
|
||||
android:text="@string/dialog_event_details_topic"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:isVisible="@{note.topic != null}"
|
||||
android:text="@{note.topicHtml}"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/dialog_event_details_body"
|
||||
android:textAppearance="@style/NavView.TextView.Helper" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{note.bodyHtml}"
|
||||
android:textAppearance="@style/NavView.TextView.Medium"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</layout>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user