diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt index fc127d28..03aec126 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ShiroProvider.kt @@ -156,7 +156,7 @@ class ShiroProvider : MainAPI() { returnValue.add(AnimeSearchResponse( i.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle, - "$mainUrl/${i.slug}", + "$mainUrl/anime/${i.slug}", i.slug, this.name, type, diff --git a/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt b/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt index 453ea8ea..1a41796f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt @@ -26,6 +26,7 @@ sealed class Resource { val errorResponse: Any?, //ResponseBody val errorString: String, ) : Resource() + data class Loading(val url : String? = null) : Resource() } suspend fun safeApiCall( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt index da4eb526..80840815 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt @@ -3,8 +3,9 @@ package com.lagradost.cloudstream3.ui import android.os.Bundle import android.view.Menu import android.view.View.* -import android.widget.ImageView +import android.widget.* import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContentProviderCompat.requireContext import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.kotlin.KotlinModule @@ -14,9 +15,13 @@ import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastSession import com.google.android.gms.cast.framework.media.RemoteMediaClient import com.google.android.gms.cast.framework.media.uicontroller.UIController +import com.google.android.gms.cast.framework.media.widget.CastSeekBar import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity +import com.google.android.material.bottomsheet.BottomSheetDialog import com.lagradost.cloudstream3.APIHolder.getApiFromName import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.UIHelper +import com.lagradost.cloudstream3.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.sortUrls @@ -86,23 +91,35 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi init { view.setImageResource(R.drawable.ic_baseline_playlist_play_24) view.setOnClickListener { - lateinit var dialog: AlertDialog + // lateinit var dialog: AlertDialog val holder = getCurrentMetaData() if (holder != null) { val items = holder.currentLinks if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) { - val builder = AlertDialog.Builder(view.context, R.style.AlertDialogCustom) - builder.setTitle("Pick source") + // val builder = AlertDialog.Builder(view.context, R.style.AlertDialogCustom) + /*val builder = BottomSheetDialog(view.context, R.style.AlertDialogCustom) + builder.setTitle("Pick source")*/ + val bottomSheetDialog = BottomSheetDialog(view.context) + bottomSheetDialog.setContentView(R.layout.sort_bottom_sheet) + val res = bottomSheetDialog.findViewById(R.id.sort_click)!! //https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl ?: remoteMediaClient?.currentItem?.media?.contentId) - builder.setSingleChoiceItems( - items.map { it.name }.toTypedArray(), - items.indexOfFirst { it.url == contentUrl } - ) { _, which -> + val sortingMethods = items.map { it.name }.toTypedArray() + val sotringIndex = items.indexOfFirst { it.url == contentUrl } + + val arrayAdapter = ArrayAdapter(view.context, R.layout.sort_bottom_single_choice) + arrayAdapter.addAll(sortingMethods.toMutableList()) + + res.choiceMode = AbsListView.CHOICE_MODE_SINGLE + res.adapter = arrayAdapter + res.setItemChecked(sotringIndex, true) + + + res.setOnItemClickListener { _, _, which, _ -> val epData = holder.episodes[holder.currentEpisodeIndex] fun loadMirror(index: Int) { @@ -144,10 +161,12 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } loadMirror(which) - dialog.dismiss() + bottomSheetDialog.dismiss() } + bottomSheetDialog.show() + /* dialog = builder.create() - dialog.show() + dialog.show()*/ } } } @@ -276,5 +295,9 @@ class ControllerActivity : ExpandedControllerActivity() { uiMediaController.bindViewToUIController(skipBackButton, SkipTimeController(skipBackButton, false)) uiMediaController.bindViewToUIController(skipForwardButton, SkipTimeController(skipForwardButton, true)) uiMediaController.bindViewToUIController(skipOpButton, SkipNextEpisodeController(skipOpButton)) + /* val progressBar: CastSeekBar? = findViewById(R.id.cast_seek_bar) + + progressBar?.backgroundTintList = (UIHelper.adjustAlpha(colorFromAttribute(R.attr.colorPrimary), 0.35f)) +*/ } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 7c3e0674..bbdb68bb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -10,11 +10,14 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.widget.FrameLayout import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.widget.NestedScrollView import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -28,7 +31,9 @@ import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable +import com.lagradost.cloudstream3.UIHelper.popCurrentPage import com.lagradost.cloudstream3.UIHelper.popupMenu import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons import com.lagradost.cloudstream3.mvvm.Resource @@ -137,14 +142,47 @@ class ResultFragment : Fragment() { } } + /// 0 = LOADING, 1 = ERROR LOADING, 2 = LOADED + fun updateVisStatus(state: Int) { + when (state) { + 0 -> { + result_loading.visibility = VISIBLE + result_finish_loading.visibility = GONE + result_reload_connectionerror.visibility = GONE + result_reload_connection_open_in_browser.visibility = GONE + } + 1 -> { + result_loading.visibility = GONE + result_finish_loading.visibility = GONE + result_reload_connectionerror.visibility = VISIBLE + result_reload_connection_open_in_browser.visibility = if (url == null) GONE else VISIBLE + } + 2 -> { + result_loading.visibility = GONE + result_finish_loading.visibility = VISIBLE + result_reload_connectionerror.visibility = GONE + result_reload_connection_open_in_browser.visibility = GONE + } + } + } + private var currentPoster: String? = null + var url: String? = null + @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) activity?.fixPaddingStatusbar(result_scroll) activity?.fixPaddingStatusbar(result_barstatus) + val backParameter = result_back.layoutParams as CoordinatorLayout.LayoutParams + backParameter.setMargins(backParameter.leftMargin, + backParameter.topMargin + requireContext().getStatusBarHeight(), + backParameter.rightMargin, + backParameter.bottomMargin) + result_back.layoutParams = backParameter + if (activity?.isCastApiAvailable() == true) { CastButtonFactory.setUpMediaRouteButton(activity, media_route_button) val castContext = CastContext.getSharedInstance(requireActivity().applicationContext) @@ -160,16 +198,20 @@ class ResultFragment : Fragment() { } // activity?.fixPaddingStatusbar(result_toolbar) - val url = arguments?.getString("url") + url = arguments?.getString("url") val slug = arguments?.getString("slug") val apiName = arguments?.getString("apiName") result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ -> if (result_poster_blur == null) return@OnScrollChangeListener result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f)) + val setAlpha = 1f - scrollY / 200f + result_back.alpha = setAlpha result_poster_blur_holder.translationY = -scrollY.toFloat() + // result_back.translationY = -scrollY.toFloat() //result_barstatus.alpha = scrollY / 200f //result_barstatus.visibility = if (scrollY > 0) View.VISIBLE else View.GONE§ + result_back.visibility = if (setAlpha > 0) VISIBLE else GONE }) result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) @@ -177,77 +219,85 @@ class ResultFragment : Fragment() { activity?.onBackPressed() } + result_back.setOnClickListener { + requireActivity().popCurrentPage() + } + + fun handleAction(episodeClick: EpisodeClickEvent) { + //val id = episodeClick.data.id + val index = episodeClick.data.index + val buildInPlayer = true + currentLoadingCount++ + when (episodeClick.action) { + ACTION_CHROME_CAST_EPISODE -> { + val currentLoad = currentLoadingCount + val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent) + val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null) + builder.setView(customLayout) + + val dialog = builder.create() + + dialog.show() + dialog.setOnDismissListener { + currentLoadingCount++ + } + // Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show() + + viewModel.loadEpisode(episodeClick.data, true) { data -> + if (currentLoadingCount != currentLoad) return@loadEpisode + dialog.dismiss() + when (data) { + is Resource.Failure -> { + Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show() + } + is Resource.Success -> { + val eps = currentEpisodes ?: return@loadEpisode + context?.startCast( + apiName ?: return@loadEpisode, + currentHeaderName, + currentPoster, + episodeClick.data.index, + eps, + sortUrls(data.value), + startTime = episodeClick.data.getRealPosition() + ) + } + } + } + } + + ACTION_PLAY_EPISODE_IN_PLAYER -> { + if (buildInPlayer) { + (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.enter_anim, + R.anim.exit_anim, + R.anim.pop_enter, + R.anim.pop_exit) + .add(R.id.homeRoot, + PlayerFragment.newInstance(PlayerData(index, null, 0), + episodeClick.data.getRealPosition()) + ) + .commit() + } + } + ACTION_RELOAD_EPISODE -> { + /*viewModel.load(episodeClick.data) { res -> + if (res is Resource.Success) { + playEpisode(allEpisodes[id], index) + } + }*/ + } + + } + } + val adapter: RecyclerView.Adapter? = activity?.let { it -> EpisodeAdapter( it, ArrayList(), result_episodes, ) { episodeClick -> - //val id = episodeClick.data.id - val index = episodeClick.data.index - val buildInPlayer = true - currentLoadingCount++ - when (episodeClick.action) { - ACTION_CHROME_CAST_EPISODE -> { - val currentLoad = currentLoadingCount - val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent) - val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null) - builder.setView(customLayout) - - val dialog = builder.create() - - dialog.show() - dialog.setOnDismissListener { - currentLoadingCount++ - } - // Toast.makeText(activity, "Loading links", Toast.LENGTH_SHORT).show() - - viewModel.loadEpisode(episodeClick.data, true) { data -> - if (currentLoadingCount != currentLoad) return@loadEpisode - dialog.dismiss() - when (data) { - is Resource.Failure -> { - Toast.makeText(activity, "Failed to load links", Toast.LENGTH_SHORT).show() - } - is Resource.Success -> { - val eps = currentEpisodes ?: return@loadEpisode - context?.startCast( - apiName ?: return@loadEpisode, - currentHeaderName, - currentPoster, - episodeClick.data.index, - eps, - sortUrls(data.value), - startTime = episodeClick.data.getRealPosition() - ) - } - } - } - } - - ACTION_PLAY_EPISODE_IN_PLAYER -> { - if (buildInPlayer) { - (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() - .setCustomAnimations(R.anim.enter_anim, - R.anim.exit_anim, - R.anim.pop_enter, - R.anim.pop_exit) - .add(R.id.homeRoot, - PlayerFragment.newInstance(PlayerData(index, null, 0), - episodeClick.data.getRealPosition()) - ) - .commit() - } - } - ACTION_RELOAD_EPISODE -> { - /*viewModel.load(episodeClick.data) { res -> - if (res is Resource.Success) { - playEpisode(allEpisodes[id], index) - } - }*/ - } - - } + handleAction(episodeClick) } } @@ -288,6 +338,8 @@ class ResultFragment : Fragment() { is Resource.Success -> { val d = data.value if (d is LoadResponse) { + updateVisStatus(2) + result_bookmark_button.text = "Watching" currentHeaderName = d.name @@ -407,6 +459,34 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE) result_tag_holder.visibility = GONE result_status.visibility = GONE + when (d.type) { + TvType.Movie -> { + result_play_movie.visibility = VISIBLE + result_episodes_text.visibility = GONE + result_episodes.visibility = GONE + + result_play_movie.setOnClickListener { + val card = currentEpisodes?.first() ?: return@setOnClickListener + if (requireContext().isCastApiAvailable()) { + val castContext = CastContext.getSharedInstance(requireContext()) + + if (castContext.castState == CastState.CONNECTED) { + handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) + } else { + handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) + } + } else { + handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) + } + } + } + else -> { + result_play_movie.visibility = GONE + result_episodes_text.visibility = VISIBLE + result_episodes.visibility = VISIBLE + } + } + when (d) { is AnimeLoadResponse -> { result_status.visibility = VISIBLE @@ -438,15 +518,34 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE) } else -> result_title.text = d.name } + } else { + updateVisStatus(1) } } is Resource.Failure -> { - + updateVisStatus(1) + } + is Resource.Loading -> { + updateVisStatus(0) } } } - if (viewModel.resultResponse.value == null && apiName != null && slug != null) - viewModel.load(requireContext(), slug, apiName) + if (apiName != null && slug != null) { + result_reload_connectionerror.setOnClickListener { + viewModel.load(requireContext(), slug, apiName) + } + + if (url != null) { + result_reload_connection_open_in_browser.setOnClickListener { + val i = Intent(Intent.ACTION_VIEW) + i.data = Uri.parse(url) + startActivity(i) + } + } + + if (viewModel.resultResponse.value == null) + viewModel.load(requireContext(), slug, apiName) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt index 977885de..164bad76 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt @@ -56,11 +56,14 @@ class ResultViewModel : ViewModel() { } fun load(context: Context, url: String, apiName: String) = viewModelScope.launch { + _resultResponse.postValue(Resource.Loading(url)) + _apiName.postValue(apiName) val api = getApiFromName(apiName) val data = safeApiCall { api.load(url) } + _resultResponse.postValue(data) when (data) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 701c6c28..7a8e2b9d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -114,8 +114,6 @@ class SearchFragment : Fragment() { main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - search_exit_icon.alpha = 0f - search_loading_bar.alpha = 1f searchViewModel.search(query) return true } @@ -128,17 +126,26 @@ class SearchFragment : Fragment() { observe(searchViewModel.searchResponse) { when (it) { is Resource.Success -> { - (cardSpace.adapter as SearchAdapter).cardList = it.value - (cardSpace.adapter as SearchAdapter).notifyDataSetChanged() + it?.value?.let { data -> + (cardSpace.adapter as SearchAdapter).cardList = data + (cardSpace.adapter as SearchAdapter).notifyDataSetChanged() + } + search_exit_icon.alpha = 1f + search_loading_bar.alpha = 0f } is Resource.Failure -> { Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show() + search_exit_icon.alpha = 1f + search_loading_bar.alpha = 0f + } + is Resource.Loading -> { + search_exit_icon.alpha = 0f + search_loading_bar.alpha = 1f } } - search_exit_icon.alpha = 1f - search_loading_bar.alpha = 0f } - (activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") + searchViewModel.search("overlord") + // (activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") /* (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.enter_anim, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt index 8e9c7a4b..1cd8abe1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt @@ -17,6 +17,7 @@ class SearchViewModel : ViewModel() { val searchResponse: LiveData>> get() = _searchResponse fun search(query: String) = viewModelScope.launch { + _searchResponse.postValue(Resource.Loading()) val data = safeApiCall { api.search(query) } diff --git a/app/src/main/res/drawable/ic_baseline_autorenew_24.xml b/app/src/main/res/drawable/ic_baseline_autorenew_24.xml new file mode 100644 index 00000000..565d7f34 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_autorenew_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_clear_24.xml b/app/src/main/res/drawable/ic_baseline_clear_24.xml new file mode 100644 index 00000000..70db409b --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_clear_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index 6de1de1c..7b175717 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -9,241 +9,323 @@ android:clickable="true" android:focusable="true" > - + + + + + + + - - - - - - + - + android:alpha="0" + tools:alpha="1" + android:layout_height="wrap_content"> + + + - - - - - + + + + android:orientation="vertical" + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + + + + + - - - - - - - + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + android:visibility="gone" + class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment" + tools:ignore="FragmentTagUsage"> + - - - - - + - + + \ No newline at end of file diff --git a/app/src/main/res/layout/sort_bottom_sheet.xml b/app/src/main/res/layout/sort_bottom_sheet.xml new file mode 100644 index 00000000..a79b6366 --- /dev/null +++ b/app/src/main/res/layout/sort_bottom_sheet.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/app/src/main/res/layout/sort_bottom_single_choice.xml b/app/src/main/res/layout/sort_bottom_single_choice.xml new file mode 100644 index 00000000..02a32b4d --- /dev/null +++ b/app/src/main/res/layout/sort_bottom_single_choice.xml @@ -0,0 +1,13 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89d374ec..371ed63a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,4 +30,8 @@ Dropped Plan to Watch None + Play Movie + Pick Source + Retry connection… + Go Back \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bcf71e6a..443bae90 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -129,8 +129,8 @@ @array/cast_expanded_controller_control_buttons @null - @color/white - ?attr/darkBackground + @color/white + ?attr/darkBackground ?attr/colorPrimary ?attr/colorPrimary @drawable/ic_baseline_play_arrow_24