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
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@ -8,9 +9,16 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.Resource
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.withContext
class HomeViewModel : ViewModel() {
var repo: APIRepository? = null
@ -25,7 +33,43 @@ class HomeViewModel : ViewModel() {
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) {
APIRepository(api)
} else {

View file

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

View file

@ -197,7 +197,7 @@ class EpisodeAdapter(
downloadButton.setUpButton(
downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null,
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) {

View file

@ -65,7 +65,9 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
import jp.wasabeef.glide.transformations.BlurTransformation
import kotlinx.android.synthetic.main.fragment_result.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
@ -168,7 +170,7 @@ class ResultFragment : Fragment() {
savedInstanceState: Bundle?,
): View? {
viewModel =
ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
return inflater.inflate(R.layout.fragment_result, container, false)
}
@ -270,15 +272,17 @@ class ResultFragment : Fragment() {
}
}
if (activity?.isCastApiAvailable() == true) {
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
activity?.let {
if (it.isCastApiAvailable()) {
CastButtonFactory.setUpMediaRouteButton(it, media_route_button)
val castContext = CastContext.getSharedInstance(it.applicationContext)
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state ->
if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
castContext.addCastStateListener { state ->
if (media_route_button != null) {
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
}
}
}
}
@ -302,7 +306,7 @@ class ResultFragment : Fragment() {
}
result_back.setOnClickListener {
requireActivity().popCurrentPage()
activity?.popCurrentPage()
}
fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
@ -432,7 +436,8 @@ class ResultFragment : Fragment() {
currentType ?: return@let,
currentHeaderName ?: return@let,
currentPoster ?: return@let,
currentId ?: return@let
currentId ?: return@let,
System.currentTimeMillis(),
)
)
@ -451,7 +456,8 @@ class ResultFragment : Fragment() {
epData.id,
parentId,
epData.rating,
epData.descript
epData.descript,
System.currentTimeMillis(),
)
)
@ -559,76 +565,78 @@ class ResultFragment : Fragment() {
}
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
if (activity?.checkWrite() != true) {
activity?.requestRW()
if (activity?.checkWrite() == true) 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}\""
activity?.let { act ->
if (!act.checkWrite()) {
act.requestRW()
if (act.checkWrite()) return@main
}
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 -> {
if (buildInPlayer) {
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
.setCustomAnimations(
(activity as AppCompatActivity?)?.supportFragmentManager?.beginTransaction()
?.setCustomAnimations(
R.anim.enter_anim,
R.anim.exit_anim,
R.anim.pop_enter,
R.anim.pop_exit
)
.add(
)?.add(
R.id.homeRoot,
PlayerFragment.newInstance(
PlayerData(index, null, 0),
episodeClick.data.getRealPosition()
)
)
.commit()
)?.commit()
}
}
@ -924,7 +932,8 @@ class ResultFragment : Fragment() {
localId,
localId,
d.rating,
d.plot
d.plot,
System.currentTimeMillis(),
)
) { downloadClickEvent ->
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.WatchType
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.getResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
@ -55,10 +56,14 @@ class ResultViewModel : ViewModel() {
context.setResultWatchState(currentId, status.internalId)
val resultPage = page.value
if (resultPage != null) {
val current = context.getBookmarkedData(currentId)
val currentTime = System.currentTimeMillis()
context.setBookmarkedData(
currentId,
DataStoreHelper.BookmarkedData(
currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
resultPage.name,
resultPage.url,
resultPage.apiName,

View file

@ -62,6 +62,11 @@ class SearchFragment : Fragment() {
fixGrid()
}
override fun onDestroyView() {
main_search?.clearFocus()
super.onDestroyView()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -104,7 +109,8 @@ class SearchFragment : Fragment() {
}
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()
)
edit.apply()
@ -142,7 +148,7 @@ class SearchFragment : Fragment() {
search_loading_bar.alpha = 0f
}
is Resource.Failure -> {
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
searchExitIcon.alpha = 1f
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 ->
if (b) {
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
searchView?.postDelayed({
val imm: InputMethodManager? =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager?
imm?.showSoftInput(view.findFocus(), 0)
(activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager?)?.showSoftInput(
view.findFocus(),
0
)
}, 200)
}
}
@ -169,7 +178,7 @@ class SearchFragment : Fragment() {
//searchViewModel.search("iron man")
//(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,
R.anim.exit_anim,
R.anim.pop_enter,

View file

@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey
const val VIDEO_POS_DUR = "video_pos_dur"
@ -27,6 +28,8 @@ object DataStoreHelper {
data class BookmarkedData(
val parentId: Int,
val bookmarkedTime : Long,
val latestUpdatedTime : Long,
override val name: String,
override val url: String,
override val apiName: String,
@ -37,6 +40,11 @@ object DataStoreHelper {
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) {
if (id == null) return
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), data)

View file

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