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:
@@ -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>()
|
||||
}
|
||||
}
|
||||
|
@@ -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))
|
||||
|
Reference in New Issue
Block a user