android player
smoother basic caching prep cache somewhat works backup other files android impl blegh lets go touchup add prefetch to js use caching
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
group = 'expo.modules.blueskyvideoplayer'
|
||||
version = '0.5.0'
|
||||
|
||||
buildscript {
|
||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||
if (expoModulesCorePlugin.exists()) {
|
||||
apply from: expoModulesCorePlugin
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
}
|
||||
|
||||
// Simple helper that allows the root project to override versions declared by this library.
|
||||
ext.safeExtGet = { prop, fallback ->
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
// Ensures backward compatibility
|
||||
ext.getKotlinVersion = {
|
||||
if (ext.has("kotlinVersion")) {
|
||||
ext.kotlinVersion()
|
||||
} else {
|
||||
ext.safeExtGet("kotlinVersion", "1.8.10")
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = mavenLocal().url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 33)
|
||||
|
||||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
||||
}
|
||||
}
|
||||
|
||||
namespace "expo.modules.blueskyvideoplayer"
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet("minSdkVersion", 21)
|
||||
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
||||
versionCode 1
|
||||
versionName "0.5.0"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':expo-modules-core')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
||||
implementation 'androidx.media3:media3-exoplayer:1.3.1'
|
||||
implementation 'androidx.media3:media3-ui:1.3.1'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<manifest>
|
||||
</manifest>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package expo.modules.blueskyvideoplayer
|
||||
|
||||
import expo.modules.kotlin.Promise
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
|
||||
class ExpoBlueskyVideoPlayerModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("ExpoBlueskyVideoPlayer")
|
||||
|
||||
AsyncFunction("prefetchAsync") {
|
||||
{ source: String, promise: Promise ->
|
||||
MediaItemManager(appContext).saveToCache(source)
|
||||
promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
View(ExpoBlueskyVideoPlayerView::class) {
|
||||
Prop("source") { view: ExpoBlueskyVideoPlayerView, source: String ->
|
||||
view.source = source
|
||||
}
|
||||
|
||||
Prop("autoplay") { view: ExpoBlueskyVideoPlayerView, autoplay: Boolean ->
|
||||
view.autoplay = autoplay
|
||||
}
|
||||
|
||||
Prop("getIsPlayingAsync") { view, promise: Promise ->
|
||||
promise.resolve(view.isPlaying)
|
||||
}
|
||||
|
||||
AsyncFunction("playAsync") { view: ExpoBlueskyVideoPlayerView ->
|
||||
view.play()
|
||||
}
|
||||
|
||||
AsyncFunction("pauseAsync") { view: ExpoBlueskyVideoPlayerView ->
|
||||
view.pause()
|
||||
}
|
||||
|
||||
AsyncFunction("toggleAsync") { view: ExpoBlueskyVideoPlayerView ->
|
||||
view.toggle()
|
||||
}
|
||||
|
||||
OnViewDidUpdateProps {
|
||||
val source = it.source
|
||||
if (source != null) {
|
||||
it.updateSource(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package expo.modules.blueskyvideoplayer
|
||||
|
||||
import android.content.Context
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.ui.PlayerView
|
||||
import expo.modules.kotlin.AppContext
|
||||
import expo.modules.kotlin.views.ExpoView
|
||||
|
||||
class ExpoBlueskyVideoPlayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
||||
private var playerView: PlayerView
|
||||
private var player: ExoPlayer
|
||||
|
||||
var autoplay: Boolean = true
|
||||
var source: String? = null
|
||||
var isPlaying = true
|
||||
|
||||
init {
|
||||
this.playerView = PlayerView(context).apply {
|
||||
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
useController = false
|
||||
}
|
||||
this.player = ExoPlayer.Builder(context).build().also {
|
||||
playerView.player = it
|
||||
}.apply {
|
||||
repeatMode = ExoPlayer.REPEAT_MODE_ONE
|
||||
volume = 0f
|
||||
}
|
||||
this.addView(this.playerView)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
if (this.autoplay && this.isPlaying) {
|
||||
this.player.play()
|
||||
}
|
||||
super.onAttachedToWindow()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
this.player.pause()
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
fun updateSource(source: String) {
|
||||
val mediaItem = MediaItemManager(appContext).getItem(source)
|
||||
player.setMediaItem(mediaItem)
|
||||
player.prepare()
|
||||
player.playWhenReady = true
|
||||
}
|
||||
|
||||
fun play() {
|
||||
this.player.play()
|
||||
this.isPlaying = true
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
this.player.pause()
|
||||
this.isPlaying = false
|
||||
}
|
||||
|
||||
fun toggle() {
|
||||
if (this.isPlaying) {
|
||||
this.player.pause()
|
||||
this.isPlaying = false
|
||||
} else {
|
||||
this.player.play()
|
||||
this.isPlaying = true
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package expo.modules.blueskyvideoplayer
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.media3.common.MediaItem
|
||||
import expo.modules.kotlin.AppContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okio.IOException
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
class MediaItemManager(private val appContext: AppContext) {
|
||||
companion object {
|
||||
private val client = OkHttpClient()
|
||||
}
|
||||
|
||||
fun getItem(source: String): MediaItem {
|
||||
val path = createPath(source)
|
||||
return if (File(path).exists()) {
|
||||
MediaItem.fromUri(Uri.parse(path))
|
||||
} else {
|
||||
saveToCache(source)
|
||||
MediaItem.fromUri(Uri.parse(source))
|
||||
}
|
||||
}
|
||||
|
||||
fun saveToCache(source: String) {
|
||||
val request = Request.Builder()
|
||||
.url(source)
|
||||
.build()
|
||||
|
||||
client.newCall(request).enqueue(object : okhttp3.Callback {
|
||||
override fun onFailure(call: okhttp3.Call, e: IOException) {
|
||||
e.printStackTrace() // Handle the error
|
||||
}
|
||||
|
||||
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
|
||||
val path = createPath(source)
|
||||
if (response.isSuccessful) {
|
||||
val file = File(path)
|
||||
val fos = FileOutputStream(file)
|
||||
val inputStream = response.body?.byteStream()
|
||||
|
||||
inputStream?.use { input ->
|
||||
fos.use { fileOut ->
|
||||
input.copyTo(fileOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getHash(source: String): String {
|
||||
val md = MessageDigest.getInstance("SHA-1")
|
||||
val byteArray = md.digest(source.toByteArray())
|
||||
return byteArray.joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
|
||||
private fun getGifsDirectory(): String {
|
||||
val gifsDirectory = appContext.cacheDirectory.path + "/gifs"
|
||||
if (!File(gifsDirectory).exists()) {
|
||||
File(gifsDirectory).mkdir()
|
||||
}
|
||||
return gifsDirectory
|
||||
}
|
||||
|
||||
private fun createPath(source: String): String {
|
||||
return getGifsDirectory() + "/" + getHash(source)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user