Skip to content

Commit

Permalink
Include Platforms and MediaPlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
jeluchu committed Aug 14, 2021
1 parent 04f615f commit 92c99e6
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 40 deletions.
2 changes: 1 addition & 1 deletion jchucomponentscompose/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ afterEvaluate {
from components.release
groupId = "com.jeluchu"
artifactId = "jchucomponentscompose"
version = "0.0.3"
version = "0.0.4"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.util.Base64
import androidx.compose.ui.graphics.Color
import org.intellij.lang.annotations.RegExp
import java.io.File
import java.io.IOException
Expand All @@ -17,23 +18,9 @@ import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Pattern

fun String.deleteDouble() = this.split(".")[0]

/** ---- DATE ---------------------------------------------------------------------------------- **/

fun String.parserDate(): String {
val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale("es", "ES"))
val formatter = SimpleDateFormat("dd/MM/yyyy", Locale("es", "ES"))
return formatter.format(parser.parse(this))
}

fun String?.compareDate(): Boolean {
val sdf = SimpleDateFormat("dd/M/yyyy", Locale("es", "ES"))
val currentDate = sdf.format(Date())
return if (this.isNullOrEmpty()) false else sdf.parse(this).before(sdf.parse(currentDate))
}
/** ---- COMPOSE FUNCTIONS --------------------------------------------------------------------- **/

fun Long.bytesToMeg(): String = (this / (1024L * 1024L)).toString()
fun String.getColor() = Color(android.graphics.Color.parseColor(this))

/** ---- YOUTUBE ------------------------------------------------------------------------------- **/

Expand All @@ -50,7 +37,6 @@ fun String.extractYTId(): String? {
return vId
}


/** ---- BITMAPS ------------------------------------------------------------------------------- **/

fun String.getBitmapFromURL(): Bitmap? =
Expand All @@ -66,29 +52,22 @@ fun String.getBitmapFromURL(): Bitmap? =
fun String.toBitmapDrawable(context: Context): BitmapDrawable =
BitmapDrawable(context.resources, getBitmapFromURL())

/** ---- ENCODE & DECODE ----------------------------------------------------------------------- **/

fun String.decodeBase64toImage(): Bitmap {
val imageBytes = Base64.decode(this, Base64.DEFAULT)
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
}

/** ---- MODIFY STRING ------------------------------------------------------------------------- **/

fun String.capitalizeWords(): String =
split(" ").joinToString(" ") { it.capitalize(Locale.getDefault()) }

fun String.capitalizeFirstLetter(): String =
substring(0, 1).uppercase(Locale("es", "ES")) + this.substring(1)

/** ---- REMOVE STRING ------------------------------------------------------------------------- **/
fun String.deleteDouble() = this.split(".")[0]

fun String.remove(value: String, ignoreCase: Boolean = false): String =
replace(value, "", ignoreCase)
/** ---- DATE ---------------------------------------------------------------------------------- **/

fun String.remove(@RegExp pattern: String) = remove(Regex(pattern, RegexOption.IGNORE_CASE))
fun String.remove(regex: Regex) = replace(regex, "")
fun String.parserDate(): String {
val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale("es", "ES"))
val formatter = SimpleDateFormat("dd/MM/yyyy", Locale("es", "ES"))
return formatter.format(parser.parse(this))
}

fun String?.compareDate(): Boolean {
val sdf = SimpleDateFormat("dd/M/yyyy", Locale("es", "ES"))
val currentDate = sdf.format(Date())
return if (this.isNullOrEmpty()) false else sdf.parse(this).before(sdf.parse(currentDate))
}

/** ---- CHECKER ------------------------------------------------------------------------------- **/

Expand Down Expand Up @@ -130,13 +109,32 @@ inline val String.isIp: Boolean
return m.find()
}

/** ---- FUNCTION STRING ----------------------------------------------------------------------- **/
/** ---- ENCODE & DECODE ----------------------------------------------------------------------- **/

fun String.decodeBase64toImage(): Bitmap {
val imageBytes = Base64.decode(this, Base64.DEFAULT)
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
}

