mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
lots of chromecast stuff for better UI
This commit is contained in:
parent
ac442edd4e
commit
6579eb3083
12 changed files with 207 additions and 66 deletions
|
@ -2,6 +2,7 @@ package com.lagradost.cloudstream3
|
||||||
|
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
@ -12,10 +13,20 @@ import androidx.lifecycle.ViewModelStoreOwner
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
|
import com.google.android.gms.cast.MediaInfo
|
||||||
|
import com.google.android.gms.cast.MediaMetadata
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem
|
||||||
|
import com.google.android.gms.cast.MediaStatus
|
||||||
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
|
import com.google.android.gms.cast.framework.CastState
|
||||||
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
import com.lagradost.cloudstream3.UIHelper.checkWrite
|
||||||
import com.lagradost.cloudstream3.UIHelper.hasPIPPermission
|
import com.lagradost.cloudstream3.UIHelper.hasPIPPermission
|
||||||
import com.lagradost.cloudstream3.UIHelper.requestRW
|
import com.lagradost.cloudstream3.UIHelper.requestRW
|
||||||
import com.lagradost.cloudstream3.UIHelper.shouldShowPIPMode
|
import com.lagradost.cloudstream3.UIHelper.shouldShowPIPMode
|
||||||
|
import kotlinx.android.synthetic.main.fragment_result.*
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
/*, ViewModelStoreOwner {
|
/*, ViewModelStoreOwner {
|
||||||
|
@ -99,5 +110,32 @@ class MainActivity : AppCompatActivity() {
|
||||||
requestRW()
|
requestRW()
|
||||||
if (checkWrite()) return
|
if (checkWrite()) return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CastButtonFactory.setUpMediaRouteButton(this, media_route_button)
|
||||||
|
/*
|
||||||
|
val castContext = CastContext.getSharedInstance(applicationContext)
|
||||||
|
fun buildMediaQueueItem(video: String): MediaQueueItem {
|
||||||
|
// val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_PHOTO)
|
||||||
|
//movieMetadata.putString(MediaMetadata.KEY_TITLE, "CloudStream")
|
||||||
|
val mediaInfo = MediaInfo.Builder(Uri.parse(video).toString())
|
||||||
|
.setStreamType(MediaInfo.STREAM_TYPE_NONE)
|
||||||
|
.setContentType(MimeTypes.IMAGE_JPEG)
|
||||||
|
// .setMetadata(movieMetadata).build()
|
||||||
|
.build()
|
||||||
|
return MediaQueueItem.Builder(mediaInfo).build()
|
||||||
|
}*/
|
||||||
|
/*
|
||||||
|
castContext.addCastStateListener { state ->
|
||||||
|
if (state == CastState.CONNECTED) {
|
||||||
|
println("TESTING")
|
||||||
|
val isCasting = castContext?.sessionManager?.currentCastSession?.remoteMediaClient?.currentItem != null
|
||||||
|
if(!isCasting) {
|
||||||
|
val castPlayer = CastPlayer(castContext)
|
||||||
|
println("LOAD ITEM")
|
||||||
|
|
||||||
|
castPlayer.loadItem(buildMediaQueueItem("https://cdn.discordapp.com/attachments/551382684560261121/730169809408622702/ChromecastLogo6.png"),0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ import android.app.AppOpsManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Color
|
||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
import android.media.AudioFocusRequest
|
import android.media.AudioFocusRequest
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
|
@ -13,6 +14,7 @@ import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -21,9 +23,10 @@ import androidx.preference.PreferenceManager
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.lagradost.cloudstream3.UIHelper.getGridFormat
|
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
object UIHelper {
|
object UIHelper {
|
||||||
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
val Int.toPx: Int get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||||
|
@ -143,6 +146,21 @@ object UIHelper {
|
||||||
return isCastApiAvailable
|
return isCastApiAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun adjustAlpha(@ColorInt color: Int, factor: Float): Int {
|
||||||
|
val alpha = (Color.alpha(color) * factor).roundToInt()
|
||||||
|
val red = Color.red(color)
|
||||||
|
val green = Color.green(color)
|
||||||
|
val blue = Color.blue(color)
|
||||||
|
return Color.argb(alpha, red, green, blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.colorFromAttribute(attribute: Int): Int {
|
||||||
|
val attributes = obtainStyledAttributes(intArrayOf(attribute))
|
||||||
|
val color = attributes.getColor(0, 0)
|
||||||
|
attributes.recycle()
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
|
||||||
fun getFocusRequest(): AudioFocusRequest? {
|
fun getFocusRequest(): AudioFocusRequest? {
|
||||||
if (_AudioFocusRequest != null) return _AudioFocusRequest
|
if (_AudioFocusRequest != null) return _AudioFocusRequest
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|
|
@ -6,14 +6,22 @@ import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_SINGLE
|
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_SINGLE
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
import com.google.android.gms.cast.framework.CastSession
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
import com.google.android.gms.cast.framework.media.uicontroller.UIController
|
import com.google.android.gms.cast.framework.media.uicontroller.UIController
|
||||||
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
|
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.hideSystemUI
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
class SkipOpController(val view: ImageView) : UIController() {
|
class SkipOpController(val view: ImageView) : UIController() {
|
||||||
init {
|
init {
|
||||||
|
@ -24,7 +32,12 @@ class SkipOpController(val view: ImageView) : UIController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SelectSourceController(val view: ImageView) : UIController() {
|
data class MetadataSource(val name: String)
|
||||||
|
data class MetadataHolder(val data: List<MetadataSource>)
|
||||||
|
|
||||||
|
class SelectSourceController(val view: ImageView, val activity: ControllerActivity) : UIController() {
|
||||||
|
private val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
|
||||||
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
view.setImageResource(R.drawable.ic_baseline_playlist_play_24)
|
view.setImageResource(R.drawable.ic_baseline_playlist_play_24)
|
||||||
|
@ -33,45 +46,52 @@ class SelectSourceController(val view: ImageView) : UIController() {
|
||||||
//println(remoteMediaClient.mediaInfo.customData)
|
//println(remoteMediaClient.mediaInfo.customData)
|
||||||
//remoteMediaClient.queueJumpToItem()
|
//remoteMediaClient.queueJumpToItem()
|
||||||
lateinit var dialog: AlertDialog
|
lateinit var dialog: AlertDialog
|
||||||
val items = mutableListOf<Pair<Int, String>>()
|
val holder = getCurrentMetaData()
|
||||||
for (i in 0 until remoteMediaClient.mediaQueue.itemCount) {
|
|
||||||
(remoteMediaClient.mediaQueue.getItemAtIndex(i)?.media?.customData?.get("data") as? String)?.let { name ->
|
if (holder != null) {
|
||||||
items.add(
|
val items = holder.data
|
||||||
remoteMediaClient.mediaQueue.getItemAtIndex(i)!!.itemId to name
|
if (items.isNotEmpty() && remoteMediaClient.currentItem != null) {
|
||||||
)
|
val builder = AlertDialog.Builder(view.context, R.style.AlertDialogCustom)
|
||||||
|
builder.setTitle("Pick source")
|
||||||
|
|
||||||
|
builder.setSingleChoiceItems(
|
||||||
|
items.map { it.name }.toTypedArray(),
|
||||||
|
remoteMediaClient.mediaQueue.indexOfItemWithId(remoteMediaClient.currentItem.itemId)
|
||||||
|
) { _, which ->
|
||||||
|
val itemId = remoteMediaClient.mediaQueue.itemIds?.get(which)
|
||||||
|
|
||||||
|
itemId?.let { id ->
|
||||||
|
remoteMediaClient.queueJumpToItem(
|
||||||
|
id,
|
||||||
|
remoteMediaClient.approximateStreamPosition,
|
||||||
|
remoteMediaClient.mediaInfo.customData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO FIX
|
private fun getCurrentMetaData(): MetadataHolder? {
|
||||||
if (items.isNotEmpty()) {
|
return try {
|
||||||
val builder = AlertDialog.Builder(view.context, R.style.AlertDialogCustom)
|
val data = remoteMediaClient.mediaInfo.customData
|
||||||
builder.setTitle("Pick source")
|
mapper.readValue<MetadataHolder>(data.toString())
|
||||||
|
} catch (e: Exception) {
|
||||||
builder.setSingleChoiceItems(
|
null
|
||||||
items.map { it.second }.toTypedArray(),
|
|
||||||
remoteMediaClient.currentItem.itemId - 1
|
|
||||||
) { _, which ->
|
|
||||||
println(
|
|
||||||
remoteMediaClient.queueJumpToItem(
|
|
||||||
items[which].first,
|
|
||||||
remoteMediaClient.approximateStreamPosition,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaStatusUpdated() {
|
override fun onMediaStatusUpdated() {
|
||||||
super.onMediaStatusUpdated()
|
super.onMediaStatusUpdated()
|
||||||
// If there's 1 item it won't show
|
view.visibility =
|
||||||
val dataString = remoteMediaClient.mediaQueue.getItemAtIndex(1)?.media?.customData?.get("data") as? String
|
if ((getCurrentMetaData()?.data?.size
|
||||||
|
?: 0) > 1
|
||||||
view.visibility = if (dataString != null) VISIBLE else INVISIBLE
|
) VISIBLE else INVISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSessionConnected(castSession: CastSession?) {
|
override fun onSessionConnected(castSession: CastSession?) {
|
||||||
|
@ -107,7 +127,7 @@ class ControllerActivity : ExpandedControllerActivity() {
|
||||||
val skipBackButton: ImageView = getButtonImageViewAt(1)
|
val skipBackButton: ImageView = getButtonImageViewAt(1)
|
||||||
val skipForwardButton: ImageView = getButtonImageViewAt(2)
|
val skipForwardButton: ImageView = getButtonImageViewAt(2)
|
||||||
val skipOpButton: ImageView = getButtonImageViewAt(3)
|
val skipOpButton: ImageView = getButtonImageViewAt(3)
|
||||||
uiMediaController.bindViewToUIController(sourcesButton, SelectSourceController(sourcesButton))
|
uiMediaController.bindViewToUIController(sourcesButton, SelectSourceController(sourcesButton, this))
|
||||||
uiMediaController.bindViewToUIController(skipBackButton, SkipTimeController(skipBackButton, false))
|
uiMediaController.bindViewToUIController(skipBackButton, SkipTimeController(skipBackButton, false))
|
||||||
uiMediaController.bindViewToUIController(skipForwardButton, SkipTimeController(skipForwardButton, true))
|
uiMediaController.bindViewToUIController(skipForwardButton, SkipTimeController(skipForwardButton, true))
|
||||||
uiMediaController.bindViewToUIController(skipOpButton, SkipOpController(skipOpButton))
|
uiMediaController.bindViewToUIController(skipOpButton, SkipOpController(skipOpButton))
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.lagradost.cloudstream3.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.ProgressBar
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import com.google.android.gms.cast.framework.media.widget.MiniControllerFragment
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.adjustAlpha
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
|
||||||
|
import com.lagradost.cloudstream3.UIHelper.toPx
|
||||||
|
|
||||||
|
class MyMiniControllerFragment : MiniControllerFragment() {
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// SEE https://github.com/dandar3/android-google-play-services-cast-framework/blob/master/res/layout/cast_mini_controller.xml
|
||||||
|
try {
|
||||||
|
val progressBar: ProgressBar? = view.findViewById(R.id.progressBar)
|
||||||
|
val containerAll: LinearLayout? = view.findViewById(R.id.container_all)
|
||||||
|
|
||||||
|
context?.let { ctx ->
|
||||||
|
progressBar?.setBackgroundColor(adjustAlpha(ctx.colorFromAttribute(R.attr.colorPrimary), 0.35f))
|
||||||
|
val params = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 2.toPx)
|
||||||
|
|
||||||
|
progressBar?.layoutParams = params
|
||||||
|
}
|
||||||
|
val child = containerAll?.getChildAt(0)
|
||||||
|
child?.alpha = 0f // REMOVE GRADIENT
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// JUST IN CASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,7 @@ import com.lagradost.cloudstream3.utils.getId
|
||||||
import kotlinx.android.synthetic.main.fragment_player.*
|
import kotlinx.android.synthetic.main.fragment_player.*
|
||||||
import kotlinx.android.synthetic.main.player_custom_layout.*
|
import kotlinx.android.synthetic.main.player_custom_layout.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
@ -608,6 +609,7 @@ class PlayerFragment : Fragment() {
|
||||||
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
||||||
private var episodes: ArrayList<ResultEpisode> = ArrayList()
|
private var episodes: ArrayList<ResultEpisode> = ArrayList()
|
||||||
var currentPoster: String? = null
|
var currentPoster: String? = null
|
||||||
|
var currentHeaderName: String? = null
|
||||||
|
|
||||||
//region PIP MODE
|
//region PIP MODE
|
||||||
private fun getPen(code: PlayerEventType): PendingIntent {
|
private fun getPen(code: PlayerEventType): PendingIntent {
|
||||||
|
@ -717,7 +719,7 @@ class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
if (activity?.isCastApiAvailable() == true) {
|
if (activity?.isCastApiAvailable() == true) {
|
||||||
CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button)
|
CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button)
|
||||||
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
|
val castContext = CastContext.getSharedInstance(requireContext())
|
||||||
|
|
||||||
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) player_media_route_button.visibility = VISIBLE
|
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) player_media_route_button.visibility = VISIBLE
|
||||||
castContext.addCastStateListener { state ->
|
castContext.addCastStateListener { state ->
|
||||||
|
@ -732,20 +734,22 @@ class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
val index = links.indexOf(getCurrentUrl())
|
val index = links.indexOf(getCurrentUrl())
|
||||||
|
|
||||||
|
val customData =
|
||||||
|
links.map { JSONObject().put("name", it.name) }
|
||||||
|
val jsonArray = JSONArray()
|
||||||
|
for (item in customData) {
|
||||||
|
jsonArray.put(item)
|
||||||
|
}
|
||||||
|
|
||||||
val mediaItems = links.map {
|
val mediaItems = links.map {
|
||||||
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
|
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
|
||||||
movieMetadata.putString(
|
|
||||||
MediaMetadata.KEY_TITLE,
|
|
||||||
|
|
||||||
"Episode ${epData.episode}" +
|
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE,
|
||||||
if (epData.name != null)
|
|
||||||
"- ${epData.name}"
|
|
||||||
else
|
|
||||||
""
|
|
||||||
)
|
|
||||||
movieMetadata.putString(MediaMetadata.KEY_ALBUM_ARTIST,
|
|
||||||
epData.name ?: "Episode ${epData.episode}")
|
epData.name ?: "Episode ${epData.episode}")
|
||||||
|
|
||||||
|
if (currentHeaderName != null)
|
||||||
|
movieMetadata.putString(MediaMetadata.KEY_TITLE, currentHeaderName)
|
||||||
|
|
||||||
val srcPoster = epData.poster ?: currentPoster
|
val srcPoster = epData.poster ?: currentPoster
|
||||||
if (srcPoster != null) {
|
if (srcPoster != null) {
|
||||||
movieMetadata.addImage(WebImage(Uri.parse(srcPoster)))
|
movieMetadata.addImage(WebImage(Uri.parse(srcPoster)))
|
||||||
|
@ -755,7 +759,8 @@ class PlayerFragment : Fragment() {
|
||||||
MediaInfo.Builder(it.url)
|
MediaInfo.Builder(it.url)
|
||||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
||||||
.setCustomData(JSONObject().put("data", it.name))
|
|
||||||
|
.setCustomData(JSONObject().put("data", jsonArray))
|
||||||
.setMetadata(movieMetadata)
|
.setMetadata(movieMetadata)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
@ -838,6 +843,7 @@ class PlayerFragment : Fragment() {
|
||||||
if (d is LoadResponse) {
|
if (d is LoadResponse) {
|
||||||
localData = d
|
localData = d
|
||||||
currentPoster = d.posterUrl
|
currentPoster = d.posterUrl
|
||||||
|
currentHeaderName = d.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Resource.Failure -> {
|
is Resource.Failure -> {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -16,7 +15,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.mediarouter.app.MediaRouteButton
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
@ -33,6 +31,7 @@ import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.cast.framework.CastState
|
import com.google.android.gms.cast.framework.CastState
|
||||||
import com.google.android.gms.common.images.WebImage
|
import com.google.android.gms.common.images.WebImage
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||||
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
|
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
|
@ -42,9 +41,9 @@ import com.lagradost.cloudstream3.ui.player.PlayerFragment
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||||
import kotlinx.android.synthetic.main.fragment_result.*
|
import kotlinx.android.synthetic.main.fragment_result.*
|
||||||
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import android.app.ProgressDialog
|
|
||||||
import com.lagradost.cloudstream3.*
|
|
||||||
|
|
||||||
const val MAX_SYNO_LENGH = 300
|
const val MAX_SYNO_LENGH = 300
|
||||||
|
|
||||||
|
@ -80,6 +79,7 @@ class ResultFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var viewModel: ResultViewModel
|
private lateinit var viewModel: ResultViewModel
|
||||||
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
||||||
|
var currentHeaderName: String? = null
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -157,8 +157,8 @@ class ResultFragment : Fragment() {
|
||||||
//dialog.show()
|
//dialog.show()
|
||||||
Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
viewModel.loadEpisode(episodeClick.data, true) { data ->
|
viewModel.loadEpisode(episodeClick.data, false) { data ->
|
||||||
// dialog.dismiss()
|
// dialog.dismiss()
|
||||||
when (data) {
|
when (data) {
|
||||||
is Resource.Failure -> {
|
is Resource.Failure -> {
|
||||||
Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show()
|
||||||
|
@ -169,30 +169,30 @@ class ResultFragment : Fragment() {
|
||||||
|
|
||||||
val castContext = CastContext.getSharedInstance(requireContext())
|
val castContext = CastContext.getSharedInstance(requireContext())
|
||||||
|
|
||||||
|
val customData =
|
||||||
|
links.map { JSONObject().put("name", it.name) }
|
||||||
|
val jsonArray = JSONArray()
|
||||||
|
for (item in customData) {
|
||||||
|
jsonArray.put(item)
|
||||||
|
}
|
||||||
|
|
||||||
val mediaItems = links.map {
|
val mediaItems = links.map {
|
||||||
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
|
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
|
||||||
movieMetadata.putString(
|
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE,
|
||||||
MediaMetadata.KEY_TITLE,
|
|
||||||
|
|
||||||
"Episode ${epData.episode}" +
|
|
||||||
if (epData.name != null)
|
|
||||||
"- ${epData.name}"
|
|
||||||
else
|
|
||||||
""
|
|
||||||
)
|
|
||||||
movieMetadata.putString(MediaMetadata.KEY_ALBUM_ARTIST,
|
|
||||||
epData.name ?: "Episode ${epData.episode}")
|
epData.name ?: "Episode ${epData.episode}")
|
||||||
|
|
||||||
|
if (currentHeaderName != null)
|
||||||
|
movieMetadata.putString(MediaMetadata.KEY_TITLE, currentHeaderName)
|
||||||
|
|
||||||
val srcPoster = epData.poster ?: currentPoster
|
val srcPoster = epData.poster ?: currentPoster
|
||||||
if (srcPoster != null) {
|
if (srcPoster != null) {
|
||||||
movieMetadata.addImage(WebImage(Uri.parse(srcPoster)))
|
movieMetadata.addImage(WebImage(Uri.parse(srcPoster)))
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaQueueItem.Builder(
|
MediaQueueItem.Builder(
|
||||||
MediaInfo.Builder(it.url)
|
MediaInfo.Builder(it.url)
|
||||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
||||||
.setCustomData(JSONObject().put("data", it.name))
|
.setCustomData(JSONObject().put("data", jsonArray))
|
||||||
.setMetadata(movieMetadata)
|
.setMetadata(movieMetadata)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
@ -255,6 +255,8 @@ class ResultFragment : Fragment() {
|
||||||
if (d is LoadResponse) {
|
if (d is LoadResponse) {
|
||||||
result_bookmark_button.text = "Watching"
|
result_bookmark_button.text = "Watching"
|
||||||
|
|
||||||
|
currentHeaderName = d.name
|
||||||
|
|
||||||
currentPoster = d.posterUrl
|
currentPoster = d.posterUrl
|
||||||
|
|
||||||
result_openinbrower.setOnClickListener {
|
result_openinbrower.setOnClickListener {
|
||||||
|
|
|
@ -38,7 +38,8 @@ class CastOptionsProvider : OptionsProvider {
|
||||||
|
|
||||||
return CastOptions.Builder()
|
return CastOptions.Builder()
|
||||||
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
|
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID)
|
||||||
//.setReceiverApplicationId("C0868879") // C0868879 = SAMPLE, CHANGE TO A NICE ID at https://developers.google.com/cast/docs/registration
|
//.setReceiverApplicationId("")
|
||||||
|
// C0868879 = SAMPLE, CHANGE TO A NICE ID at https://developers.google.com/cast/docs/registration
|
||||||
.setStopReceiverApplicationWhenEndingSession(true)
|
.setStopReceiverApplicationWhenEndingSession(true)
|
||||||
.setCastMediaOptions(mediaOptions)
|
.setCastMediaOptions(mediaOptions)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object Coroutines {
|
||||||
|
fun main(work: suspend (() -> Unit)) {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
work()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
app/src/main/res/drawable/solid_primary.xml
Normal file
4
app/src/main/res/drawable/solid_primary.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?attr/colorPrimary"/>
|
||||||
|
</shape>
|
|
@ -42,13 +42,14 @@
|
||||||
app:layout_constraintBottom_toTopOf="@+id/nav_view"
|
app:layout_constraintBottom_toTopOf="@+id/nav_view"
|
||||||
android:id="@+id/cast_mini_controller_holder"
|
android:id="@+id/cast_mini_controller_holder"
|
||||||
>
|
>
|
||||||
|
<!--com.google.android.gms.cast.framework.media.widget.MiniControllerFragment-->
|
||||||
<fragment
|
<fragment
|
||||||
app:castControlButtons="@array/cast_mini_controller_control_buttons"
|
app:castControlButtons="@array/cast_mini_controller_control_buttons"
|
||||||
android:id="@+id/cast_mini_controller"
|
android:id="@+id/cast_mini_controller"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"
|
class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment"
|
||||||
tools:ignore="FragmentTagUsage">
|
tools:ignore="FragmentTagUsage">
|
||||||
</fragment>
|
</fragment>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -229,7 +229,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment"
|
class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment"
|
||||||
tools:ignore="FragmentTagUsage">
|
tools:ignore="FragmentTagUsage">
|
||||||
</fragment>
|
</fragment>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
<item name="tabMode">scrollable</item>
|
<item name="tabMode">scrollable</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="AlertDialogCustom" parent="Theme.AppCompat.Dialog.Alert">
|
<style name="AlertDialogCustom" parent="Theme.AppCompat.Dialog.Alert">
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
<item name="android:textColor">@color/textColor</item>
|
<item name="android:textColor">@color/textColor</item>
|
||||||
<item name="android:textColorPrimary">@color/textColor</item>
|
<item name="android:textColorPrimary">@color/textColor</item>
|
||||||
<!--<item name="android:background">@color/darkBackground</item>-->
|
<!--<item name="android:background">@color/darkBackground</item>-->
|
||||||
|
|
Loading…
Reference in a new issue