Added intent filter for video/image files
Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
parent
5933cd2b8a
commit
8865f2c5b4
10 changed files with 351 additions and 85 deletions
|
@ -42,6 +42,15 @@
|
|||
<entry key="../../../../../layout/compose-model-1641156419059.xml" value="0.5" />
|
||||
<entry key="../../../../../layout/compose-model-1641157410658.xml" value="1.0" />
|
||||
<entry key="../../../../../layout/compose-model-1641157605359.xml" value="0.5" />
|
||||
<entry key="../../../../../layout/compose-model-1641158311201.xml" value="1.0" />
|
||||
<entry key="../../../../../layout/compose-model-1641198484109.xml" value="0.30743243243243246" />
|
||||
<entry key="../../../../../layout/compose-model-1641204990382.xml" value="0.1" />
|
||||
<entry key="../../../../../layout/compose-model-1641204995500.xml" value="0.1" />
|
||||
<entry key="../../../../../layout/compose-model-1641204997625.xml" value="0.44814814814814813" />
|
||||
<entry key="../../../../../layout/compose-model-1641219750398.xml" value="0.25" />
|
||||
<entry key="../../../../../layout/compose-model-1641222972102.xml" value="0.46296296296296297" />
|
||||
<entry key="../../../../../layout/compose-model-1641224248444.xml" value="0.46296296296296297" />
|
||||
<entry key="../../../../../layout/compose-model-1641226030069.xml" value="0.46296296296296297" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
@ -4,6 +4,8 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
|
||||
|
||||
compileSdk 31
|
||||
|
||||
defaultConfig {
|
||||
|
@ -56,6 +58,10 @@ android {
|
|||
kotlinCompilerVersion '1.5.21'
|
||||
}
|
||||
packagingOptions {
|
||||
exclude 'META-INF/*'
|
||||
// Due to https://github.com/Kotlin/kotlinx.coroutines/issues/2023
|
||||
exclude 'META-INF/licenses/*'
|
||||
exclude '**/attach_hotspot_windows.dll'
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
|
@ -82,6 +88,10 @@ dependencies {
|
|||
implementation 'com.github.yausername.youtubedl-android:library:0.13.+'
|
||||
implementation 'com.github.yausername.youtubedl-android:ffmpeg:0.13.+'
|
||||
implementation "androidx.media:media:1.4.3"
|
||||
|
||||
implementation 'io.ktor:ktor-server-core:1.6.2'
|
||||
implementation 'io.ktor:ktor-server-jetty:1.6.2'
|
||||
implementation 'io.ktor:ktor-websockets:1.6.2'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
|
|
@ -27,6 +27,16 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ChromecastManagerActivity"
|
||||
|
|
|
@ -11,6 +11,8 @@ import kotlinx.coroutines.withContext
|
|||
import su.litvak.chromecast.api.v2.Application
|
||||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
import su.litvak.chromecast.api.v2.MediaStatus
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
|
||||
object ChromeCastHelper {
|
||||
|
||||
|
@ -161,5 +163,14 @@ object ChromeCastHelper {
|
|||
}
|
||||
return exitStatus
|
||||
}
|
||||
|
||||
fun getIPv4Address(): InetAddress? {
|
||||
NetworkInterface.getNetworkInterfaces().toList().forEach { interf ->
|
||||
interf.inetAddresses.toList().forEach { inetAddress ->
|
||||
if (!inetAddress.isLoopbackAddress && inetAddress.hostAddress.indexOf(':') < 0) {
|
||||
return inetAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -1,12 +1,19 @@
|
|||
package eu.toldi.balazs.caster
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
@ -25,12 +32,16 @@ import coil.compose.rememberImagePainter
|
|||
import com.yausername.ffmpeg.FFmpeg
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLException
|
||||
import eu.toldi.balazs.caster.helpers.FileCacheHelper
|
||||
import eu.toldi.balazs.caster.model.ChromecastManageViewmodel
|
||||
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
||||
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
||||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
import su.litvak.chromecast.api.v2.Media
|
||||
import su.litvak.chromecast.api.v2.MediaStatus
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.FileOutputStream
|
||||
|
||||
|
||||
class ChromecastManagerActivity : ComponentActivity() {
|
||||
|
@ -152,37 +163,50 @@ class ChromecastManagerActivity : ComponentActivity() {
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = String.format(
|
||||
"%02d:%02d",
|
||||
((mediaStatus.currentTime % 3600) / 60).toInt(),
|
||||
(mediaStatus.currentTime % 60).toInt()
|
||||
if(mediaStatus.media.contentType.startsWith("video/")) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = String.format(
|
||||
"%02d:%02d",
|
||||
((mediaStatus.currentTime % 3600) / 60).toInt(),
|
||||
(mediaStatus.currentTime % 60).toInt()
|
||||
)
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = String.format(
|
||||
"%02d:%02d",
|
||||
((mediaStatus.media.duration % 3600) / 60).toInt(),
|
||||
(mediaStatus.media.duration % 60).toInt()
|
||||
Text(
|
||||
text = String.format(
|
||||
"%02d:%02d",
|
||||
((mediaStatus.media.duration % 3600) / 60).toInt(),
|
||||
(mediaStatus.media.duration % 60).toInt()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
Slider(value = sliderPosition, onValueChange = {
|
||||
sliderPosition = it
|
||||
sliderMoving = true
|
||||
}, onValueChangeFinished = {
|
||||
viewModel.seek(sliderPosition.toDouble())
|
||||
sliderMoving = false
|
||||
})
|
||||
}
|
||||
Slider(value = sliderPosition, onValueChange = {
|
||||
sliderPosition = it
|
||||
sliderMoving = true
|
||||
}, onValueChangeFinished = {
|
||||
viewModel.seek(sliderPosition.toDouble())
|
||||
sliderMoving = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun MenuBar() {
|
||||
val pickPictureLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.OpenDocument()
|
||||
) { fileUri ->
|
||||
if (fileUri != null) {
|
||||
val cacheHelper = FileCacheHelper(applicationContext)
|
||||
cacheHelper.cacheThis(listOf(fileUri))
|
||||
viewModel.castFromCache(cacheHelper.tryFileName(fileUri))
|
||||
|
||||
}
|
||||
}
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text("Caster")
|
||||
|
@ -197,7 +221,14 @@ class ChromecastManagerActivity : ComponentActivity() {
|
|||
},
|
||||
actions = {
|
||||
Row {
|
||||
|
||||
IconButton(onClick = {
|
||||
pickPictureLauncher.launch(arrayOf("image/*", "video/*"))
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.Folder,
|
||||
contentDescription = stringResource(id = R.string.back)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -32,11 +32,13 @@ import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
|||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
open class MainActivity : ComponentActivity() {
|
||||
|
||||
lateinit var viewModel: ChromeCastViewModel
|
||||
private var multicastLock: MulticastLock? = null
|
||||
protected lateinit var viewModel: ChromeCastViewModel
|
||||
protected var multicastLock: MulticastLock? = null
|
||||
|
||||
|
||||
protected fun isViewModelInitialised() = ::viewModel.isInitialized
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val wifi = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
|
||||
|
@ -184,7 +186,11 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
|
||||
@Composable
|
||||
fun showChromeCastButton(chromeCast: ChromeCast) {
|
||||
fun showChromeCastButton(chromeCast: ChromeCast,buttonCallBack: () -> Unit = {
|
||||
ChromecastManagerActivity.chromeCast_ = chromeCast
|
||||
val intent = Intent(applicationContext, ChromecastManagerActivity::class.java)
|
||||
startActivity(intent)
|
||||
}) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
|
@ -193,11 +199,9 @@ class MainActivity : ComponentActivity() {
|
|||
Card(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
ChromecastManagerActivity.chromeCast_ = chromeCast
|
||||
val intent = Intent(applicationContext, ChromecastManagerActivity::class.java)
|
||||
startActivity(intent)
|
||||
buttonCallBack()
|
||||
}) {
|
||||
Row() {
|
||||
Row {
|
||||
val image_id = when (chromeCast.model) {
|
||||
"Chromecast Ultra" -> R.drawable.chromecastultra
|
||||
else -> R.drawable.chromecastv1
|
||||
|
@ -217,7 +221,8 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
)
|
||||
}
|
||||
Column() {
|
||||
Column(modifier = Modifier.fillMaxWidth().height(80.dp),
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
Text(
|
||||
text = "Name: " + when {
|
||||
chromeCast.title != null -> chromeCast.title
|
||||
|
@ -267,7 +272,7 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
open fun DefaultPreview() {
|
||||
CasterTheme {
|
||||
Column {
|
||||
MenuBar({}, {})
|
||||
|
|
|
@ -2,8 +2,10 @@ package eu.toldi.balazs.caster
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
|
@ -19,49 +21,47 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLException
|
||||
import eu.toldi.balazs.caster.helpers.FileCacheHelper
|
||||
import eu.toldi.balazs.caster.model.ChromeCastViewModel
|
||||
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
||||
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
|
||||
class ShareRecieverActivity : ComponentActivity() {
|
||||
|
||||
lateinit var viewModel: ChromeCastViewModel
|
||||
private var multicastLock: WifiManager.MulticastLock? = null
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val wifi = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
|
||||
// get the device ip address
|
||||
multicastLock = wifi.createMulticastLock(javaClass.name)
|
||||
multicastLock!!.setReferenceCounted(true)
|
||||
multicastLock!!.acquire()
|
||||
if (!this::viewModel.isInitialized)
|
||||
viewModel = ViewModelProvider(this).get(ChromeCastViewModel::class.java)
|
||||
viewModel.startScanning()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (multicastLock != null) {
|
||||
Log.i("Caster", "Releasing Mutlicast Lock...")
|
||||
multicastLock!!.release()
|
||||
multicastLock = null
|
||||
}
|
||||
viewModel.startScanning()
|
||||
}
|
||||
class ShareRecieverActivity : MainActivity() {
|
||||
|
||||
@SuppressLint("CoroutineCreationDuringComposition")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (intent?.action != Intent.ACTION_SEND || intent.type != "text/plain") {
|
||||
if (intent?.action != Intent.ACTION_SEND ) {
|
||||
finish()
|
||||
}
|
||||
val link = intent.getStringExtra(Intent.EXTRA_TEXT) as String
|
||||
Log.d(null, intent.type.toString())
|
||||
val link = when {
|
||||
intent.type == "text/plain" -> intent.getStringExtra(Intent.EXTRA_TEXT) as String
|
||||
intent.type?.startsWith("video/") == true ||
|
||||
intent.type?.startsWith("image/") == true -> {
|
||||
val uri = (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)
|
||||
if(uri is Uri) {
|
||||
val fileCache = FileCacheHelper(applicationContext)
|
||||
fileCache.cacheThis(listOf(uri))
|
||||
"http://" + ChromeCastHelper.getIPv4Address()?.hostAddress + ":" + ChromecastManagerService.PORT + "/assets/" + fileCache.tryFileName(
|
||||
uri
|
||||
)
|
||||
}else ""
|
||||
|
||||
|
||||
}
|
||||
else -> {
|
||||
""
|
||||
}
|
||||
}
|
||||
if (link == "") finish()
|
||||
try {
|
||||
YoutubeDL.getInstance().init(application)
|
||||
} catch (e: YoutubeDLException) {
|
||||
|
@ -69,7 +69,7 @@ class ShareRecieverActivity : ComponentActivity() {
|
|||
}
|
||||
setContent {
|
||||
CasterTheme {
|
||||
if (!this::viewModel.isInitialized)
|
||||
if (!isViewModelInitialised())
|
||||
viewModel = ViewModelProvider(this).get(ChromeCastViewModel::class.java)
|
||||
viewModel.startScanning()
|
||||
val chromeCastState = viewModel.chromeCasts.observeAsState(emptyList())
|
||||
|
@ -110,8 +110,8 @@ class ShareRecieverActivity : ComponentActivity() {
|
|||
link = link
|
||||
)
|
||||
}
|
||||
item{
|
||||
if(enabled.not()){
|
||||
item {
|
||||
if (enabled.not()) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
|
@ -130,25 +130,23 @@ class ShareRecieverActivity : ComponentActivity() {
|
|||
chromeCast: ChromeCast,
|
||||
link: String
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
showChromeCastButton(chromeCast = chromeCast, buttonCallBack = {
|
||||
if (enabled) {
|
||||
onEnableChanged(false)
|
||||
Intent(this, ChromecastManagerService::class.java).also {
|
||||
it.action = ChromecastManagerService.ACTION_INIT
|
||||
it.putExtra("CHROMECAST_ADDRESS", chromeCast.address)
|
||||
it.putExtra("CHROMECAST_NAME", chromeCast.title)
|
||||
ContextCompat.startForegroundService(this, it)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
ChromeCastHelper.chromeCast = chromeCast
|
||||
ChromeCastHelper.castLink(link) {
|
||||
finish()
|
||||
//finish()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(5.dp),
|
||||
enabled = enabled
|
||||
) {
|
||||
when {
|
||||
chromeCast.title != null -> Text(text = chromeCast.title)
|
||||
chromeCast.name != null -> Text(text = chromeCast.name)
|
||||
else -> Text(text = chromeCast.address)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -173,7 +171,7 @@ class ShareRecieverActivity : ComponentActivity() {
|
|||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
override fun DefaultPreview() {
|
||||
CasterTheme {
|
||||
MenuBar {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
package eu.toldi.balazs.caster.helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.webkit.MimeTypeMap
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class FileCacheHelper(
|
||||
private val mContext: Context
|
||||
) {
|
||||
|
||||
// content resolver
|
||||
private val contentResolver = mContext.contentResolver
|
||||
|
||||
// to get the type of file
|
||||
private val mimeTypeMap = MimeTypeMap.getSingleton()
|
||||
|
||||
private val mCacheLocation = mContext.cacheDir
|
||||
|
||||
fun cacheThis(uris: List<Uri>) {
|
||||
executor.submit {
|
||||
uris.forEach { uri -> copyFromSource(uri) }
|
||||
}
|
||||
}
|
||||
|
||||
fun tryFileName(uri: Uri): String{
|
||||
val fileExtension: String = getFileExtension(uri) ?: kotlin.run {
|
||||
throw RuntimeException("Extension is null for $uri")
|
||||
}
|
||||
return queryName(uri) ?: getFileName(fileExtension)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the actual data from provided content provider.
|
||||
*/
|
||||
private fun copyFromSource(uri: Uri) {
|
||||
|
||||
val fileExtension: String = getFileExtension(uri) ?: kotlin.run {
|
||||
throw RuntimeException("Extension is null for $uri")
|
||||
}
|
||||
val fileName = queryName(uri) ?: getFileName(fileExtension)
|
||||
|
||||
val inputStream = contentResolver.openInputStream(uri) ?: kotlin.run {
|
||||
throw RuntimeException("Cannot open for reading $uri")
|
||||
}
|
||||
val bufferedInputStream = BufferedInputStream(inputStream)
|
||||
|
||||
// the file which will be the new cached file
|
||||
val outputFile = File(mCacheLocation, fileName)
|
||||
val bufferedOutputStream = BufferedOutputStream(FileOutputStream(outputFile))
|
||||
|
||||
// this will hold the content for each iteration
|
||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||
|
||||
var readBytes = 0 // will be -1 if reached the end of file
|
||||
|
||||
while (true) {
|
||||
readBytes = bufferedInputStream.read(buffer)
|
||||
|
||||
// check if the read was failure
|
||||
if (readBytes == -1) {
|
||||
bufferedOutputStream.flush()
|
||||
break
|
||||
}
|
||||
|
||||
bufferedOutputStream.write(buffer)
|
||||
bufferedOutputStream.flush()
|
||||
}
|
||||
Log.i("FileCache",outputFile.absoluteFile.toString())
|
||||
// close everything
|
||||
inputStream.close()
|
||||
bufferedInputStream.close()
|
||||
bufferedOutputStream.close()
|
||||
|
||||
}
|
||||
|
||||
private fun getFileExtension(uri: Uri): String? {
|
||||
return mimeTypeMap.getExtensionFromMimeType(contentResolver.getType(uri))
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get actual name of the file being copied.
|
||||
* This might be required in some of the cases where you might want to know the file name too.
|
||||
*
|
||||
* @param uri
|
||||
*
|
||||
*/
|
||||
@SuppressLint("Recycle")
|
||||
private fun queryName(uri: Uri): String? {
|
||||
val returnCursor: Cursor = contentResolver.query(uri, null, null, null, null) ?: return null
|
||||
val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
returnCursor.moveToFirst()
|
||||
val name: String = returnCursor.getString(nameIndex)
|
||||
returnCursor.close()
|
||||
return name
|
||||
}
|
||||
|
||||
private fun getFileName(fileExtension: String): String {
|
||||
return "${System.currentTimeMillis().toString()}.$fileExtension"
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove everything that we have cached.
|
||||
* You might want to invoke this method before quiting the application.
|
||||
*/
|
||||
fun removeAll() {
|
||||
mContext.cacheDir.deleteRecursively()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// base buffer size
|
||||
private const val BASE_BUFFER_SIZE = 1024
|
||||
|
||||
// if you want to modify size use binary multiplier 2, 4, 6, 8
|
||||
private const val DEFAULT_BUFFER_SIZE = BASE_BUFFER_SIZE * 4
|
||||
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
|
||||
}
|
||||
}
|
|
@ -1,15 +1,19 @@
|
|||
package eu.toldi.balazs.caster.model
|
||||
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import eu.toldi.balazs.caster.ChromeCastHelper
|
||||
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
import su.litvak.chromecast.api.v2.MediaStatus
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
|
||||
class ChromecastManageViewmodel : ViewModel() {
|
||||
|
||||
|
@ -60,6 +64,13 @@ class ChromecastManageViewmodel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun castFromCache(fileName: String,callBack: () -> Unit = {}){
|
||||
//Log.i("Caster","http://"+getIPv4Address()?.hostAddress+":"+ChromecastManagerService.PORT+"/assets/"+fileName)
|
||||
viewModelScope.launch(IO) {
|
||||
ChromeCastHelper.castLink("http://"+getIPv4Address()?.hostAddress+":"+ChromecastManagerService.PORT+"/assets/"+fileName,callBack)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopApp() {
|
||||
viewModelScope.launch(IO) {
|
||||
ChromeCastHelper.stopApp()
|
||||
|
@ -95,4 +106,15 @@ class ChromecastManageViewmodel : ViewModel() {
|
|||
setVolume(chromeCast.status.volume.level - 0.05f)
|
||||
}
|
||||
}
|
||||
|
||||
fun getIPv4Address(): InetAddress? {
|
||||
NetworkInterface.getNetworkInterfaces().toList().forEach { interf ->
|
||||
interf.inetAddresses.toList().forEach { inetAddress ->
|
||||
if (!inetAddress.isLoopbackAddress && inetAddress.hostAddress.indexOf(':') < 0) {
|
||||
return inetAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -16,14 +16,21 @@ import androidx.core.app.NotificationCompat
|
|||
import eu.toldi.balazs.caster.App.Companion.CHANNEL_ID
|
||||
import eu.toldi.balazs.caster.ChromeCastHelper
|
||||
import eu.toldi.balazs.caster.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import io.ktor.application.*
|
||||
import io.ktor.features.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.content.*
|
||||
import io.ktor.response.*
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.jetty.*
|
||||
import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import su.litvak.chromecast.api.v2.ChromeCast
|
||||
import su.litvak.chromecast.api.v2.Media
|
||||
import su.litvak.chromecast.api.v2.MediaStatus
|
||||
import java.io.File
|
||||
|
||||
|
||||
class ChromecastManagerService : Service() {
|
||||
|
@ -36,6 +43,9 @@ class ChromecastManagerService : Service() {
|
|||
const val ACTION_NEXT = "action_next"
|
||||
const val ACTION_PREVIOUS = "action_previous"
|
||||
const val ACTION_STOP = "action_stop"
|
||||
const val ACTION_SETFILE = "action_file"
|
||||
|
||||
const val PORT = 3080
|
||||
}
|
||||
|
||||
private val mMediaPlayer = MediaPlayer()
|
||||
|
@ -43,10 +53,33 @@ class ChromecastManagerService : Service() {
|
|||
private lateinit var mController: MediaControllerCompat
|
||||
private lateinit var pendingIntent : PendingIntent
|
||||
private var mediaStatus: MediaStatus? = null
|
||||
|
||||
private var file : File? = null
|
||||
|
||||
override fun onBind(p0: Intent?): IBinder? = null
|
||||
private lateinit var chromeCast: ChromeCast
|
||||
private val server by lazy {
|
||||
embeddedServer(Jetty, PORT, watchPaths = emptyList()) {
|
||||
install(WebSockets)
|
||||
install(CallLogging)
|
||||
routing {
|
||||
get("/") {
|
||||
if(file == null) {
|
||||
call.respondText(
|
||||
text = "Hello!! You are here in ${Build.MODEL}",
|
||||
contentType = ContentType.Text.Plain
|
||||
)
|
||||
}else{
|
||||
|
||||
call.respondFile(file!!)
|
||||
}
|
||||
}
|
||||
static("assets") {
|
||||
staticRootFolder = applicationContext.cacheDir
|
||||
files(".")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
|
@ -81,8 +114,11 @@ class ChromecastManagerService : Service() {
|
|||
.setContentIntent(pendingIntent).build()
|
||||
|
||||
startForeground(1, notification)
|
||||
CoroutineScope(IO).launch {
|
||||
server.start(wait = true)
|
||||
}
|
||||
CoroutineScope(IO).launch {
|
||||
|
||||
GlobalScope.launch(IO) {
|
||||
while (true) {
|
||||
try {
|
||||
ChromeCastHelper.chromeCast = chromeCast
|
||||
|
@ -116,6 +152,11 @@ class ChromecastManagerService : Service() {
|
|||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
server.stop(1_000, 2_000)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private suspend fun buildNotification() {
|
||||
var status = chromeCast.status
|
||||
Log.d(null, status.applications.toString())
|
||||
|
|
Loading…
Reference in a new issue