/** ---- CONVERTERS AND MODIFIERS -------------------------------------------------------------- **/

fun Long.bytesToMeg(): String = (this / (1024L * 1024L)).toString()

fun String.capitalizeWords(): String =
split(" ").joinToString(" ") { it.uppercase(Locale.getDefault()) }

fun String.capitalizeFirstLetter(): String =
substring(0, 1).uppercase(Locale.getDefault()) + this.substring(1)

fun String.remove(value: String, ignoreCase: Boolean = false): String =
replace(value, "", ignoreCase)

fun String.remove(@RegExp pattern: String) = remove(Regex(pattern, RegexOption.IGNORE_CASE))
fun String.remove(regex: Regex) = replace(regex, "")

fun String.Companion.empty() = ""
fun String?.orEmpty(): String = this ?: String.empty()

fun String.isEmpty(): Boolean = length == 0

fun String.replace(): String = replace("-", " ")

fun String.parseDate(): String? =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.jeluchu.jchucomponentscompose.core.platform

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.jeluchu.jchucomponentscompose.core.exception.Failure

abstract class BaseViewModel : ViewModel() {

var failure: MutableLiveData<Failure> = MutableLiveData()
var showSpinner: MutableLiveData<Boolean> = MutableLiveData()

protected fun handleShowSpinner(show: Boolean) {
this.showSpinner.value = show
}

protected fun handleFailure(failure: Failure) {
this.failure.value = failure
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.jeluchu.jchucomponentscompose.core.platform

import android.content.Context

class ContextHandler
(private val context: Context) {
val appContext: Context get() = context.applicationContext
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jeluchu.jchucomponentscompose.core.platform

import android.content.Context
import com.jeluchu.jchucomponentscompose.core.extensions.context.checkNetworkState

class NetworkHandler
(private val context: Context) {
val isConnected get() = context.checkNetworkState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package com.jeluchu.jchucomponentscompose.utils.mediaplayer

import android.content.Context
import android.media.MediaPlayer
import android.net.Uri
import com.jeluchu.inook.core.utils.mediaplayer.PlaybackInfoListener
import com.jeluchu.jchucomponentscompose.core.extensions.ints.milliSecondsToTimer
import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

class MediaPlayerHolder(context: Context) : PlayerAdapter {
private val mContext: Context = context.applicationContext
private var mMediaPlayer: MediaPlayer? = null
private var mResourceId = String.empty()
private var mPlaybackInfoListener: PlaybackInfoListener? = null
private var mExecutor: ScheduledExecutorService? = null
private var mSeekbarPositionUpdateTask: Runnable? = null

/**
* Once the [MediaPlayer] is released, it can't be used again, and another one has to be
* created. In the onStop() method of the [MainActivity] the [MediaPlayer] is
* released. Then in the onStart() of the [MainActivity] a new [MediaPlayer]
* object has to be created. That's why this method is private, and called by load(int) and
* not the constructor.
*/
private fun initializeMediaPlayer() {
if (mMediaPlayer == null) {
mMediaPlayer = MediaPlayer()
mMediaPlayer!!.setOnCompletionListener {
stopUpdatingCallbackWithPosition(true)
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.COMPLETED)
mPlaybackInfoListener!!.onPlaybackCompleted()
}
}
}
}

override val isPlaying: Boolean
get() = if (mMediaPlayer != null) mMediaPlayer!!.isPlaying else false

override val currentProgress: Float
get() = if (mMediaPlayer != null) {
val currentSeconds: Long = (mMediaPlayer!!.currentPosition / 1000).toLong()
val totalSeconds: Long = (mMediaPlayer!!.duration / 1000).toLong()
(currentSeconds.toDouble() / totalSeconds * 100).toFloat()
} else 0F

override val currentTime: String
get() = if (mMediaPlayer != null) mMediaPlayer!!.currentPosition.milliSecondsToTimer()
else String.empty()

override val totalTime: String
get() = if (mMediaPlayer != null) mMediaPlayer!!.duration.milliSecondsToTimer()
else String.empty()

override fun togglePlaying(isPlaying: Boolean) {
if (mMediaPlayer != null) {
when (isPlaying) {
true -> mMediaPlayer!!.pause()
false -> mMediaPlayer!!.start()
}
}
}

fun setPlaybackInfoListener(listener: PlaybackInfoListener?) {
mPlaybackInfoListener = listener
}

override fun loadMedia(mp3Link: String) {
mResourceId = mp3Link
initializeMediaPlayer()
try {
mMediaPlayer!!.setDataSource(mContext, Uri.parse(mResourceId))
} catch (e: Exception) {
e.message
}
try {
mMediaPlayer!!.prepare()
} catch (e: Exception) {
e.message
}
initializeProgressCallback()
}

override fun release() {
if (mMediaPlayer != null) {
mMediaPlayer!!.release()
mMediaPlayer = null
}
}


override fun play() {
if (mMediaPlayer != null && !mMediaPlayer!!.isPlaying) {
mMediaPlayer!!.start()
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.PLAYING)
}
startUpdatingCallbackWithPosition()
}
}

override fun reset() {
if (mMediaPlayer != null) {
mMediaPlayer!!.reset()
loadMedia(mResourceId)
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.RESET)
}
stopUpdatingCallbackWithPosition(true)
}
}

