fixed potential crash issues

This commit is contained in:
LagradOst 2021-07-30 23:03:46 +02:00
parent 891d9b717f
commit a467060486
8 changed files with 166 additions and 90 deletions

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.home package com.lagradost.cloudstream3.ui.home
import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -8,9 +9,16 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.HomePageResponse import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class HomeViewModel : ViewModel() { class HomeViewModel : ViewModel() {
var repo: APIRepository? = null var repo: APIRepository? = null
@ -25,7 +33,43 @@ class HomeViewModel : ViewModel() {
return APIRepository(apis.first { it.hasMainPage }) return APIRepository(apis.first { it.hasMainPage })
} }
fun load(api : MainAPI?) = viewModelScope.launch { private val availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>()
private val bookmarks = MutableLiveData<List<SearchResponse>>()
fun loadStoredData(context: Context, preferredWatchStatus: WatchType?) = viewModelScope.launch {
val watchStatusIds = withContext(Dispatchers.IO) {
context.getAllWatchStateIds().map { id ->
Pair(id, context.getResultWatchState(id))
}
}
val length = WatchType.values().size
val currentWatchTypes = HashSet<WatchType>()
for (watch in watchStatusIds) {
currentWatchTypes.add(watch.second)
if (currentWatchTypes.size >= length) {
break
}
}
if (currentWatchTypes.size <= 0) {
bookmarks.postValue(ArrayList())
return@launch
}
val watchStatus = preferredWatchStatus ?: currentWatchTypes.first()
availableWatchStatusTypes.postValue(
Pair(
watchStatus,
currentWatchTypes.sortedBy { it.internalId }.toList()
)
)
val list = withContext(Dispatchers.IO) {
watchStatusIds.map { context.getBookmarkedData(it.first) }
}
}
fun load(api: MainAPI?) = viewModelScope.launch {
repo = if (api?.hasMainPage == true) { repo = if (api?.hasMainPage == true) {
APIRepository(api) APIRepository(api)
} else { } else {

View file

@ -598,7 +598,7 @@ class PlayerFragment : Fragment() {
} }
} }
private lateinit var volumeObserver: SettingsContentObserver private var volumeObserver: SettingsContentObserver? = null
companion object { companion object {
fun newInstance(data: PlayerData, startPos: Long? = null) = fun newInstance(data: PlayerData, startPos: Long? = null) =
@ -913,19 +913,18 @@ class PlayerFragment : Fragment() {
resizeMode = requireContext().getKey(RESIZE_MODE_KEY, 0)!! resizeMode = requireContext().getKey(RESIZE_MODE_KEY, 0)!!
playbackSpeed = requireContext().getKey(PLAYBACK_SPEED_KEY, 1f)!! playbackSpeed = requireContext().getKey(PLAYBACK_SPEED_KEY, 1f)!!
volumeObserver = SettingsContentObserver( activity?.let {
Handler( it.contentResolver?.registerContentObserver(
Looper.getMainLooper() android.provider.Settings.System.CONTENT_URI, true, SettingsContentObserver(
), requireActivity() Handler(
) Looper.getMainLooper()
), it
activity?.contentResolver )
?.registerContentObserver(
android.provider.Settings.System.CONTENT_URI, true, volumeObserver
) )
}
if (!isDownloadedFile) { if (!isDownloadedFile) {
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java) viewModel = ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
observeDirectly(viewModel.episodes) { _episodes -> observeDirectly(viewModel.episodes) { _episodes ->
episodes = _episodes episodes = _episodes
@ -1347,7 +1346,7 @@ class PlayerFragment : Fragment() {
outState.putBoolean(STATE_PLAYER_PLAYING, isPlayerPlaying) outState.putBoolean(STATE_PLAYER_PLAYING, isPlayerPlaying)
outState.putInt(RESIZE_MODE_KEY, resizeMode) outState.putInt(RESIZE_MODE_KEY, resizeMode)
outState.putFloat(PLAYBACK_SPEED, playbackSpeed) outState.putFloat(PLAYBACK_SPEED, playbackSpeed)
if(!isDownloadedFile) { if (!isDownloadedFile) {
outState.putString("data", mapper.writeValueAsString(playerData)) outState.putString("data", mapper.writeValueAsString(playerData))
} }
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)

View file

@ -197,7 +197,7 @@ class EpisodeAdapter(
downloadButton.setUpButton( downloadButton.setUpButton(
downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null, downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null,
VideoDownloadHelper.DownloadEpisodeCached( VideoDownloadHelper.DownloadEpisodeCached(
card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript, System.currentTimeMillis(),
) )
) { ) {
if (it.action == DOWNLOAD_ACTION_DOWNLOAD) { if (it.action == DOWNLOAD_ACTION_DOWNLOAD) {

View file

@ -65,7 +65,9 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -168,7 +170,7 @@ class ResultFragment : Fragment() {
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View? { ): View? {
viewModel = viewModel =
ViewModelProvider(requireActivity()).get(ResultViewModel::class.java) ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
return inflater.inflate(R.layout.fragment_result, container, false) return inflater.inflate(R.layout.fragment_result, container, false)
} }
@ -270,15 +272,17 @@ class ResultFragment : Fragment() {
} }
} }
if (activity?.isCastApiAvailable() == true) { activity?.let {
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button) if (it.isCastApiAvailable()) {
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext) CastButtonFactory.setUpMediaRouteButton(it, media_route_button)
val castContext = CastContext.getSharedInstance(it.applicationContext)
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state -> castContext.addCastStateListener { state ->
if (media_route_button != null) { if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else { if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
}
} }
} }
} }
@ -302,7 +306,7 @@ class ResultFragment : Fragment() {
} }
result_back.setOnClickListener { result_back.setOnClickListener {
requireActivity().popCurrentPage() activity?.popCurrentPage()
} }
fun handleAction(episodeClick: EpisodeClickEvent): Job = main { fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
@ -432,7 +436,8 @@ class ResultFragment : Fragment() {
currentType ?: return@let, currentType ?: return@let,
currentHeaderName ?: return@let, currentHeaderName ?: return@let,
currentPoster ?: return@let, currentPoster ?: return@let,
currentId ?: return@let currentId ?: return@let,
System.currentTimeMillis(),
) )
) )
@ -451,7 +456,8 @@ class ResultFragment : Fragment() {
epData.id, epData.id,
parentId, parentId,
epData.rating, epData.rating,
epData.descript epData.descript,
System.currentTimeMillis(),
) )
) )
@ -559,76 +565,78 @@ class ResultFragment : Fragment() {
} }
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> { ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
if (activity?.checkWrite() != true) { activity?.let { act ->
activity?.requestRW() if (!act.checkWrite()) {
if (activity?.checkWrite() == true) return@main act.requestRW()
} if (act.checkWrite()) return@main
val data = currentLinks ?: return@main
val subs = currentSubs
val outputDir = requireContext().cacheDir
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
var text = "#EXTM3U"
if (subs != null) {
for (sub in subs) {
text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.lang}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.lang}\",URI=\"${sub.url}\""
} }
val data = currentLinks ?: return@main
val subs = currentSubs
val outputDir = requireContext().cacheDir
val outputFile = withContext(Dispatchers.IO) {
File.createTempFile("mirrorlist", ".m3u8", outputDir)
}
var text = "#EXTM3U"
if (subs != null) {
for (sub in subs) {
text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.lang}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.lang}\",URI=\"${sub.url}\""
}
}
for (link in data.sortedBy { -it.quality }) {
text += "\n#EXTINF:, ${link.name}\n${link.url}"
}
outputFile.writeText(text)
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
vlcIntent.setPackage(VLC_PACKAGE)
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
vlcIntent.setDataAndType(
FileProvider.getUriForFile(
act,
act.applicationContext.packageName + ".provider",
outputFile
), "video/*"
)
val startId = VLC_FROM_PROGRESS
var position = startId
if (startId == VLC_FROM_START) {
position = 1
} else if (startId == VLC_FROM_PROGRESS) {
position = 0
}
vlcIntent.putExtra("position", position)
vlcIntent.component = VLC_COMPONENT
requireContext().setKey(VLC_LAST_ID_KEY, episodeClick.data.id)
act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
} }
for (link in data.sortedBy { -it.quality }) {
text += "\n#EXTINF:, ${link.name}\n${link.url}"
}
outputFile.writeText(text)
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
vlcIntent.setPackage(VLC_PACKAGE)
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
vlcIntent.setDataAndType(
FileProvider.getUriForFile(
requireActivity(),
requireActivity().applicationContext.packageName + ".provider",
outputFile
), "video/*"
)
val startId = VLC_FROM_PROGRESS
var position = startId
if (startId == VLC_FROM_START) {
position = 1
} else if (startId == VLC_FROM_PROGRESS) {
position = 0
}
vlcIntent.putExtra("position", position)
vlcIntent.component = VLC_COMPONENT
requireContext().setKey(VLC_LAST_ID_KEY, episodeClick.data.id)
activity?.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
} }
ACTION_PLAY_EPISODE_IN_PLAYER -> { ACTION_PLAY_EPISODE_IN_PLAYER -> {
if (buildInPlayer) { if (buildInPlayer) {
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() (activity as AppCompatActivity?)?.supportFragmentManager?.beginTransaction()
.setCustomAnimations( ?.setCustomAnimations(
R.anim.enter_anim, R.anim.enter_anim,
R.anim.exit_anim, R.anim.exit_anim,
R.anim.pop_enter, R.anim.pop_enter,
R.anim.pop_exit R.anim.pop_exit
) )?.add(
.add(
R.id.homeRoot, R.id.homeRoot,
PlayerFragment.newInstance( PlayerFragment.newInstance(
PlayerData(index, null, 0), PlayerData(index, null, 0),
episodeClick.data.getRealPosition() episodeClick.data.getRealPosition()
) )
) )?.commit()
.commit()
} }
} }
@ -924,7 +932,8 @@ class ResultFragment : Fragment() {
localId, localId,
localId, localId,
d.rating, d.rating,
d.plot d.plot,
System.currentTimeMillis(),
) )
) { downloadClickEvent -> ) { downloadClickEvent ->
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) { if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {

View file

@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
@ -55,10 +56,14 @@ class ResultViewModel : ViewModel() {
context.setResultWatchState(currentId, status.internalId) context.setResultWatchState(currentId, status.internalId)
val resultPage = page.value val resultPage = page.value
if (resultPage != null) { if (resultPage != null) {
val current = context.getBookmarkedData(currentId)
val currentTime = System.currentTimeMillis()
context.setBookmarkedData( context.setBookmarkedData(
currentId, currentId,
DataStoreHelper.BookmarkedData( DataStoreHelper.BookmarkedData(
currentId, currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
resultPage.name, resultPage.name,
resultPage.url, resultPage.url,
resultPage.apiName, resultPage.apiName,

View file

@ -62,6 +62,11 @@ class SearchFragment : Fragment() {
fixGrid() fixGrid()
} }
override fun onDestroyView() {
main_search?.clearFocus()
super.onDestroyView()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -104,7 +109,8 @@ class SearchFragment : Fragment() {
} }
val edit = settingsManagerLocal.edit() val edit = settingsManagerLocal.edit()
edit.putStringSet(getString(R.string.search_providers_list_key), edit.putStringSet(
getString(R.string.search_providers_list_key),
apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet() apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet()
) )
edit.apply() edit.apply()
@ -142,7 +148,7 @@ class SearchFragment : Fragment() {
search_loading_bar.alpha = 0f search_loading_bar.alpha = 0f
} }
is Resource.Failure -> { is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show() // Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon.alpha = 1f searchExitIcon.alpha = 1f
search_loading_bar.alpha = 0f search_loading_bar.alpha = 0f
} }
@ -152,15 +158,18 @@ class SearchFragment : Fragment() {
} }
} }
} }
allApi.providersActive = requireActivity().getApiSettings() activity?.let {
allApi.providersActive = it.getApiSettings()
}
main_search.setOnQueryTextFocusChangeListener { searchView, b -> main_search.setOnQueryTextFocusChangeListener { searchView, b ->
if (b) { if (b) {
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview // https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
searchView?.postDelayed({ searchView?.postDelayed({
val imm: InputMethodManager? = (activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager?)?.showSoftInput(
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager? view.findFocus(),
imm?.showSoftInput(view.findFocus(), 0) 0
)
}, 200) }, 200)
} }
} }
@ -169,7 +178,7 @@ class SearchFragment : Fragment() {
//searchViewModel.search("iron man") //searchViewModel.search("iron man")
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
/* /*
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() (activity as AppCompatActivity?)?.supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.enter_anim, .setCustomAnimations(R.anim.enter_anim,
R.anim.exit_anim, R.anim.exit_anim,
R.anim.pop_enter, R.anim.pop_enter,

View file

@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
const val VIDEO_POS_DUR = "video_pos_dur" const val VIDEO_POS_DUR = "video_pos_dur"
@ -27,6 +28,8 @@ object DataStoreHelper {
data class BookmarkedData( data class BookmarkedData(
val parentId: Int, val parentId: Int,
val bookmarkedTime : Long,
val latestUpdatedTime : Long,
override val name: String, override val name: String,
override val url: String, override val url: String,
override val apiName: String, override val apiName: String,
@ -37,6 +40,11 @@ object DataStoreHelper {
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
fun Context.getAllWatchStateIds(): List<Int> {
val folder = "$currentAccount/$RESULT_WATCH_STATE"
return getKeys(folder).mapNotNull { it.removePrefix(folder).toIntOrNull() }
}
fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) { fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) {
if (id == null) return if (id == null) return
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), data) setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), data)

View file

@ -12,14 +12,16 @@ object VideoDownloadHelper {
val parentId: Int, val parentId: Int,
val rating: Int?, val rating: Int?,
val descript: String?, val descript: String?,
val cacheTime: Long,
) )
data class DownloadHeaderCached( data class DownloadHeaderCached(
val apiName: String, val apiName: String,
val url: String, val url: String,
val type : TvType, val type: TvType,
val name: String, val name: String,
val poster: String?, val poster: String?,
val id: Int, val id: Int,
val cacheTime: Long,
) )
} }