diff --git a/app/build.gradle b/app/build.gradle
index 90f219fa8..043de6114 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,6 +17,10 @@ android {
resValue "string", "app_name", "LibreTube"
}
+ buildFeatures {
+ viewBinding true
+ }
+
buildTypes {
release {
minifyEnabled true
diff --git a/app/src/main/java/com/github/libretube/MainActivity.kt b/app/src/main/java/com/github/libretube/MainActivity.kt
index 5c694a275..459bb8d4a 100644
--- a/app/src/main/java/com/github/libretube/MainActivity.kt
+++ b/app/src/main/java/com/github/libretube/MainActivity.kt
@@ -32,6 +32,7 @@ import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.setupWithNavController
+import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.fragments.PlayerFragment
import com.github.libretube.fragments.isFullScreen
import com.github.libretube.services.ClosingService
@@ -46,7 +47,9 @@ import com.google.android.material.color.DynamicColors
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
- lateinit var bottomNavigationView: BottomNavigationView
+ lateinit var binding: ActivityMainBinding
+
+ private lateinit var bottomNavigationView: BottomNavigationView
private lateinit var toolbar: Toolbar
lateinit var navController: NavController
@@ -76,12 +79,12 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
}
} else {
- setContentView(R.layout.activity_main)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
- bottomNavigationView = findViewById(R.id.bottomNav)
navController = findNavController(R.id.fragment)
- bottomNavigationView.setupWithNavController(navController)
+ binding.bottomNav.setupWithNavController(navController)
// hide the trending page if enabled
val hideTrendingPage = PreferenceHelper.getBoolean(this, "hide_trending_page", false)
@@ -94,7 +97,7 @@ class MainActivity : AppCompatActivity() {
"library" -> navController.navigate(R.id.library)
}
- bottomNavigationView.setOnItemSelectedListener {
+ binding.bottomNav.setOnItemSelectedListener {
when (it.itemId) {
R.id.home2 -> {
navController.backQueue.clear()
@@ -112,7 +115,6 @@ class MainActivity : AppCompatActivity() {
false
}
- toolbar = findViewById(R.id.toolbar)
val typedValue = TypedValue()
this.theme.resolveAttribute(R.attr.colorPrimary, typedValue, true)
val hexColor = String.format("#%06X", (0xFFFFFF and typedValue.data))
@@ -120,15 +122,15 @@ class MainActivity : AppCompatActivity() {
"LibreTube",
HtmlCompat.FROM_HTML_MODE_COMPACT
)
- toolbar.title = appName
+ binding.toolbar.title = appName
- toolbar.setNavigationOnClickListener {
+ binding.toolbar.setNavigationOnClickListener {
// settings activity stuff
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
- toolbar.setOnMenuItemClickListener {
+ binding.toolbar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_search -> {
navController.navigate(R.id.searchFragment)
@@ -267,7 +269,7 @@ class MainActivity : AppCompatActivity() {
override fun onBackPressed() {
try {
- val mainMotionLayout = findViewById(R.id.mainMotionLayout)
+ val mainMotionLayout = binding.mainMotionLayout
if (mainMotionLayout.progress == 0.toFloat()) {
mainMotionLayout.transitionToEnd()
findViewById(R.id.main_container).isClickable = false
diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt
index 55b6cbd3b..263352f56 100644
--- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt
+++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt
@@ -22,14 +22,10 @@ import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageButton
-import android.widget.ImageView
import android.widget.LinearLayout
-import android.widget.RelativeLayout
-import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast
import androidx.constraintlayout.motion.widget.MotionLayout
-import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
@@ -37,12 +33,12 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.MainActivity
import com.github.libretube.R
import com.github.libretube.adapters.ChaptersAdapter
import com.github.libretube.adapters.CommentsAdapter
import com.github.libretube.adapters.TrendingAdapter
+import com.github.libretube.databinding.FragmentPlayerBinding
import com.github.libretube.dialogs.AddtoPlaylistDialog
import com.github.libretube.dialogs.DownloadDialog
import com.github.libretube.dialogs.ShareDialog
@@ -102,6 +98,8 @@ var isMiniPlayerVisible = false
class PlayerFragment : Fragment() {
private val TAG = "PlayerFragment"
+ private lateinit var binding: FragmentPlayerBinding
+
private var videoId: String? = null
private var playlistId: String? = null
private var sId: Int = 0
@@ -114,14 +112,11 @@ class PlayerFragment : Fragment() {
private var isSubscribed: Boolean = false
- private lateinit var relatedRecView: RecyclerView
- private lateinit var commentsRecView: RecyclerView
private var commentsAdapter: CommentsAdapter? = null
private var commentsLoaded: Boolean? = false
private var nextPage: String? = null
private var isLoading = true
private lateinit var exoPlayerView: StyledPlayerView
- private lateinit var motionLayout: MotionLayout
private lateinit var exoPlayer: ExoPlayer
private lateinit var segmentData: Segments
private var relatedStreamsEnabled = true
@@ -133,8 +128,6 @@ class PlayerFragment : Fragment() {
private var isPlayerLocked: Boolean = false
- private lateinit var relDownloadVideo: LinearLayout
-
private lateinit var mediaSession: MediaSessionCompat
private lateinit var mediaSessionConnector: MediaSessionConnector
private lateinit var playerNotification: PlayerNotificationManager
@@ -156,9 +149,10 @@ class PlayerFragment : Fragment() {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
+ ): View {
+ binding = FragmentPlayerBinding.inflate(layoutInflater, container, false)
// Inflate the layout for this fragment
- return inflater.inflate(R.layout.fragment_player, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -172,17 +166,14 @@ class PlayerFragment : Fragment() {
}
private fun initializeTransitionLayout(view: View) {
- val playerDescription = view.findViewById(R.id.player_description)
videoId = videoId!!.replace("/watch?v=", "")
- relDownloadVideo = view.findViewById(R.id.relPlayer_download)
- val mainActivity = activity as MainActivity
- mainActivity.findViewById(R.id.container).visibility = View.VISIBLE
- val playerMotionLayout = view.findViewById(R.id.playerMotionLayout)
- motionLayout = playerMotionLayout
- exoPlayerView = view.findViewById(R.id.player)
- view.findViewById(R.id.player_description).text = videoId
- playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
+ val mainActivity = activity as MainActivity
+ mainActivity.binding.container.visibility = View.VISIBLE
+
+ exoPlayerView = binding.player
+
+ binding.playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
@@ -198,7 +189,7 @@ class PlayerFragment : Fragment() {
) {
val mainActivity = activity as MainActivity
val mainMotionLayout =
- mainActivity.findViewById(R.id.mainMotionLayout)
+ mainActivity.binding.mainMotionLayout
mainMotionLayout.progress = abs(progress)
exoPlayerView.hideController()
eId = endId
@@ -209,7 +200,7 @@ class PlayerFragment : Fragment() {
println(currentId)
val mainActivity = activity as MainActivity
val mainMotionLayout =
- mainActivity.findViewById(R.id.mainMotionLayout)
+ mainActivity.binding.mainMotionLayout
if (currentId == eId) {
isMiniPlayerVisible = true
exoPlayerView.useController = false
@@ -222,7 +213,7 @@ class PlayerFragment : Fragment() {
}
override fun onTransitionTrigger(
- motionLayout: MotionLayout?,
+ MotionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float
@@ -230,12 +221,12 @@ class PlayerFragment : Fragment() {
}
})
- playerMotionLayout.progress = 1.toFloat()
- playerMotionLayout.transitionToStart()
+ binding.playerMotionLayout.progress = 1.toFloat()
+ binding.playerMotionLayout.transitionToStart()
- view.findViewById(R.id.close_imageView).setOnClickListener {
+ binding.closeImageView.setOnClickListener {
isMiniPlayerVisible = false
- motionLayout.transitionToEnd()
+ binding.playerMotionLayout.transitionToEnd()
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
mainActivity.supportFragmentManager.beginTransaction()
@@ -244,32 +235,30 @@ class PlayerFragment : Fragment() {
}
view.findViewById(R.id.close_imageButton).setOnClickListener {
isMiniPlayerVisible = false
- motionLayout.transitionToEnd()
+ binding.playerMotionLayout.transitionToEnd()
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
mainActivity.supportFragmentManager.beginTransaction()
.remove(this)
.commit()
}
- val playImageView = view.findViewById(R.id.play_imageView)
- playImageView.setOnClickListener {
+ binding.playImageView.setOnClickListener {
paused = if (paused) {
- playImageView.setImageResource(R.drawable.ic_pause)
+ binding.playImageView.setImageResource(R.drawable.ic_pause)
exoPlayer.play()
false
} else {
- playImageView.setImageResource(R.drawable.ic_play)
+ binding.playImageView.setImageResource(R.drawable.ic_play)
exoPlayer.pause()
true
}
}
// video description and chapters toggle
- val descLinLayout = view.findViewById(R.id.desc_linLayout)
- view.findViewById(R.id.player_title_layout).setOnClickListener {
- val arrowImageView = view.findViewById(R.id.player_description_arrow)
- arrowImageView.animate().rotationBy(180F).setDuration(250).start()
- descLinLayout.visibility = if (descLinLayout.isVisible) View.GONE else View.VISIBLE
+ binding.playerTitleLayout.setOnClickListener {
+ binding.playerDescriptionArrow.animate().rotationBy(180F).setDuration(250).start()
+ binding.descLinLayout.visibility =
+ if (binding.descLinLayout.isVisible) View.GONE else View.VISIBLE
}
view.findViewById(R.id.comments_toggle)
@@ -279,33 +268,31 @@ class PlayerFragment : Fragment() {
val fullScreenButton = view.findViewById(R.id.fullscreen)
val exoTitle = view.findViewById(R.id.exo_title)
- val mainContainer = view.findViewById(R.id.main_container)
- val linLayout = view.findViewById(R.id.linLayout)
// FullScreen button trigger
fullScreenButton.setOnClickListener {
exoPlayerView.hideController()
if (!isFullScreen) {
- with(motionLayout) {
+ with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
enableTransition(R.id.yt_transition, false)
}
- mainContainer.isClickable = true
- linLayout.visibility = View.GONE
+ binding.mainContainer.isClickable = true
+ binding.linLayout.visibility = View.GONE
fullScreenButton.setImageResource(R.drawable.ic_fullscreen_exit)
exoTitle.visibility = View.VISIBLE
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
} else {
- with(motionLayout) {
+ with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition, true)
}
- mainContainer.isClickable = false
- linLayout.visibility = View.VISIBLE
+ binding.mainContainer.isClickable = false
+ binding.linLayout.visibility = View.VISIBLE
fullScreenButton.setImageResource(R.drawable.ic_fullscreen)
exoTitle.visibility = View.INVISIBLE
@@ -343,32 +330,28 @@ class PlayerFragment : Fragment() {
isPlayerLocked = !isPlayerLocked
}
- val scrollView = view.findViewById(R.id.player_scrollView)
- scrollView.viewTreeObserver
+ binding.playerScrollView.viewTreeObserver
.addOnScrollChangedListener {
- if (scrollView.getChildAt(0).bottom
- == (scrollView.height + scrollView.scrollY) &&
+ if (binding.playerScrollView.getChildAt(0).bottom
+ == (binding.playerScrollView.height + binding.playerScrollView.scrollY) &&
nextPage != null
) {
fetchNextComments()
}
}
- commentsRecView = view.findViewById(R.id.comments_recView)
- commentsRecView.layoutManager = LinearLayoutManager(view.context)
+ binding.commentsRecView.layoutManager = LinearLayoutManager(view.context)
+ binding.commentsRecView.setItemViewCacheSize(20)
- commentsRecView.setItemViewCacheSize(20)
-
- relatedRecView = view.findViewById(R.id.player_recView)
- relatedRecView.layoutManager =
+ binding.relatedRecView.layoutManager =
GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items))
}
private fun toggleComments() {
- commentsRecView.visibility =
- if (commentsRecView.isVisible) View.GONE else View.VISIBLE
- relatedRecView.visibility =
- if (relatedRecView.isVisible) View.GONE else View.VISIBLE
+ binding.commentsRecView.visibility =
+ if (binding.commentsRecView.isVisible) View.GONE else View.VISIBLE
+ binding.relatedRecView.visibility =
+ if (binding.relatedRecView.isVisible) View.GONE else View.VISIBLE
if (!commentsLoaded!!) fetchComments()
}
@@ -638,18 +621,17 @@ class PlayerFragment : Fragment() {
}
private fun initializePlayerView(view: View, response: Streams) {
- view.findViewById(R.id.player_views_info).text =
+ binding.playerViewsInfo.text =
context?.getString(R.string.views, response.views.formatShort()) +
" • " + response.uploadDate
- view.findViewById(R.id.textLike).text = response.likes.formatShort()
- view.findViewById(R.id.textDislike).text = response.dislikes.formatShort()
- val channelImage = view.findViewById(R.id.player_channelImage)
- Picasso.get().load(response.uploaderAvatar).into(channelImage)
- view.findViewById(R.id.player_channelName).text = response.uploader
+ binding.textLike.text = response.likes.formatShort()
+ binding.textDislike.text = response.dislikes.formatShort()
+ Picasso.get().load(response.uploaderAvatar).into(binding.playerChannelImage)
+ binding.playerChannelName.text = response.uploader
- view.findViewById(R.id.title_textView).text = response.title
- view.findViewById(R.id.player_title).text = response.title
- view.findViewById(R.id.player_description).text = response.description
+ binding.titleTextView.text = response.title
+ binding.playerTitle.text = response.title
+ binding.playerDescription.text = response.description
view.findViewById(R.id.exo_title).text = response.title
@@ -690,31 +672,28 @@ class PlayerFragment : Fragment() {
if (playWhenReady && playbackState == Player.STATE_READY) {
// media actually playing
transitioning = false
- view.findViewById(R.id.play_imageView)
- .setImageResource(R.drawable.ic_pause)
+ binding.playImageView.setImageResource(R.drawable.ic_pause)
} else if (playWhenReady) {
// might be idle (plays after prepare()),
// buffering (plays when data available)
// or ended (plays when seek away from end)
- view.findViewById(R.id.play_imageView)
- .setImageResource(R.drawable.ic_play)
+ binding.playImageView.setImageResource(R.drawable.ic_play)
} else {
// player paused in any state
- view.findViewById(R.id.play_imageView)
- .setImageResource(R.drawable.ic_play)
+ binding.playImageView.setImageResource(R.drawable.ic_play)
}
}
})
// share button
- view.findViewById(R.id.relPlayer_share).setOnClickListener {
+ binding.relPlayerShare.setOnClickListener {
val shareDialog = ShareDialog(videoId!!, false)
shareDialog.show(childFragmentManager, "ShareDialog")
}
// check if livestream
if (response.duration!! > 0) {
// download clicked
- relDownloadVideo.setOnClickListener {
+ binding.relPlayerDownload.setOnClickListener {
if (!IS_DOWNLOAD_RUNNING) {
val newFragment = DownloadDialog()
val bundle = Bundle()
@@ -732,7 +711,7 @@ class PlayerFragment : Fragment() {
}
if (response.hls != null) {
- view.findViewById(R.id.relPlayer_vlc).setOnClickListener {
+ binding.relPlayerVlc.setOnClickListener {
// start an intent with video as mimetype using the hls stream
val uri: Uri = Uri.parse(response.hls)
val intent = Intent()
@@ -749,14 +728,14 @@ class PlayerFragment : Fragment() {
}
if (relatedStreamsEnabled) {
// only show related streams if enabled
- relatedRecView.adapter = TrendingAdapter(
+ binding.relatedRecView.adapter = TrendingAdapter(
response.relatedStreams!!,
childFragmentManager
)
}
// set video description
val description = response.description!!
- view.findViewById(R.id.player_description).text =
+ binding.playerDescription.text =
// detect whether the description is html formatted
if (description.contains("<") && description.contains(">")) {
if (SDK_INT >= Build.VERSION_CODES.N) {
@@ -769,19 +748,18 @@ class PlayerFragment : Fragment() {
description
}
- view.findViewById(R.id.player_channel).setOnClickListener {
+ binding.playerChannel.setOnClickListener {
val activity = view.context as MainActivity
val bundle = bundleOf("channel_id" to response.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
- activity.findViewById(R.id.mainMotionLayout).transitionToEnd()
- view.findViewById(R.id.playerMotionLayout).transitionToEnd()
+ activity.binding.mainMotionLayout.transitionToEnd()
+ binding.playerMotionLayout.transitionToEnd()
}
val token = PreferenceHelper.getToken(requireContext())
if (token != "") {
val channelId = response.uploaderUrl?.replace("/channel/", "")
- val subButton = view.findViewById(R.id.player_subscribe)
- isSubscribed(subButton, channelId!!)
- view.findViewById(R.id.save).setOnClickListener {
+ isSubscribed(binding.playerSubscribe, channelId!!)
+ binding.save.setOnClickListener {
val newFragment = AddtoPlaylistDialog()
val bundle = Bundle()
bundle.putString("videoId", videoId)
@@ -792,13 +770,11 @@ class PlayerFragment : Fragment() {
}
private fun initializeChapters(chapters: List) {
- val chaptersRecView = view?.findViewById(R.id.chapters_recView)
-
if (chapters.isNotEmpty()) {
- chaptersRecView?.layoutManager =
+ binding.chaptersRecView.layoutManager =
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false)
- chaptersRecView?.adapter = ChaptersAdapter(chapters, exoPlayer)
- chaptersRecView?.visibility = View.VISIBLE
+ binding.chaptersRecView.adapter = ChaptersAdapter(chapters, exoPlayer)
+ binding.chaptersRecView.visibility = View.VISIBLE
}
}
@@ -1143,7 +1119,7 @@ class PlayerFragment : Fragment() {
return@launchWhenCreated
}
commentsAdapter = CommentsAdapter(videoId!!, commentsResponse.comments)
- commentsRecView.adapter = commentsAdapter
+ binding.commentsRecView.adapter = commentsAdapter
nextPage = commentsResponse.nextpage
commentsLoaded = true
isLoading = false
@@ -1176,35 +1152,34 @@ class PlayerFragment : Fragment() {
if (isInPictureInPictureMode) {
exoPlayerView.hideController()
exoPlayerView.useController = false
- with(motionLayout) {
+ with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, -1)
enableTransition(R.id.yt_transition, false)
}
- view?.findViewById(R.id.main_container)?.isClickable = true
- view?.findViewById(R.id.top_bar)?.visibility = View.GONE
+ binding.mainContainer.isClickable = true
+ view?.findViewById(R.id.exo_top_bar)?.visibility = View.GONE
val mainActivity = activity as MainActivity
mainActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
isFullScreen = false
} else {
- with(motionLayout) {
+ with(binding.playerMotionLayout) {
getConstraintSet(R.id.start).constrainHeight(R.id.player, 0)
enableTransition(R.id.yt_transition, true)
}
exoPlayerView.showController()
exoPlayerView.useController = true
- view?.findViewById(R.id.main_container)?.isClickable = false
- view?.findViewById(R.id.top_bar)?.visibility = View.VISIBLE
+ binding.mainContainer.isClickable = false
+ view?.findViewById(R.id.exo_top_bar)?.visibility = View.VISIBLE
}
}
fun onUserLeaveHint() {
val bounds = Rect()
- val scrollView = view?.findViewById(R.id.player_scrollView)
- scrollView?.getHitRect(bounds)
+ binding.playerScrollView.getHitRect(bounds)
if (SDK_INT >= Build.VERSION_CODES.O &&
exoPlayer.isPlaying && (
- scrollView?.getLocalVisibleRect(bounds) == true ||
+ binding.playerScrollView.getLocalVisibleRect(bounds) == true ||
isFullScreen
)
) {
diff --git a/app/src/main/res/layout/exo_styled_player_control_view.xml b/app/src/main/res/layout/exo_styled_player_control_view.xml
index 02d9d5655..d0f2f93f2 100644
--- a/app/src/main/res/layout/exo_styled_player_control_view.xml
+++ b/app/src/main/res/layout/exo_styled_player_control_view.xml
@@ -28,7 +28,7 @@
android:background="@color/exo_black_opacity_60" />