[Messages] Add new attachments view. Allow replying to deleted messages.

This commit is contained in:
Kuba Szczodrzyński 2020-04-05 23:56:56 +02:00
parent 0e4d609bbf
commit 8a4866cb62
3 changed files with 21 additions and 269 deletions

View File

@ -5,28 +5,16 @@
package pl.szczodrzynski.edziennik.ui.modules.messages package pl.szczodrzynski.edziennik.ui.modules.messages
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.text.Html import android.text.Html
import android.text.TextUtils
import android.view.Gravity.CENTER_VERTICAL
import android.view.Gravity.END
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ProgressBar
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsColor
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.IconicsSize
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -34,9 +22,6 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.TYPE_FINISHED
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore.Companion.LOGIN_TYPE_IDZIENNIK import pl.szczodrzynski.edziennik.data.db.entity.LoginStore.Companion.LOGIN_TYPE_IDZIENNIK
@ -47,14 +32,9 @@ import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
import pl.szczodrzynski.edziennik.utils.Anim import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.getStringFromFile
import pl.szczodrzynski.edziennik.utils.Utils.readableFileSize
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.colorAttr import pl.szczodrzynski.navlib.colorAttr
import java.io.File
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.min import kotlin.math.min
@ -72,7 +52,6 @@ class MessageFragment : Fragment(), CoroutineScope {
get() = job + Dispatchers.Main get() = job + Dispatchers.Main
private lateinit var message: MessageFull private lateinit var message: MessageFull
private var attachmentList = mutableListOf<Attachment>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null activity = (getActivity() as MainActivity?) ?: return null
@ -238,9 +217,9 @@ class MessageFragment : Fragment(), CoroutineScope {
b.subject.text = message.subject b.subject.text = message.subject
b.replyButton.isVisible = message.type == TYPE_RECEIVED b.replyButton.isVisible = message.type == TYPE_RECEIVED || message.type == TYPE_DELETED
b.deleteButton.isVisible = message.type == TYPE_RECEIVED b.deleteButton.isVisible = message.type == TYPE_RECEIVED
if (message.type == TYPE_RECEIVED) { if (message.type == TYPE_RECEIVED || message.type == TYPE_DELETED) {
activity.navView.apply { activity.navView.apply {
bottomBar.apply { bottomBar.apply {
fabEnable = true fabEnable = true
@ -291,173 +270,21 @@ class MessageFragment : Fragment(), CoroutineScope {
MessagesFragment.pageSelection = min(message.type, 1) MessagesFragment.pageSelection = min(message.type, 1)
} }
private val attachmentOnClick = { v: View ->
if (v.tag is Int) {
downloadAttachment(v.tag as Int)
}
}
private val attachmentOnLongClick = { v: View ->
(v.tag as? Int)?.let { tag ->
val popupMenu = PopupMenu(v.context, v)
popupMenu.menu.add(0, tag, 0, R.string.messages_attachment_download_again)
popupMenu.setOnMenuItemClickListener {
downloadAttachment(it.itemId, forceDownload = true)
true
}
popupMenu.show()
}
true
}
private fun showAttachments() { private fun showAttachments() {
if (message.attachmentIds != null) { if (message.attachmentIds.isNullOrEmpty() || message.attachmentNames.isNullOrEmpty()) {
val insertPoint = b.attachments b.attachmentsTitle.isVisible = false
insertPoint.removeAllViews() b.attachmentsFragment.isVisible = false
val chipLayoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
chipLayoutParams.setMargins(0, 8.dp, 0, 0)
val progressLayoutParams = FrameLayout.LayoutParams(18.dp, 18.dp)
progressLayoutParams.setMargins(8.dp, 0, 8.dp, 0)
progressLayoutParams.gravity = END or CENTER_VERTICAL
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
message.attachmentNames?.forEachIndexed { index, name ->
val messageId = message.id
val id = message.attachmentIds?.getOrNull(index) ?: return@forEachIndexed
val size = message.attachmentSizes?.getOrNull(index) ?: return@forEachIndexed
// create the parent
val attachmentLayout = FrameLayout(b.root.context)
attachmentLayout.setPadding(16.dp, 0, 16.dp, 0)
val attachmentChip = Chip(attachmentLayout.context)
//attachmentChip.setChipBackgroundColorResource(ThemeUtils.getChipColorRes());
attachmentChip.layoutParams = chipLayoutParams
attachmentChip.chipMinHeight = 40.dp.toFloat()
//attachmentChip.height = Utils.dpToPx(40)
// show the file size or not
if (size == -1L)
attachmentChip.text = getString(R.string.messages_attachment_no_size_format, name)
else
attachmentChip.text = getString(R.string.messages_attachment_format, name, readableFileSize(size))
attachmentChip.ellipsize = TextUtils.TruncateAt.MIDDLE
// create an icon for the attachment
val icon: IIcon = when (Utils.getExtensionFromFileName(name)) {
"doc", "docx", "odt", "rtf" -> SzkolnyFont.Icon.szf_file_word_outline
"xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline
"ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline
"pdf" -> SzkolnyFont.Icon.szf_file_pdf_outline
"mp3", "wav", "aac" -> SzkolnyFont.Icon.szf_file_music_outline
"mp4", "avi", "3gp", "mkv", "flv" -> SzkolnyFont.Icon.szf_file_video_outline
"jpg", "jpeg", "png", "bmp", "gif" -> SzkolnyFont.Icon.szf_file_image_outline
"zip", "rar", "tar", "7z" -> SzkolnyFont.Icon.szf_zip_box_outline
"html", "cpp", "c", "h", "css", "java", "py" -> SzkolnyFont.Icon.szf_file_code_outline
else -> CommunityMaterial.Icon.cmd_file_document_outline
}
attachmentChip.chipIcon = IconicsDrawable(activity).color(IconicsColor.colorRes(R.color.colorPrimary)).icon(icon).size(IconicsSize.dp(26))
attachmentChip.closeIcon = IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_check).size(IconicsSize.dp(18)).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorPrimary)))
attachmentChip.isCloseIconVisible = false
// set the object's index in the attachmentList as the tag
attachmentChip.tag = index
attachmentChip.onClick(attachmentOnClick)
attachmentChip.onLongClick(attachmentOnLongClick)
attachmentLayout.addView(attachmentChip)
val attachmentProgress = ProgressBar(attachmentLayout.context)
attachmentProgress.layoutParams = progressLayoutParams
attachmentProgress.visibility = View.GONE
attachmentLayout.addView(attachmentProgress)
insertPoint.addView(attachmentLayout)
// create an object and add to the list
val a = Attachment(App.profileId, messageId, id, name, size, attachmentLayout, attachmentChip, attachmentProgress)
attachmentList.add(a)
// check if the file is already downloaded. Show the check icon if necessary and set `downloaded` to true.
checkAttachment(a)
}
} else {
// no attachments found
b.attachmentsTitle.visibility = View.GONE
}
}
private fun downloadAttachment(index: Int, forceDownload: Boolean = false) {
val attachment = attachmentList[index]
if (!forceDownload && attachment.downloaded != null) {
Utils.openFile(activity, File(attachment.downloaded))
return
}
attachment.chip.isEnabled = false
attachment.chip.setTextColor(Themes.getSecondaryTextColor(activity))
attachment.progressBar.visibility = View.VISIBLE
EdziennikTask.attachmentGet(
App.profileId,
message,
attachment.attachmentId,
attachment.attachmentName
).enqueue(activity)
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onAttachmentGetEvent(event: AttachmentGetEvent) {
EventBus.getDefault().removeStickyEvent(event)
attachmentList.firstOrNull { it.profileId == event.profileId
&& it.messageId == event.ownerId
&& it.attachmentId == event.attachmentId }?.let { attachment ->
when (event.eventType) {
TYPE_FINISHED -> {
// save the downloaded file name
attachment.downloaded = event.fileName
// set the correct name (and size)
if (attachment.attachmentSize == -1L)
attachment.chip.text = getString(R.string.messages_attachment_no_size_format, attachment.attachmentName)
else
attachment.chip.text = getString(R.string.messages_attachment_format, attachment.attachmentName, readableFileSize(attachment.attachmentSize))
// hide the progress bar and show a tick icon
attachment.progressBar.visibility = View.GONE
attachment.chip.isEnabled = true
attachment.chip.setTextColor(Themes.getPrimaryTextColor(activity))
attachment.chip.isCloseIconVisible = true
// open the file
Utils.openFile(activity, File(attachment.downloaded))
}
TYPE_PROGRESS -> {
attachment.chip.text = getString(R.string.messages_attachment_downloading_format, attachment.attachmentName, event.bytesWritten.toFloat() / 1000000)
}
}
}
}
private fun checkAttachment(attachment: Attachment) {
val storageDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu")
storageDir.mkdirs()
val attachmentDataFile = File(storageDir, "." + attachment.profileId + "_" + attachment.messageId + "_" + attachment.attachmentId)
if (attachmentDataFile.exists()) {
try {
val attachmentFileName = getStringFromFile(attachmentDataFile)
val attachmentFile = File(attachmentFileName)
if (attachmentFile.exists()) {
attachment.downloaded = attachmentFileName
attachment.chip.isCloseIconVisible = true
}
} catch (e: Exception) {
e.printStackTrace()
//app.apiEdziennik.guiReportException(activity, 355, e)
} }
else {
b.attachmentsTitle.isVisible = true
b.attachmentsFragment.isVisible = true
b.attachmentsFragment.init(Bundle().also {
it.putInt("profileId", message.profileId)
it.putLongArray("attachmentIds", message.attachmentIds!!.toLongArray())
it.putStringArray("attachmentNames", message.attachmentNames!!.toTypedArray())
//if (message.attachmentSizes.isNotNullNorEmpty())
// it.putLongArray("attachmentSizes", message.attachmentSizes!!.toLongArray())
}, owner = message)
} }
} }
@ -473,20 +300,4 @@ class MessageFragment : Fragment(), CoroutineScope {
super.onDestroy() super.onDestroy()
job.cancel() job.cancel()
} }
private class Attachment(
var profileId: Int,
var messageId: Long,
var attachmentId: Long,
var attachmentName: String,
var attachmentSize: Long,
var parent: FrameLayout,
var chip: Chip,
var progressBar: ProgressBar
) {
/**
* An absolute path of the downloaded file. `null` if not downloaded yet.
*/
internal var downloaded: String? = null
}
} }

View File

@ -98,7 +98,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
(b.list.layoutManager as? LinearLayoutManager)?.let { layoutManager -> (b.list.layoutManager as? LinearLayoutManager)?.let { layoutManager ->
if (topPosition != NO_POSITION && topPosition > layoutManager.findLastCompletelyVisibleItemPosition()) { if (topPosition != NO_POSITION && topPosition > layoutManager.findLastCompletelyVisibleItemPosition()) {
b.list.scrollToPosition(topPosition) b.list.scrollToPosition(topPosition)
} else if (bottomPosition != NO_POSITION && bottomPosition < layoutManager.findFirstCompletelyVisibleItemPosition()) { } else if (bottomPosition != NO_POSITION && bottomPosition < layoutManager.findFirstVisibleItemPosition()) {
b.list.scrollToPosition(bottomPosition) b.list.scrollToPosition(bottomPosition)
} }
topPosition = NO_POSITION topPosition = NO_POSITION
@ -121,7 +121,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
super.onDestroy() super.onDestroy()
if (!isAdded) return if (!isAdded) return
onPageDestroy?.invoke(position, Bundle( onPageDestroy?.invoke(position, Bundle(
"topPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findFirstCompletelyVisibleItemPosition(), "topPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition(),
"bottomPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition() "bottomPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition()
)) ))
} }

View File

@ -179,71 +179,12 @@
android:text="Załączniki:" android:text="Załączniki:"
android:textAppearance="@style/NavView.TextView.Subtitle" /> android:textAppearance="@style/NavView.TextView.Subtitle" />
<LinearLayout <pl.szczodrzynski.edziennik.ui.modules.views.AttachmentsView
android:id="@+id/attachments" android:id="@+id/attachmentsFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_marginHorizontal="8dp"
android:paddingBottom="8dp"> android:layout_marginBottom="8dp"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingLeft="16dp"
android:paddingRight="16dp"
tools:visibility="visible">
<com.google.android.material.chip.Chip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:text="Testowy plik.pdf"
android:visibility="visible"
app:chipBackgroundColor="@color/mtrl_chip_background_color"
app:chipIcon="@drawable/googleg_standard_color_18"
app:chipMinHeight="36dp"
app:chipSurfaceColor="@color/mtrl_chip_surface_color"
app:closeIcon="@drawable/ic_error_outline"
app:closeIconVisible="true" />
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical|end"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:paddingLeft="16dp"
android:paddingRight="16dp"
tools:visibility="visible">
<com.google.android.material.chip.Chip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:text="Wyniki sprawdzianu z matematyki.pdf"
android:visibility="visible"
app:chipIcon="@drawable/googleg_standard_color_18" />
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical|end"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
</FrameLayout>
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"