Skip to content

Instantly share code, notes, and snippets.

@TalbotGooday
Created October 29, 2021 07:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TalbotGooday/0c36c67bf72db296f43e420a2f384a38 to your computer and use it in GitHub Desktop.
Save TalbotGooday/0c36c67bf72db296f43e420a2f384a38 to your computer and use it in GitHub Desktop.
MediaView
package com.comunitee.ui.widgets.media_view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.annotation.FloatRange
import androidx.appcompat.widget.AppCompatImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.doOnLayout
import androidx.core.view.doOnPreDraw
import com.aghajari.zoomhelper.ZoomHelper
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.comunitee.model.feed.PostMedia
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.firebase.storage.FirebaseStorage
import kotlin.math.min
import kotlin.random.Random
class MediaView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
private val data = mutableListOf<PostMedia>()
private var isComment = false
private val firestore = FirebaseStorage.getInstance()
private var players = mutableListOf<ExoPlayer>()
private var _width = 0
private var _height = 0
init {
Log.d("Size::", "============>")
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = suggestedMinimumWidth
val width = measureDimension(desiredWidth, widthMeasureSpec)
val height = getContentHeight(width)
Log.d("Size::onMeasure", "$desiredWidth, $width, $height")
_width = width
_height = height
setMeasuredDimension(width, height)
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChildWithMargins(child, widthMeasureSpec, width, MeasureSpec.EXACTLY, height)
if (child.visibility != GONE) {
child.measure(
MeasureSpec.makeMeasureSpec(
width,
MeasureSpec.EXACTLY
),
MeasureSpec.makeMeasureSpec(
height,
MeasureSpec.EXACTLY
)
)
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
refresh()
}
private fun refresh() {
// setMedias(data)
// requestLayout()
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopPlayers()
removeMediaViews()
}
fun setMedias(data: List<PostMedia>) {
stopPlayers()
Log.d("Size::setMedias", "data: ${data.size}")
this.data.clear()
this.data.addAll(data)
removeMediaViews()
addViews()
requestLayout()
}
private fun removeMediaViews() {
removeAllViews()
}
private fun stopPlayers() {
players.forEach {
it.stop()
it.release()
}
players.clear()
}
private fun addViews() {
val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
when (data.size) {
1 -> {
addView(addOneMediaView(data.first()), layoutParams)
}
2 -> {
addView(addTwoMediaViews(data.take(data.size)), layoutParams)
}
3 -> {
addView(addThreeMediaViews(data.take(data.size)), layoutParams)
}
4 -> {
addView(addFourMediaViews(data.take(data.size)), layoutParams)
}
5 -> {
addView(addFiveMediaViews(data.take(data.size)), layoutParams)
}
}
}
private fun addOneMediaView(media: PostMedia): View {
return getViewByType(media).apply { tag = "media" }
}
private fun addTwoMediaViews(media: List<PostMedia>): View {
return addMediaViews(media, LinearLayout.HORIZONTAL)
}
private fun addThreeMediaViews(media: List<PostMedia>): LinearLayout {
return LinearLayout(context).apply {
setBackgroundColor(randomColor())
tag = "media"
orientation = LinearLayout.HORIZONTAL
val view = getViewByType(media.first())
addView(view)
val secondView = addMediaViews(
media = media.subList(1, 3),
requestedOrientation = LinearLayout.VERTICAL
)
addView(secondView)
}
}
private fun addFourMediaViews(media: List<PostMedia>): LinearLayout {
return LinearLayout(context).apply {
setBackgroundColor(randomColor())
tag = "media"
orientation = LinearLayout.HORIZONTAL
arrayOf(media.subList(0, 2), media.subList(2, 4)).forEach { _media ->
val firstView = addMediaViews(
media = _media,
requestedOrientation = LinearLayout.VERTICAL
)
addView(firstView)
}
}
}
private fun addFiveMediaViews(media: List<PostMedia>): LinearLayout {
return LinearLayout(context).apply {
setBackgroundColor(randomColor())
tag = "media"
orientation = LinearLayout.HORIZONTAL
arrayOf(media.subList(0, 2), media.subList(2, 5)).forEach { _media ->
val firstView = addMediaViews(
media = _media,
requestedOrientation = LinearLayout.VERTICAL
)
addView(firstView)
}
}
}
private fun addMediaViews(
media: List<PostMedia>,
requestedOrientation: Int = LinearLayout.HORIZONTAL
): LinearLayout {
return LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
1f
)
setBackgroundColor(randomColor())
tag = "media"
orientation = requestedOrientation
media.forEach { mediaItem ->
val childView = getViewByType(mediaItem)
addView(childView)
}
}
}
private fun getViewByType(firstMedia: PostMedia): View {
return FrameLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
1f
)
setBackgroundColor(randomColor())
// val view = if (firstMedia.mediaType == PostMedia.Type.IMAGE) {
// createImageView(firstMedia)
// } else {
// createVideoView(firstMedia)
// }
//
// addView(view)
}
}
private fun createVideoView(media: PostMedia): View {
return StyledPlayerView(context).apply {
useController = false
val player = SimpleExoPlayer.Builder(context).build()
players.add(player)
player.repeatMode = Player.REPEAT_MODE_ALL
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
setShutterBackgroundColor(Color.WHITE)
setPlayer(player)
firestore.getReferenceFromUrl(media.url).downloadUrl.addOnSuccessListener {
player.volume = 0f
player.setMediaItem(MediaItem.fromUri(it), true)
player.prepare()
player.play()
}.addOnFailureListener { it.printStackTrace() }
}
}
private fun createImageView(media: PostMedia): View {
return AppCompatImageView(context).apply {
scaleType = ImageView.ScaleType.CENTER_CROP
loadImage(media.url, this)
}
}
private fun measureDimension(desiredSize: Int, measureSpec: Int): Int {
var result: Int
val specMode = MeasureSpec.getMode(measureSpec)
val specSize = MeasureSpec.getSize(measureSpec)
if (specMode == MeasureSpec.EXACTLY) {
result = specSize
} else {
result = desiredSize
if (specMode == MeasureSpec.AT_MOST) {
result = result.coerceAtMost(specSize)
}
}
if (result < desiredSize) {
Log.e("ChartView", "The view is too small, the content might get cut")
}
return result
}
private fun getContentHeight(width: Int): Int {
return when (data.size) {
1 -> {
val media = data[0]
val realHeight = (width * media.height / media.width.toFloat()).toInt()
if (isComment) {
realHeight
} else {
val maxHeight = (width * 1.25f).toInt()
min(maxHeight, realHeight)
}
}
2 -> width / 2
3 -> (width * 0.75f).toInt()
else -> width
}
}
private fun loadImage(
imageUrl: String,
imageContent: AppCompatImageView
) {
ZoomHelper.addZoomableView(imageContent)
Glide.with(imageContent)
.load(FirebaseStorage.getInstance().getReferenceFromUrl(imageUrl))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(imageContent)
}
fun randomColor(@FloatRange(from = 0.0, to = 1.0) value: Float = 1f) = Color.argb(
(255 * value).toInt(),
Random.nextInt(256), 100,
Random.nextInt(256)
)
}
class PostMedia(
val id: String = "",
val url: String = "",
val mediaType: Type = Type.IMAGE,
val duration: Int = 0,
val deletable: Boolean = false,
val thumbnailUrl: String? = null,
val gifUrl: String? = null,
val width: Int = 0,
val height: Int = 0,
val deleted: Boolean = false,
val muted: Boolean = false
) {
enum class Type(val value: Int) {
IMAGE(0),
VIDEO(1);
companion object {
fun from(value: Int?) = values().find { it.value == value } ?: IMAGE
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment