1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-09-01 02:17:56 +00:00

More improve Kotlin converted from java in various places

This commit is contained in:
Stypox
2025-06-10 18:30:56 +02:00
parent 317db719db
commit a4182b474b
2 changed files with 122 additions and 164 deletions

View File

@@ -7,7 +7,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.pm.ActivityInfo
import android.database.ContentObserver
@@ -18,7 +17,6 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.text.TextUtils
import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue
@@ -44,14 +42,14 @@ import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.core.os.postDelayed
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.preference.PreferenceManager
import coil3.util.CoilUtils.dispose
import coil3.util.CoilUtils
import com.evernote.android.state.State
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.PlaybackParameters
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
@@ -115,17 +113,15 @@ import org.schabi.newpipe.util.StreamTypeUtil
import org.schabi.newpipe.util.ThemeHelper
import org.schabi.newpipe.util.external_communication.KoreUtils
import org.schabi.newpipe.util.external_communication.ShareUtils
import org.schabi.newpipe.util.image.CoilHelper.loadAvatar
import org.schabi.newpipe.util.image.CoilHelper.loadDetailsThumbnail
import org.schabi.newpipe.util.image.CoilHelper
import java.util.LinkedList
import java.util.Objects
import java.util.concurrent.TimeUnit
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class VideoDetailFragment :
BaseStateFragment<StreamInfo?>(),
BaseStateFragment<StreamInfo>(),
BackPressable,
PlayerServiceExtendedEventListener,
OnKeyDownListener {
@@ -160,15 +156,15 @@ class VideoDetailFragment :
private var lastAppBarVerticalOffset = Int.Companion.MAX_VALUE // prevents useless updates
private val preferenceChangeListener =
OnSharedPreferenceChangeListener { sharedPreferences: SharedPreferences?, key: String? ->
OnSharedPreferenceChangeListener { sharedPreferences, key ->
if (getString(R.string.show_comments_key) == key) {
showComments = sharedPreferences!!.getBoolean(key, true)
showComments = sharedPreferences.getBoolean(key, true)
tabSettingsChanged = true
} else if (getString(R.string.show_next_video_key) == key) {
showRelatedItems = sharedPreferences!!.getBoolean(key, true)
showRelatedItems = sharedPreferences.getBoolean(key, true)
tabSettingsChanged = true
} else if (getString(R.string.show_description_key) == key) {
showDescription = sharedPreferences!!.getBoolean(key, true)
showDescription = sharedPreferences.getBoolean(key, true)
tabSettingsChanged = true
}
}
@@ -198,11 +194,11 @@ class VideoDetailFragment :
// It will do nothing if the player is not in fullscreen mode
hideSystemUiIfNeeded()
val mainUi = player?.UIs()?.get(MainPlayerUi::class)
if (player?.videoPlayerSelected() != true && !playAfterConnect) {
return
}
val mainUi = player?.UIs()?.get(MainPlayerUi::class)
if (DeviceUtils.isLandscape(requireContext())) {
// If the video is playing but orientation changed
// let's make the video in fullscreen again
@@ -252,7 +248,7 @@ class VideoDetailFragment :
setupBroadcastReceiver()
settingsContentObserver = object : ContentObserver(Handler()) {
settingsContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
if (activity != null && !PlayerHelper.globalScreenOrientationLocked(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
@@ -277,9 +273,7 @@ class VideoDetailFragment :
override fun onPause() {
super.onPause()
if (currentWorker != null) {
currentWorker!!.dispose()
}
currentWorker?.dispose()
restoreDefaultBrightness()
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
putString(
@@ -304,9 +298,7 @@ class VideoDetailFragment :
if (tabSettingsChanged) {
tabSettingsChanged = false
initTabs()
if (currentInfo != null) {
updateTabs(currentInfo!!)
}
currentInfo?.let { updateTabs(it) }
}
// Check if it was loading when the fragment was stopped/paused
@@ -339,12 +331,8 @@ class VideoDetailFragment :
activity.unregisterReceiver(broadcastReceiver)
activity.contentResolver.unregisterContentObserver(settingsContentObserver!!)
if (positionSubscriber != null) {
positionSubscriber!!.dispose()
}
if (currentWorker != null) {
currentWorker!!.dispose()
}
positionSubscriber?.dispose()
currentWorker?.dispose()
disposables.clear()
positionSubscriber = null
currentWorker = null
@@ -353,7 +341,7 @@ class VideoDetailFragment :
if (activity.isFinishing) {
playQueue = null
currentInfo = null
stack = LinkedList<StackItem?>()
stack = LinkedList<StackItem>()
}
}
@@ -367,8 +355,7 @@ class VideoDetailFragment :
if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
NavigationHelper.openVideoDetailFragment(
requireContext(), getFM(),
serviceId, url, title, null, false
requireContext(), getFM(), serviceId, url, title, null, false
)
} else {
Log.e(TAG, "ReCaptcha failed")
@@ -385,11 +372,11 @@ class VideoDetailFragment :
binding.detailTitleRootLayout.setOnClickListener { toggleTitleAndSecondaryControls() }
binding.detailUploaderRootLayout.setOnClickListener(
makeOnClickListener { info ->
if (TextUtils.isEmpty(info.subChannelUrl)) {
if (!TextUtils.isEmpty(info.uploaderUrl)) {
if (info.subChannelUrl.isEmpty()) {
if (info.uploaderUrl.isNotEmpty()) {
openChannel(info.uploaderUrl, info.uploaderName, info.serviceId)
} else if (DEBUG) {
Log.i(TAG, "Can't open sub-channel because we got no channel URL")
Log.w(TAG, "Can't open sub-channel because we got no channel URL")
}
} else {
openChannel(info.subChannelUrl, info.subChannelName, info.serviceId)
@@ -495,7 +482,7 @@ class VideoDetailFragment :
}
binding.detailUploaderRootLayout.setOnLongClickListener(
makeOnLongClickListener { info ->
if (TextUtils.isEmpty(info.subChannelUrl)) {
if (info.subChannelUrl.isEmpty()) {
Log.w(TAG, "Can't open parent channel because we got no parent channel URL")
} else {
openChannel(info.uploaderUrl, info.uploaderName, info.serviceId)
@@ -541,7 +528,7 @@ class VideoDetailFragment :
}
private fun toggleTitleAndSecondaryControls() {
if (binding.detailSecondaryControlPanel.visibility == View.GONE) {
if (binding.detailSecondaryControlPanel.isGone) {
binding.detailVideoTitleView.setMaxLines(10)
binding.detailToggleSecondaryControlsView
.animateRotation(VideoPlayerUi.DEFAULT_CONTROLS_DURATION, 180)
@@ -569,18 +556,12 @@ class VideoDetailFragment :
binding.detailThumbnailRootLayout.requestFocus()
binding.detailControlsPlayWithKodi.visibility =
if (KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId))
View.VISIBLE
else
View.GONE
binding.detailControlsCrashThePlayer.visibility =
if (DEBUG && PreferenceManager.getDefaultSharedPreferences(requireContext())
.getBoolean(getString(R.string.show_crash_the_player_key), false)
)
View.VISIBLE
else
View.GONE
binding.detailControlsPlayWithKodi.isVisible =
KoreUtils.shouldShowPlayWithKodi(requireContext(), serviceId)
binding.detailControlsCrashThePlayer.isVisible =
DEBUG && PreferenceManager.getDefaultSharedPreferences(requireContext())
.getBoolean(getString(R.string.show_crash_the_player_key), false)
accommodateForTvAndDesktopMode()
}
@@ -591,8 +572,8 @@ class VideoDetailFragment :
setOnClickListeners()
setOnLongClickListeners()
val controlsTouchListener = OnTouchListener { view: View?, motionEvent: MotionEvent? ->
if (motionEvent!!.action == MotionEvent.ACTION_DOWN &&
val controlsTouchListener = OnTouchListener { view, motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_DOWN &&
PlayButtonHelper.shouldShowHoldToAppendTip(activity)
) {
binding.touchAppendDetail.animate(true, 250, AnimationType.ALPHA, 0) {
@@ -604,16 +585,14 @@ class VideoDetailFragment :
binding.detailControlsBackground.setOnTouchListener(controlsTouchListener)
binding.detailControlsPopup.setOnTouchListener(controlsTouchListener)
binding.appBarLayout.addOnOffsetChangedListener(
OnOffsetChangedListener { layout: AppBarLayout?, verticalOffset: Int ->
// prevent useless updates to tab layout visibility if nothing changed
if (verticalOffset != lastAppBarVerticalOffset) {
lastAppBarVerticalOffset = verticalOffset
// the view was scrolled
updateTabLayoutVisibility()
}
binding.appBarLayout.addOnOffsetChangedListener { layout, verticalOffset ->
// prevent useless updates to tab layout visibility if nothing changed
if (verticalOffset != lastAppBarVerticalOffset) {
lastAppBarVerticalOffset = verticalOffset
// the view was scrolled
updateTabLayoutVisibility()
}
)
}
setupBottomPlayer()
if (!PlayerHolder.isBound) {
@@ -656,7 +635,7 @@ class VideoDetailFragment :
// Remove top
stack.pop()
// Get stack item from the new top
setupFromHistoryItem(Objects.requireNonNull<StackItem?>(stack.peek()))
setupFromHistoryItem(stack.peek()!!)
return true
}
@@ -688,10 +667,9 @@ class VideoDetailFragment :
return
}
currentInfo?.let { info ->
prepareAndHandleInfoIfNeededAfterDelay(info, false, 50)
} ?: {
prepareAndLoadInfo()
when (val info = currentInfo) {
null -> prepareAndLoadInfo()
else -> prepareAndHandleInfoIfNeededAfterDelay(info, false, 50)
}
}
@@ -756,9 +734,7 @@ class VideoDetailFragment :
initTabs()
currentInfo = null
if (currentWorker != null) {
currentWorker!!.dispose()
}
currentWorker?.dispose()
runWorker(forceLoad, addToBackStack ?: stack.isEmpty())
}
@@ -783,7 +759,7 @@ class VideoDetailFragment :
if (playQueue == null) {
playQueue = SinglePlayQueue(result)
}
if (stack.isEmpty() || stack.peek()!!.playQueue != playQueue) {
if (stack.peek()?.playQueue != playQueue) { // also if stack empty (!)
stack.push(StackItem(serviceId, url, title, playQueue))
}
}
@@ -866,13 +842,14 @@ class VideoDetailFragment :
private fun updateTabs(info: StreamInfo) {
if (showRelatedItems) {
if (binding.relatedItemsLayout == null) { // phone
pageAdapter.updateItem(RELATED_TAB_TAG, getInstance(info))
} else { // tablet + TV
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedItemsLayout, getInstance(info))
.commitAllowingStateLoss()
binding.relatedItemsLayout!!.isVisible = !this.isFullscreen
when (val relatedItemsLayout = binding.relatedItemsLayout) {
null -> pageAdapter.updateItem(RELATED_TAB_TAG, getInstance(info)) // phone
else -> { // tablet + TV
getChildFragmentManager().beginTransaction()
.replace(R.id.relatedItemsLayout, getInstance(info))
.commitAllowingStateLoss()
relatedItemsLayout.isVisible = !this.isFullscreen
}
}
}
@@ -909,8 +886,7 @@ class VideoDetailFragment :
// call `post()` to be sure `viewPager.getHitRect()`
// is up to date and not being currently recomputed
binding.tabLayout.post {
val activity = getActivity()
if (activity != null) {
getActivity()?.let { activity ->
val pagerHitRect = Rect()
binding.viewPager.getHitRect(pagerHitRect)
@@ -1056,7 +1032,7 @@ class VideoDetailFragment :
}
private fun openMainPlayer() {
if (noPlayerServiceAvailable()) {
if (playerService == null) {
PlayerHolder.startService(autoPlayEnabled, this)
return
}
@@ -1082,13 +1058,13 @@ class VideoDetailFragment :
*/
private fun hideMainPlayerOnLoadingNewStream() {
val root = this.root
if (noPlayerServiceAvailable() || root == null || !player!!.videoPlayerSelected()) {
if (root == null || playerService == null || player?.videoPlayerSelected() != true) {
return
}
removeVideoPlayerView()
if (this.isAutoplayEnabled) {
playerService!!.stopForImmediateReusing()
playerService?.stopForImmediateReusing()
root.visibility = View.GONE
} else {
PlayerHolder.stopService()
@@ -1127,8 +1103,11 @@ class VideoDetailFragment :
val recordManager = HistoryRecordManager(requireContext())
disposables.add(
recordManager.onViewed(info).onErrorComplete()
.subscribe({ }, { throwable -> Log.e(TAG, "Register view failure: ", throwable) })
recordManager.onViewed(info)
.subscribe(
{ /* successful */ },
{ throwable -> Log.e(TAG, "Register view failure: ", throwable) }
)
)
}
@@ -1157,8 +1136,10 @@ class VideoDetailFragment :
if (player == null || view == null) {
return@post
}
// setup the surface view height, so that it fits the video correctly
setHeightThumbnail()
player?.UIs()?.get(MainPlayerUi::class)?.let { playerUi ->
// sometimes binding would be null here, even though getView() != null above u.u
nullableBinding?.let { b ->
@@ -1186,13 +1167,13 @@ class VideoDetailFragment :
}
private val preDrawListener: OnPreDrawListener = OnPreDrawListener {
if (view != null) {
view?.let { view ->
val decorView = if (DeviceUtils.isInMultiWindow(activity))
requireView()
view
else
activity.window.decorView
setHeightThumbnail(decorView.height, resources.displayMetrics)
requireView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener)
view.getViewTreeObserver().removeOnPreDrawListener(preDrawListener)
}
return@OnPreDrawListener false
}
@@ -1366,20 +1347,20 @@ class VideoDetailFragment :
binding.relatedItemsLayout?.isVisible = showRelatedItems && !this.isFullscreen
dispose(binding.detailThumbnailImageView)
dispose(binding.detailSubChannelThumbnailView)
dispose(binding.overlayThumbnail)
dispose(binding.detailUploaderThumbnailView)
CoilUtils.dispose(binding.detailThumbnailImageView)
CoilUtils.dispose(binding.detailSubChannelThumbnailView)
CoilUtils.dispose(binding.overlayThumbnail)
CoilUtils.dispose(binding.detailUploaderThumbnailView)
binding.detailThumbnailImageView.setImageBitmap(null)
binding.detailSubChannelThumbnailView.setImageBitmap(null)
}
override fun handleResult(info: StreamInfo?) {
override fun handleResult(info: StreamInfo) {
super.handleResult(info)
currentInfo = info
setInitialData(info!!.serviceId, info.originalUrl, info.name, playQueue)
setInitialData(info.serviceId, info.originalUrl, info.name, playQueue)
updateTabs(info)
@@ -1388,10 +1369,10 @@ class VideoDetailFragment :
binding.detailSubChannelThumbnailView.visibility = View.GONE
if (!TextUtils.isEmpty(info.subChannelName)) {
displayBothUploaderAndSubChannel(info)
} else {
if (info.subChannelName.isEmpty()) {
displayUploaderAsSubChannel(info)
} else {
displayBothUploaderAndSubChannel(info)
}
if (info.viewCount >= 0) {
@@ -1459,10 +1440,7 @@ class VideoDetailFragment :
binding.detailSecondaryControlPanel.visibility = View.GONE
checkUpdateProgressInfo(info)
loadDetailsThumbnail(
binding.detailThumbnailImageView,
info.thumbnails
)
CoilHelper.loadDetailsThumbnail(binding.detailThumbnailImageView, info.thumbnails)
ExtractorHelper.showMetaInfoInTextView(
info.metaInfo, binding.detailMetaInfoTextView,
binding.detailMetaInfoSeparator, disposables
@@ -1475,12 +1453,8 @@ class VideoDetailFragment :
if (!info.errors.isEmpty()) {
// Bandcamp fan pages are not yet supported and thus a ContentNotAvailableException is
// thrown. This is not an error and thus should not be shown to the user.
for (throwable in info.errors) {
if (throwable is ContentNotSupportedException &&
"Fan pages are not supported" == throwable.message
) {
info.errors.remove(throwable)
}
info.errors.removeIf {
it is ContentNotSupportedException && "Fan pages are not supported" == it.message
}
if (!info.errors.isEmpty()) {
@@ -1490,8 +1464,9 @@ class VideoDetailFragment :
}
}
val hasAudioStreams = info.videoStreams.isNotEmpty() || info.audioStreams.isNotEmpty()
binding.detailControlsDownload.isVisible = !StreamTypeUtil.isLiveStream(info.streamType)
val hasAudioStreams = info.videoStreams.isNotEmpty() || info.audioStreams.isNotEmpty()
binding.detailControlsBackground.isVisible = hasAudioStreams
val hasVideoStreams = info.videoStreams.isNotEmpty() || info.videoOnlyStreams.isNotEmpty()
@@ -1514,7 +1489,7 @@ class VideoDetailFragment :
binding.detailUploaderTextView.visibility = View.GONE
}
loadAvatar(binding.detailSubChannelThumbnailView, info.uploaderAvatars)
CoilHelper.loadAvatar(binding.detailSubChannelThumbnailView, info.uploaderAvatars)
binding.detailSubChannelThumbnailView.visibility = View.VISIBLE
binding.detailUploaderThumbnailView.visibility = View.GONE
}
@@ -1525,8 +1500,8 @@ class VideoDetailFragment :
binding.detailSubChannelTextView.setSelected(true)
val subText = StringBuilder()
if (!TextUtils.isEmpty(info.uploaderName)) {
subText.append(String.format(getString(R.string.video_detail_by), info.uploaderName))
if (info.uploaderName.isNotEmpty()) {
subText.append(getString(R.string.video_detail_by, info.uploaderName))
}
if (info.uploaderSubscriberCount > -1) {
if (subText.isNotEmpty()) {
@@ -1545,26 +1520,22 @@ class VideoDetailFragment :
binding.detailUploaderTextView.setSelected(true)
}
loadAvatar(binding.detailSubChannelThumbnailView, info.subChannelAvatars)
CoilHelper.loadAvatar(binding.detailSubChannelThumbnailView, info.subChannelAvatars)
binding.detailSubChannelThumbnailView.visibility = View.VISIBLE
loadAvatar(binding.detailUploaderThumbnailView, info.uploaderAvatars)
CoilHelper.loadAvatar(binding.detailUploaderThumbnailView, info.uploaderAvatars)
binding.detailUploaderThumbnailView.visibility = View.VISIBLE
}
fun openDownloadDialog() {
if (currentInfo == null) {
return
}
val info = currentInfo ?: return
try {
val downloadDialog = DownloadDialog(activity, currentInfo!!)
val downloadDialog = DownloadDialog(activity, info)
downloadDialog.show(activity.supportFragmentManager, "downloadDialog")
} catch (e: Exception) {
showSnackbar(
activity,
ErrorInfo(
e, UserAction.DOWNLOAD_OPEN_DIALOG, "Showing download dialog", currentInfo
)
ErrorInfo(e, UserAction.DOWNLOAD_OPEN_DIALOG, "Showing download dialog", info)
)
}
}
@@ -1586,7 +1557,7 @@ class VideoDetailFragment :
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ state -> updatePlaybackProgress(state.progressMillis, info.duration * 1000) },
{ throwable -> /* ignore errors */ },
{ throwable -> /* impossible due to the onErrorComplete() */ },
{ /* onComplete */
binding.positionView.visibility = View.GONE
binding.detailPositionView.visibility = View.GONE
@@ -1600,11 +1571,10 @@ class VideoDetailFragment :
}
val progressSeconds = TimeUnit.MILLISECONDS.toSeconds(progress).toInt()
val durationSeconds = TimeUnit.MILLISECONDS.toSeconds(duration).toInt()
binding.positionView.setMax(durationSeconds)
// If the old and the new progress values have a big difference then use animation.
// Otherwise don't because it affects CPU
val progressDifference = abs(binding.positionView.progress - progressSeconds)
binding.positionView.setMax(durationSeconds)
if (progressDifference > 2) {
if (abs(binding.positionView.progress - progressSeconds) > 2) {
binding.positionView.setProgressAnimated(progressSeconds)
} else {
binding.positionView.progress = progressSeconds
@@ -1637,15 +1607,15 @@ class VideoDetailFragment :
}
// Register broadcast receiver to listen to playQueue changes
// and hide the overlayPlayQueueButton when the playQueue is empty / destroyed.
// and hide the overlayPlayQueueButton when the playQueue is empty / destroyed.7
playQueue?.broadcastReceiver?.subscribe { updateOverlayPlayQueueButtonVisibility() }
?.let { disposables.add(it) }
// This should be the only place where we push data to stack.
// It will allow to have live instance of PlayQueue with actual information about
// deleted/added items inside Channel/Playlist queue and makes possible to have
// a history of played items
val stackPeek: StackItem? = stack.peek()
if (stackPeek != null && stackPeek.playQueue != queue) {
if (stack.peek()?.playQueue?.equals(queue) == false) {
queue.item?.let { queueItem ->
stack.push(StackItem(queueItem.serviceId, queueItem.url, queueItem.title, queue))
return@onQueueUpdate
@@ -1703,7 +1673,7 @@ class VideoDetailFragment :
}
updateOverlayData(info.name, info.uploaderName, info.thumbnails)
if (currentInfo?.url == info.url) {
if (info.url == currentInfo?.url) {
return
}
@@ -1780,17 +1750,15 @@ class VideoDetailFragment :
* Will scroll down to description view after long click on moreOptionsButton
* */
override fun onMoreOptionsLongClicked() {
val params =
binding.appBarLayout.layoutParams as CoordinatorLayout.LayoutParams
val params = binding.appBarLayout.layoutParams as CoordinatorLayout.LayoutParams
val behavior = params.behavior as AppBarLayout.Behavior
val valueAnimator = ValueAnimator.ofInt(0, -binding.playerPlaceholder.height)
valueAnimator.interpolator = DecelerateInterpolator()
valueAnimator.addUpdateListener { animation ->
behavior.setTopAndBottomOffset(animation.getAnimatedValue() as Int)
binding.appBarLayout.requestLayout()
}
valueAnimator.interpolator = DecelerateInterpolator()
valueAnimator.setDuration(500)
valueAnimator.duration = 500
valueAnimator.start()
}
@@ -1867,9 +1835,11 @@ class VideoDetailFragment :
private val isFullscreen: Boolean
get() = player?.UIs()?.get(VideoPlayerUi::class)?.isFullscreen == true
/**
* @return true if the player is null, or if the player is nonnull but is stopped.
*/
@Suppress("NullableBooleanElvis") // rewriting as "!= false" creates more confusion
private val playerIsStopped
// returns true if the player is null, or if the player is nonnull but is stopped
get() = player?.isStopped ?: true
private fun restoreDefaultBrightness() {
@@ -2017,9 +1987,8 @@ class VideoDetailFragment :
.map { it.getResolution() as CharSequence }
.toTypedArray()
builder.setSingleChoiceItems(
resolutions, selectedVideoStreamIndexForExternalPlayers, null
)
builder
.setSingleChoiceItems(resolutions, selectedVideoStreamIndexForExternalPlayers, null)
builder.setNegativeButton(R.string.cancel, null)
builder.setPositiveButton(R.string.ok) { dialog, which ->
val index = (dialog as AlertDialog).listView.getCheckedItemPosition()
@@ -2050,16 +2019,14 @@ class VideoDetailFragment :
startOnExternalPlayer(activity, info, audioTracks[0])
} else {
val selectedAudioStream = ListHelper.getDefaultAudioFormat(activity, audioTracks)
val trackNames = audioTracks
.map { Localization.audioTrackName(activity, it) }
.toTypedArray()
val trackNames = audioTracks.map { Localization.audioTrackName(activity, it) }
AlertDialog.Builder(activity)
.setTitle(R.string.select_audio_track_external_players)
.setNeutralButton(R.string.open_in_browser) { dialog, which ->
ShareUtils.openUrlInBrowser(requireActivity(), url)
}
.setSingleChoiceItems(trackNames, selectedAudioStream, null)
.setSingleChoiceItems(trackNames.toTypedArray(), selectedAudioStream, null)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok) { dialog, which ->
val index = (dialog as AlertDialog).listView.getCheckedItemPosition()
@@ -2079,7 +2046,7 @@ class VideoDetailFragment :
PlayerHolder.stopService()
setInitialData(0, null, "", null)
currentInfo = null
updateOverlayData(null, null, mutableListOf<Image>())
updateOverlayData(null, null, listOf())
}
/*//////////////////////////////////////////////////////////////////////////
@@ -2145,8 +2112,8 @@ class VideoDetailFragment :
val behavior = params.behavior as AppBarLayout.Behavior?
val bottomSheetLayout = activity.findViewById<FrameLayout>(R.id.fragment_player_holder)
bottomSheetBehavior = BottomSheetBehavior.from<FrameLayout?>(bottomSheetLayout)
bottomSheetBehavior.setState(lastStableBottomSheetState)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout)
bottomSheetBehavior.state = lastStableBottomSheetState
updateBottomSheetState(lastStableBottomSheetState)
val peekHeight = resources.getDimensionPixelSize(R.dimen.mini_player_height)
@@ -2154,9 +2121,9 @@ class VideoDetailFragment :
manageSpaceAtTheBottom(false)
bottomSheetBehavior.peekHeight = peekHeight
if (bottomSheetState == BottomSheetBehavior.STATE_COLLAPSED) {
binding.overlayLayout.setAlpha(MAX_OVERLAY_ALPHA)
binding.overlayLayout.alpha = MAX_OVERLAY_ALPHA
} else if (bottomSheetState == BottomSheetBehavior.STATE_EXPANDED) {
binding.overlayLayout.setAlpha(0f)
binding.overlayLayout.alpha = 0f
setOverlayElementsClickable(false)
}
}
@@ -2189,7 +2156,7 @@ class VideoDetailFragment :
!this@VideoDetailFragment.isFullscreen &&
!DeviceUtils.isTablet(activity)
) {
player!!.UIs().get(MainPlayerUi::class)?.toggleFullscreen()
player?.UIs()?.get(MainPlayerUi::class)?.toggleFullscreen()
}
setOverlayLook(binding.appBarLayout, behavior, 1f)
}
@@ -2238,18 +2205,18 @@ class VideoDetailFragment :
private fun updateOverlayPlayQueueButtonVisibility() {
// hide the button if the queue is empty; no player => no play queue :)
nullableBinding?.overlayPlayQueueButton?.isVisible = player?.playQueue?.isEmpty != true
nullableBinding?.overlayPlayQueueButton?.isVisible = player?.playQueue?.isEmpty == false
}
private fun updateOverlayData(
overlayTitle: String?,
uploader: String?,
thumbnails: MutableList<Image>
thumbnails: List<Image>
) {
binding.overlayTitleTextView.text = overlayTitle ?: ""
binding.overlayChannelTextView.text = uploader ?: ""
binding.overlayThumbnail.setImageDrawable(null)
loadDetailsThumbnail(binding.overlayThumbnail, thumbnails)
CoilHelper.loadDetailsThumbnail(binding.overlayThumbnail, thumbnails)
}
private fun setOverlayPlayPauseImage(playerIsPlaying: Boolean) {
@@ -2267,12 +2234,7 @@ class VideoDetailFragment :
if (behavior == null || slideOffset < 0) {
return
}
binding.overlayLayout.setAlpha(
min(
MAX_OVERLAY_ALPHA.toDouble(),
(1 - slideOffset).toDouble()
).toFloat()
)
binding.overlayLayout.alpha = min(MAX_OVERLAY_ALPHA, 1 - slideOffset)
// These numbers are not special. They just do a cool transition
behavior.setTopAndBottomOffset(
(-binding.detailThumbnailImageView.height * 2 * (1 - slideOffset) / 3).toInt()
@@ -2291,10 +2253,6 @@ class VideoDetailFragment :
binding.overlayCloseButton.isClickable = enable
}
fun noPlayerServiceAvailable(): Boolean {
return playerService == null
}
val root: View?
get() = player?.UIs()?.get(VideoPlayerUi::class)?.binding?.root
@@ -2356,6 +2314,6 @@ class VideoDetailFragment :
* Stack that contains the "navigation history".<br></br>
* The peek is the current video.
*/
private var stack = LinkedList<StackItem?>()
private var stack = LinkedList<StackItem>()
}
}

View File

@@ -9,6 +9,7 @@ import android.support.v4.media.MediaDescriptionCompat
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.media.MediaBrowserServiceCompat
import androidx.media.MediaBrowserServiceCompat.Result
import androidx.media.utils.MediaConstants
@@ -180,17 +181,16 @@ class MediaBrowserImpl(
private fun createPlaylistMediaItem(playlist: PlaylistLocalItem): MediaBrowserCompat.MediaItem {
val builder = MediaDescriptionCompat.Builder()
builder
.setMediaId(createMediaIdForInfoItem(playlist is PlaylistRemoteEntity, playlist.uid))
.setTitle(playlist.orderingName)
.setIconUri(imageUriOrNullIfDisabled(playlist.thumbnailUrl))
.setExtras(
bundleOf(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE
to context.resources.getString(R.string.tab_bookmarks)
)
)
val extras = Bundle()
extras.putString(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
context.resources.getString(R.string.tab_bookmarks),
)
builder.setExtras(extras)
return MediaBrowserCompat.MediaItem(
builder.build(),
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE,
@@ -199,7 +199,7 @@ class MediaBrowserImpl(
private fun createInfoItemMediaItem(item: InfoItem): MediaBrowserCompat.MediaItem? {
val builder = MediaDescriptionCompat.Builder()
builder.setMediaId(createMediaIdForInfoItem(item))
.setMediaId(createMediaIdForInfoItem(item))
.setTitle(item.name)
.setIconUri(ImageStrategy.choosePreferredImage(item.thumbnails)?.toUri())
@@ -250,7 +250,7 @@ class MediaBrowserImpl(
index: Int,
): MediaBrowserCompat.MediaItem {
val builder = MediaDescriptionCompat.Builder()
builder.setMediaId(createMediaIdForPlaylistIndex(false, playlistId, index))
.setMediaId(createMediaIdForPlaylistIndex(false, playlistId, index))
.setTitle(item.streamEntity.title)
.setSubtitle(item.streamEntity.uploader)
.setIconUri(imageUriOrNullIfDisabled(item.streamEntity.thumbnailUrl))