override fun pause() {
if (mMediaPlayer != null && mMediaPlayer!!.isPlaying) {
mMediaPlayer!!.pause()
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onStateChanged(PlaybackInfoListener.State.PAUSED)
}
}
}

override fun seekTo(position: Int) {
if (mMediaPlayer != null) {
mMediaPlayer!!.seekTo(position)
}
}

private fun startUpdatingCallbackWithPosition() {
if (mExecutor == null) {
mExecutor = Executors.newSingleThreadScheduledExecutor()
}
if (mSeekbarPositionUpdateTask == null) {
mSeekbarPositionUpdateTask = Runnable { updateProgressCallbackTask() }
}
mExecutor!!.scheduleAtFixedRate(
mSeekbarPositionUpdateTask,
0,
PLAYBACK_POSITION_REFRESH_INTERVAL_MS.toLong(),
TimeUnit.MILLISECONDS
)
}

// Reports media playback position to mPlaybackProgressCallback.
private fun stopUpdatingCallbackWithPosition(resetUIPlaybackPosition: Boolean) {
if (mExecutor != null) {
mExecutor!!.shutdownNow()
mExecutor = null
mSeekbarPositionUpdateTask = null
if (resetUIPlaybackPosition && mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onPositionChanged(0)
}
}
}

private fun updateProgressCallbackTask() {
if (mMediaPlayer != null && mMediaPlayer!!.isPlaying) {
val currentPosition = mMediaPlayer!!.currentPosition
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onPositionChanged(currentPosition)
}
}
}

override fun initializeProgressCallback() {
val duration = mMediaPlayer!!.duration
if (mPlaybackInfoListener != null) {
mPlaybackInfoListener!!.onDurationChanged(duration)
mPlaybackInfoListener!!.onPositionChanged(0)
}
}

companion object {
const val PLAYBACK_POSITION_REFRESH_INTERVAL_MS = 1000
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.jeluchu.inook.core.utils.mediaplayer

abstract class PlaybackInfoListener {

internal annotation class State {
companion object {
var INVALID = -1
var PLAYING = 0
var PAUSED = 1
var RESET = 2
var COMPLETED = 3
}
}

open fun onLogUpdated(formattedMessage: String?) {}
open fun onDurationChanged(duration: Int) {}
open fun onPositionChanged(position: Int) {}
open fun onStateChanged(@State state: Int) {}
open fun onPlaybackCompleted() {}

companion object {
@JvmStatic
fun convertStateToString(@State state: Int): String {
return when (state) {
State.COMPLETED -> "COMPLETED"
State.INVALID -> "INVALID"
State.PAUSED -> "PAUSED"
State.PLAYING -> "PLAYING"
State.RESET -> "RESET"
else -> "N/A"
}
}
}
}
Loading

0 comments on commit 92c99e6

Please sign in to comment.