Created
October 29, 2021 07:03
-
-
Save TalbotGooday/0c36c67bf72db296f43e420a2f384a38 to your computer and use it in GitHub Desktop.
MediaView
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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