mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 22:30:30 +05:30
Merge branch 'libre-tube:master' into master
This commit is contained in:
commit
332c65e51b
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: ktlint
|
||||
uses: ScaCap/action-ktlint@1.3
|
||||
uses: ScaCap/action-ktlint@1.4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: github-pr-check
|
||||
|
@ -3,6 +3,7 @@ package com.github.libretube
|
||||
import android.content.Context
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.util.DescriptionAdapter
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
@ -10,6 +11,7 @@ import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import gen._base._base_java__rjava_resources.srcjar.R.id.title
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@ -66,13 +68,20 @@ class BackgroundMode {
|
||||
/**
|
||||
* Initializes the [playerNotification] attached to the [player] and shows it.
|
||||
*/
|
||||
private fun initializePlayerNotification(c: Context) {
|
||||
private fun initializePlayerNotification(c: Context, streams: Streams) {
|
||||
playerNotification = PlayerNotificationManager
|
||||
.Builder(c, 1, "background_mode").build()
|
||||
playerNotification.setPlayer(player)
|
||||
playerNotification.setUsePreviousAction(false)
|
||||
playerNotification.setUseNextAction(false)
|
||||
playerNotification.setMediaSessionToken(mediaSession.sessionToken)
|
||||
.Builder(c, 1, "background_mode")
|
||||
// set the description of the notification
|
||||
.setMediaDescriptionAdapter(
|
||||
DescriptionAdapter(streams.title!!, streams.uploader!!, streams.thumbnailUrl!!)
|
||||
)
|
||||
.build()
|
||||
playerNotification.apply {
|
||||
setPlayer(player)
|
||||
setUsePreviousAction(false)
|
||||
setUseNextAction(false)
|
||||
setMediaSessionToken(mediaSession.sessionToken)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,7 +113,7 @@ class BackgroundMode {
|
||||
job.join()
|
||||
|
||||
initializePlayer(c)
|
||||
initializePlayerNotification(c)
|
||||
initializePlayerNotification(c, response!!)
|
||||
|
||||
player?.apply {
|
||||
playWhenReady = playWhenReadyPlayer
|
||||
|
@ -10,6 +10,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
@ -68,6 +69,12 @@ class MainActivity : AppCompatActivity() {
|
||||
sharedPreferences.getBoolean("sponsors_category_key", false)
|
||||
SponsorBlockSettings.outroEnabled =
|
||||
sharedPreferences.getBoolean("outro_category_key", false)
|
||||
SponsorBlockSettings.fillerEnabled =
|
||||
sharedPreferences.getBoolean("filler_category_key", false)
|
||||
SponsorBlockSettings.musicOfftopicEnabled =
|
||||
sharedPreferences.getBoolean("music_offtopic_category_key", false)
|
||||
SponsorBlockSettings.previewEnabled =
|
||||
sharedPreferences.getBoolean("preview_category_key", false)
|
||||
|
||||
ThemeHelper().updateTheme(this)
|
||||
LocaleHelper().updateLanguage(this)
|
||||
@ -199,19 +206,12 @@ class MainActivity : AppCompatActivity() {
|
||||
.replace("/embed/", "")
|
||||
val bundle = Bundle()
|
||||
bundle.putString("videoId", watch)
|
||||
val frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
Handler().postDelayed({
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
motionLayout.transitionToStart()
|
||||
}, 100)
|
||||
// for time stamped links
|
||||
if (data.query != null && data.query?.contains("t=")!!) {
|
||||
val timeStamp = data.query.toString().split("t=")[1]
|
||||
bundle.putLong("timeStamp", timeStamp.toLong())
|
||||
}
|
||||
loadWatch(bundle)
|
||||
} else if (data.path!!.contains("/watch") && data.query != null) {
|
||||
Log.d("dafaq", data.query!!)
|
||||
var watch = data.query!!
|
||||
@ -226,39 +226,41 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", watch.replace("v=", ""))
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
Handler().postDelayed({
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
motionLayout.transitionToStart()
|
||||
}, 100)
|
||||
// for time stamped links
|
||||
if (data.query != null && data.query?.contains("t=")!!) {
|
||||
val timeStamp = data.query.toString().split("t=")[1]
|
||||
bundle.putLong("timeStamp", timeStamp.toLong())
|
||||
}
|
||||
loadWatch(bundle)
|
||||
} else {
|
||||
var watch = data.path!!.replace("/", "")
|
||||
var bundle = Bundle()
|
||||
bundle.putString("videoId", watch)
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
Handler().postDelayed({
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
motionLayout.transitionToStart()
|
||||
}, 100)
|
||||
// for time stamped links
|
||||
if (data.query != null && data.query?.contains("t=")!!) {
|
||||
val timeStamp = data.query.toString().split("t=")[1]
|
||||
bundle.putLong("timeStamp", timeStamp.toLong())
|
||||
}
|
||||
loadWatch(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadWatch(bundle: Bundle) {
|
||||
var frag = PlayerFragment()
|
||||
frag.arguments = bundle
|
||||
supportFragmentManager.beginTransaction()
|
||||
.remove(PlayerFragment())
|
||||
.commit()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
|
||||
motionLayout.transitionToEnd()
|
||||
motionLayout.transitionToStart()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
try {
|
||||
val mainMotionLayout = findViewById<MotionLayout>(R.id.mainMotionLayout)
|
||||
|
@ -8,14 +8,19 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.fragments.PlayerFragment
|
||||
import com.github.libretube.obj.StreamItem
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) :
|
||||
class ChannelAdapter(
|
||||
private val videoFeed: MutableList<StreamItem>,
|
||||
private val childFragmentManager: FragmentManager
|
||||
) :
|
||||
RecyclerView.Adapter<ChannelViewHolder>() {
|
||||
override fun getItemCount(): Int {
|
||||
return videoFeed.size
|
||||
@ -55,6 +60,12 @@ class ChannelAdapter(private val videoFeed: MutableList<StreamItem>) :
|
||||
.replace(R.id.container, frag)
|
||||
.commitNow()
|
||||
}
|
||||
holder.v.setOnLongClickListener {
|
||||
val videoId = trending.url!!.replace("/watch?v=", "")
|
||||
VideoOptionsDialog(videoId, holder.v.context)
|
||||
.show(childFragmentManager, VideoOptionsDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.obj.ChapterSegment
|
||||
import com.google.android.exoplayer2.ExoPlayer
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class ChaptersAdapter(
|
||||
private val chapters: List<ChapterSegment>,
|
||||
private val exoPlayer: ExoPlayer
|
||||
) : RecyclerView.Adapter<ChaptersViewHolder>() {
|
||||
val TAG = "ChaptersAdapter"
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val cell = layoutInflater.inflate(R.layout.chapter_column, parent, false)
|
||||
return ChaptersViewHolder(cell)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChaptersViewHolder, position: Int) {
|
||||
val chapter = chapters[position]
|
||||
val chapterImage = holder.v.findViewById<ImageView>(R.id.chapter_image)
|
||||
Picasso.get().load(chapter.image).fit().centerCrop().into(chapterImage)
|
||||
|
||||
val chapterTitle = holder.v.findViewById<TextView>(R.id.chapter_title)
|
||||
chapterTitle.text = chapter.title
|
||||
|
||||
holder.v.setOnClickListener {
|
||||
val chapterStart = chapter.start!!.toLong() * 1000 // multiply by thousand for ms -> s
|
||||
exoPlayer.seekTo(chapterStart)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return chapters.size
|
||||
}
|
||||
}
|
||||
|
||||
class ChaptersViewHolder(val v: View) : RecyclerView.ViewHolder(v) {
|
||||
init {
|
||||
}
|
||||
}
|
@ -67,7 +67,8 @@ class VideoOptionsDialog(private val videoId: String, context: Context) : Dialog
|
||||
}
|
||||
2 -> {
|
||||
val shareDialog = ShareDialog(videoId)
|
||||
shareDialog.show(childFragmentManager, "ShareDialog")
|
||||
// using parentFragmentManager is important here
|
||||
shareDialog.show(parentFragmentManager, "ShareDialog")
|
||||
}
|
||||
else -> {
|
||||
dialog.dismiss()
|
||||
|
@ -218,7 +218,10 @@ class ChannelFragment : Fragment() {
|
||||
val channelImage = view.findViewById<ImageView>(R.id.channel_image)
|
||||
Picasso.get().load(response.bannerUrl).into(bannerImage)
|
||||
Picasso.get().load(response.avatarUrl).into(channelImage)
|
||||
channelAdapter = ChannelAdapter(response.relatedStreams!!.toMutableList())
|
||||
channelAdapter = ChannelAdapter(
|
||||
response.relatedStreams!!.toMutableList(),
|
||||
childFragmentManager
|
||||
)
|
||||
view.findViewById<RecyclerView>(R.id.channel_recView).adapter = channelAdapter
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package com.github.libretube.fragments
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.graphics.Rect
|
||||
@ -18,9 +17,6 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.view.animation.RotateAnimation
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
@ -43,6 +39,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.IS_DOWNLOAD_RUNNING
|
||||
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.dialogs.AddtoPlaylistDialog
|
||||
@ -57,6 +54,7 @@ import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.SponsorBlockSettings
|
||||
import com.github.libretube.util.CronetHelper
|
||||
import com.github.libretube.util.DescriptionAdapter
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.google.android.exoplayer2.C
|
||||
@ -102,7 +100,7 @@ class PlayerFragment : Fragment() {
|
||||
private var whichQuality = 0
|
||||
private var isZoomed: Boolean = false
|
||||
|
||||
var isSubscribed: Boolean = false
|
||||
private var isSubscribed: Boolean = false
|
||||
|
||||
private lateinit var relatedRecView: RecyclerView
|
||||
private lateinit var commentsRecView: RecyclerView
|
||||
@ -122,6 +120,10 @@ class PlayerFragment : Fragment() {
|
||||
private lateinit var mediaSessionConnector: MediaSessionConnector
|
||||
private lateinit var playerNotification: PlayerNotificationManager
|
||||
|
||||
private lateinit var title: String
|
||||
private lateinit var uploader: String
|
||||
private lateinit var thumbnailUrl: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
@ -142,6 +144,11 @@ class PlayerFragment : Fragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
hideKeyboard()
|
||||
|
||||
initializeTransitionLayout(view)
|
||||
fetchJsonAndInitPlayer(view)
|
||||
}
|
||||
|
||||
private fun initializeTransitionLayout(view: View) {
|
||||
val playerDescription = view.findViewById<TextView>(R.id.player_description)
|
||||
videoId = videoId!!.replace("/watch?v=", "")
|
||||
relDownloadVideo = view.findViewById(R.id.relPlayer_download)
|
||||
@ -208,7 +215,7 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
playerMotionLayout.progress = 1.toFloat()
|
||||
playerMotionLayout.transitionToStart()
|
||||
fetchJson(view)
|
||||
|
||||
view.findViewById<ImageView>(R.id.close_imageView).setOnClickListener {
|
||||
motionLayout.transitionToEnd()
|
||||
val mainActivity = activity as MainActivity
|
||||
@ -239,25 +246,11 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
view.findViewById<RelativeLayout>(R.id.player_title_layout).setOnClickListener {
|
||||
val image = view.findViewById<ImageView>(R.id.player_description_arrow)
|
||||
image.animate().rotationBy(180F).setDuration(100).start()
|
||||
if (playerDescription.isVisible) {
|
||||
val image = view.findViewById<ImageView>(R.id.player_description_arrow)
|
||||
image.clearAnimation()
|
||||
playerDescription.visibility = View.GONE
|
||||
} else {
|
||||
// toggle button
|
||||
val rotate = RotateAnimation(
|
||||
0F,
|
||||
180F,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f
|
||||
)
|
||||
rotate.duration = 100
|
||||
rotate.interpolator = LinearInterpolator()
|
||||
rotate.fillAfter = true
|
||||
val image = view.findViewById<ImageView>(R.id.player_description_arrow)
|
||||
image.startAnimation(rotate)
|
||||
playerDescription.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
@ -319,8 +312,6 @@ class PlayerFragment : Fragment() {
|
||||
commentsRecView.layoutManager = LinearLayoutManager(view.context)
|
||||
|
||||
commentsRecView.setItemViewCacheSize(20)
|
||||
commentsRecView.isDrawingCacheEnabled = true
|
||||
commentsRecView.drawingCacheQuality = View.DRAWING_CACHE_QUALITY_HIGH
|
||||
|
||||
relatedRecView = view.findViewById(R.id.player_recView)
|
||||
relatedRecView.layoutManager =
|
||||
@ -372,7 +363,7 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchJson(view: View) {
|
||||
private fun fetchJsonAndInitPlayer(view: View) {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
@ -387,6 +378,40 @@ class PlayerFragment : Fragment() {
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
// for the notification description adapter
|
||||
title = response.title!!
|
||||
uploader = response.uploader!!
|
||||
thumbnailUrl = response.thumbnailUrl!!
|
||||
|
||||
// check whether related streams are enabled
|
||||
val sharedPreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(requireContext())
|
||||
relatedStreamsEnabled = sharedPreferences.getBoolean("related_streams_toggle", true)
|
||||
runOnUiThread {
|
||||
createExoPlayer(view)
|
||||
prepareExoPlayerView()
|
||||
if (response.chapters != null) initializeChapters(response.chapters)
|
||||
setResolutionAndSubtitles(view, response)
|
||||
// support for time stamped links
|
||||
if (arguments?.getLong("timeStamp") != null) {
|
||||
val position = arguments?.getLong("timeStamp")!! * 1000
|
||||
exoPlayer.seekTo(position)
|
||||
}
|
||||
exoPlayer.prepare()
|
||||
exoPlayer.play()
|
||||
initializePlayerView(view, response)
|
||||
initializePlayerNotification(requireContext())
|
||||
fetchSponsorBlockSegments()
|
||||
if (!relatedStreamsEnabled) toggleComments()
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun fetchSponsorBlockSegments() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
if (SponsorBlockSettings.sponsorBlockEnabled) {
|
||||
val categories: ArrayList<String> = arrayListOf()
|
||||
if (SponsorBlockSettings.introEnabled) {
|
||||
@ -404,6 +429,15 @@ class PlayerFragment : Fragment() {
|
||||
if (SponsorBlockSettings.outroEnabled) {
|
||||
categories.add("outro")
|
||||
}
|
||||
if (SponsorBlockSettings.fillerEnabled) {
|
||||
categories.add("filler")
|
||||
}
|
||||
if (SponsorBlockSettings.musicOfftopicEnabled) {
|
||||
categories.add("music_offtopic")
|
||||
}
|
||||
if (SponsorBlockSettings.previewEnabled) {
|
||||
categories.add("preview")
|
||||
}
|
||||
if (categories.size > 0) {
|
||||
segmentData = try {
|
||||
|
||||
@ -425,20 +459,6 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// check whether related streams are enabled
|
||||
val sharedPreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(requireContext())
|
||||
relatedStreamsEnabled = sharedPreferences.getBoolean("related_streams_toggle", true)
|
||||
runOnUiThread {
|
||||
createExoPlayer(view)
|
||||
prepareExoPlayerView()
|
||||
if (response.chapters != null) initializeChapters(response.chapters)
|
||||
setResolutionAndSubtitles(view, response)
|
||||
exoPlayer.prepare()
|
||||
exoPlayer.play()
|
||||
initializePlayerView(view, response)
|
||||
if (!relatedStreamsEnabled) toggleComments()
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
@ -470,7 +490,7 @@ class PlayerFragment : Fragment() {
|
||||
view.findViewById<TextView>(R.id.player_description).text = response.description
|
||||
|
||||
// Listener for play and pause icon change
|
||||
exoPlayer.addListener(object : com.google.android.exoplayer2.Player.Listener {
|
||||
exoPlayer.addListener(object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) {
|
||||
exoPlayerView.postDelayed(
|
||||
@ -479,7 +499,7 @@ class PlayerFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(message = "Deprecated", level = DeprecationLevel.HIDDEN)
|
||||
override fun onPlayerStateChanged(
|
||||
playWhenReady: Boolean,
|
||||
playbackState: Int
|
||||
@ -520,7 +540,7 @@ class PlayerFragment : Fragment() {
|
||||
relDownloadVideo.setOnClickListener {
|
||||
if (!IS_DOWNLOAD_RUNNING) {
|
||||
val newFragment = DownloadDialog()
|
||||
var bundle = Bundle()
|
||||
val bundle = Bundle()
|
||||
bundle.putString("video_id", videoId)
|
||||
bundle.putParcelable("streams", response)
|
||||
newFragment.arguments = bundle
|
||||
@ -588,7 +608,7 @@ class PlayerFragment : Fragment() {
|
||||
isSubscribed(subButton, channelId!!)
|
||||
view.findViewById<LinearLayout>(R.id.save).setOnClickListener {
|
||||
val newFragment = AddtoPlaylistDialog()
|
||||
var bundle = Bundle()
|
||||
val bundle = Bundle()
|
||||
bundle.putString("videoId", videoId)
|
||||
newFragment.arguments = bundle
|
||||
newFragment.show(childFragmentManager, "AddToPlaylist")
|
||||
@ -597,8 +617,28 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun initializeChapters(chapters: List<ChapterSegment>) {
|
||||
chapters.forEach { chapter ->
|
||||
Log.e(TAG, chapter.title!!)
|
||||
val chaptersToggle = view?.findViewById<LinearLayout>(R.id.chapters_toggle)
|
||||
val chaptersRecView = view?.findViewById<RecyclerView>(R.id.chapters_recView)
|
||||
val chaptersToggleText = view?.findViewById<TextView>(R.id.chapters_toggle_text)
|
||||
val chaptersToggleArrow = view?.findViewById<ImageView>(R.id.chapters_toggle_arrow)
|
||||
|
||||
if (chapters.isNotEmpty()) {
|
||||
chaptersToggle?.visibility = View.VISIBLE
|
||||
|
||||
chaptersToggle?.setOnClickListener {
|
||||
if (chaptersRecView?.isVisible!!) {
|
||||
chaptersRecView?.visibility = View.GONE
|
||||
chaptersToggleText?.text = getString(R.string.show_chapters)
|
||||
} else {
|
||||
chaptersRecView?.visibility = View.VISIBLE
|
||||
chaptersToggleText?.text = getString(R.string.hide_chapters)
|
||||
}
|
||||
chaptersToggleArrow!!.animate().setDuration(100).rotationBy(180F).start()
|
||||
}
|
||||
|
||||
chaptersRecView?.layoutManager =
|
||||
LinearLayoutManager(this.context, LinearLayoutManager.HORIZONTAL, false)
|
||||
chaptersRecView?.adapter = ChaptersAdapter(chapters, exoPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +649,7 @@ class PlayerFragment : Fragment() {
|
||||
val name = vid.quality + " " + vid.format
|
||||
videosNameArray += name
|
||||
}
|
||||
var subtitle = mutableListOf<SubtitleConfiguration>()
|
||||
val subtitle = mutableListOf<SubtitleConfiguration>()
|
||||
if (response.subtitles!!.isNotEmpty()) {
|
||||
subtitle.add(
|
||||
SubtitleConfiguration.Builder(response.subtitles[0].url!!.toUri())
|
||||
@ -719,70 +759,69 @@ class PlayerFragment : Fragment() {
|
||||
val builder: MaterialAlertDialogBuilder? = activity?.let {
|
||||
MaterialAlertDialogBuilder(it)
|
||||
}
|
||||
var lastPosition = exoPlayer.currentPosition
|
||||
val lastPosition = exoPlayer.currentPosition
|
||||
builder!!.setTitle(R.string.choose_quality_dialog)
|
||||
.setItems(
|
||||
videosNameArray,
|
||||
DialogInterface.OnClickListener { _, which ->
|
||||
whichQuality = which
|
||||
if (response.subtitles.isNotEmpty()) {
|
||||
var subtitle =
|
||||
mutableListOf<SubtitleConfiguration>()
|
||||
subtitle.add(
|
||||
SubtitleConfiguration.Builder(
|
||||
response.subtitles[0].url!!.toUri()
|
||||
)
|
||||
.setMimeType(response.subtitles[0].mimeType!!) // The correct MIME type (required).
|
||||
.setLanguage(response.subtitles[0].code) // The subtitle language (optional).
|
||||
.build()
|
||||
videosNameArray
|
||||
) { _, which ->
|
||||
whichQuality = which
|
||||
if (response.subtitles.isNotEmpty()) {
|
||||
val subtitle =
|
||||
mutableListOf<SubtitleConfiguration>()
|
||||
subtitle.add(
|
||||
SubtitleConfiguration.Builder(
|
||||
response.subtitles[0].url!!.toUri()
|
||||
)
|
||||
}
|
||||
if (which == 0) {
|
||||
val mediaItem: MediaItem = MediaItem.Builder()
|
||||
.setUri(response.hls)
|
||||
.setSubtitleConfigurations(subtitle)
|
||||
.setMimeType(response.subtitles[0].mimeType!!) // The correct MIME type (required).
|
||||
.setLanguage(response.subtitles[0].code) // The subtitle language (optional).
|
||||
.build()
|
||||
exoPlayer.setMediaItem(mediaItem)
|
||||
} else {
|
||||
val dataSourceFactory: DataSource.Factory =
|
||||
DefaultHttpDataSource.Factory()
|
||||
val videoItem: MediaItem = MediaItem.Builder()
|
||||
.setUri(response.videoStreams[which - 1].url)
|
||||
.setSubtitleConfigurations(subtitle)
|
||||
.build()
|
||||
val videoSource: MediaSource =
|
||||
DefaultMediaSourceFactory(dataSourceFactory)
|
||||
.createMediaSource(videoItem)
|
||||
var audioSource: MediaSource =
|
||||
DefaultMediaSourceFactory(dataSourceFactory)
|
||||
.createMediaSource(
|
||||
fromUri(response.audioStreams!![0].url!!)
|
||||
)
|
||||
if (response.videoStreams[which - 1].quality == "720p" ||
|
||||
response.videoStreams[which - 1].quality == "1080p" ||
|
||||
response.videoStreams[which - 1].quality == "480p"
|
||||
) {
|
||||
audioSource =
|
||||
ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(
|
||||
fromUri(
|
||||
response.audioStreams[
|
||||
getMostBitRate(
|
||||
response.audioStreams
|
||||
)
|
||||
].url!!
|
||||
)
|
||||
)
|
||||
}
|
||||
val mergeSource: MediaSource =
|
||||
MergingMediaSource(videoSource, audioSource)
|
||||
exoPlayer.setMediaSource(mergeSource)
|
||||
}
|
||||
exoPlayer.seekTo(lastPosition)
|
||||
view.findViewById<TextView>(R.id.quality_text).text =
|
||||
videosNameArray[which]
|
||||
)
|
||||
}
|
||||
)
|
||||
if (which == 0) {
|
||||
val mediaItem: MediaItem = MediaItem.Builder()
|
||||
.setUri(response.hls)
|
||||
.setSubtitleConfigurations(subtitle)
|
||||
.build()
|
||||
exoPlayer.setMediaItem(mediaItem)
|
||||
} else {
|
||||
val dataSourceFactory: DataSource.Factory =
|
||||
DefaultHttpDataSource.Factory()
|
||||
val videoItem: MediaItem = MediaItem.Builder()
|
||||
.setUri(response.videoStreams[which - 1].url)
|
||||
.setSubtitleConfigurations(subtitle)
|
||||
.build()
|
||||
val videoSource: MediaSource =
|
||||
DefaultMediaSourceFactory(dataSourceFactory)
|
||||
.createMediaSource(videoItem)
|
||||
var audioSource: MediaSource =
|
||||
DefaultMediaSourceFactory(dataSourceFactory)
|
||||
.createMediaSource(
|
||||
fromUri(response.audioStreams!![0].url!!)
|
||||
)
|
||||
if (response.videoStreams[which - 1].quality == "720p" ||
|
||||
response.videoStreams[which - 1].quality == "1080p" ||
|
||||
response.videoStreams[which - 1].quality == "480p"
|
||||
) {
|
||||
audioSource =
|
||||
ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(
|
||||
fromUri(
|
||||
response.audioStreams[
|
||||
getMostBitRate(
|
||||
response.audioStreams
|
||||
)
|
||||
].url!!
|
||||
)
|
||||
)
|
||||
}
|
||||
val mergeSource: MediaSource =
|
||||
MergingMediaSource(videoSource, audioSource)
|
||||
exoPlayer.setMediaSource(mergeSource)
|
||||
}
|
||||
exoPlayer.seekTo(lastPosition)
|
||||
view.findViewById<TextView>(R.id.quality_text).text =
|
||||
videosNameArray[which]
|
||||
}
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
}
|
||||
@ -822,8 +861,6 @@ class PlayerFragment : Fragment() {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val playbackSpeed = sharedPreferences.getString("playback_speed", "1F")?.toFloat()
|
||||
exoPlayer.setPlaybackSpeed(playbackSpeed!!)
|
||||
|
||||
initializePlayerNotification(requireContext())
|
||||
}
|
||||
|
||||
private fun initializePlayerNotification(c: Context) {
|
||||
@ -838,6 +875,9 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
playerNotification = PlayerNotificationManager
|
||||
.Builder(c, 1, "background_mode")
|
||||
.setMediaDescriptionAdapter(
|
||||
DescriptionAdapter(title, uploader, thumbnailUrl)
|
||||
)
|
||||
.build()
|
||||
|
||||
playerNotification.apply {
|
||||
|
@ -198,7 +198,6 @@ class SearchFragment : Fragment() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
isFetchingSearch = true
|
||||
hideKeyboard()
|
||||
Log.e("here", "here")
|
||||
val response = try {
|
||||
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
|
||||
} catch (e: IOException) {
|
||||
|
@ -29,40 +29,42 @@ class AboutFragment : Fragment() {
|
||||
val topBarText = activity?.findViewById<TextView>(R.id.topBar_textView)
|
||||
topBarText?.text = getString(R.string.about)
|
||||
|
||||
val appVersion = view?.findViewById<TextView>(R.id.app_version)
|
||||
val appVersion = view.findViewById<TextView>(R.id.app_version)
|
||||
appVersion.text = BuildConfig.VERSION_NAME
|
||||
|
||||
val website = view?.findViewById<LinearLayout>(R.id.website)
|
||||
website?.setOnClickListener {
|
||||
val website = view.findViewById<LinearLayout>(R.id.website)
|
||||
website.setOnClickListener {
|
||||
openLinkFromHref("https://libre-tube.github.io/")
|
||||
}
|
||||
val authors = view?.findViewById<LinearLayout>(R.id.authors)
|
||||
authors?.setOnClickListener {
|
||||
val authors = view.findViewById<LinearLayout>(R.id.authors)
|
||||
authors.setOnClickListener {
|
||||
openLinkFromHref("https://github.com/libre-tube/LibreTube/graphs/contributors")
|
||||
}
|
||||
val donate = view?.findViewById<LinearLayout>(R.id.donate)
|
||||
donate?.setOnClickListener {
|
||||
val donate = view.findViewById<LinearLayout>(R.id.donate)
|
||||
donate.setOnClickListener {
|
||||
openLinkFromHref("https://libre-tube.github.io/#donate")
|
||||
}
|
||||
val contributing = view?.findViewById<LinearLayout>(R.id.contributing)
|
||||
contributing?.setOnClickListener {
|
||||
val contributing = view.findViewById<LinearLayout>(R.id.contributing)
|
||||
contributing.setOnClickListener {
|
||||
openLinkFromHref("https://github.com/libre-tube/LibreTube")
|
||||
}
|
||||
val license = view.findViewById<LinearLayout>(R.id.license)
|
||||
license?.setOnClickListener {
|
||||
val licenseString = view?.context?.assets!!
|
||||
license.setOnClickListener {
|
||||
val licenseString = view.context.assets
|
||||
.open("gpl3.html").bufferedReader().use {
|
||||
it.readText()
|
||||
}
|
||||
val licenseHtml = if (Build.VERSION.SDK_INT >= 24) Html.fromHtml(licenseString, 1)
|
||||
else Html.fromHtml(licenseString)
|
||||
val licenseHtml = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Html.fromHtml(licenseString, 1)
|
||||
} else {
|
||||
Html.fromHtml(licenseString)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(view?.context!!)
|
||||
MaterialAlertDialogBuilder(view.context!!)
|
||||
.setPositiveButton(getString(R.string.okay)) { _, _ -> }
|
||||
.setMessage(licenseHtml)
|
||||
.create()
|
||||
.show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.ActivityCompat.recreate
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@ -30,7 +29,6 @@ import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import org.chromium.base.ThreadUtils.runOnUiThread
|
||||
import org.json.JSONObject
|
||||
import org.json.JSONTokener
|
||||
import retrofit2.HttpException
|
||||
|
@ -17,6 +17,9 @@ class SponsorBlockSettings : PreferenceFragmentCompat() {
|
||||
var interactionEnabled: Boolean = false
|
||||
var introEnabled: Boolean = false
|
||||
var outroEnabled: Boolean = false
|
||||
var fillerEnabled: Boolean = false
|
||||
var musicOfftopicEnabled: Boolean = false
|
||||
var previewEnabled: Boolean = false
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
@ -65,5 +68,23 @@ class SponsorBlockSettings : PreferenceFragmentCompat() {
|
||||
outroEnabled = newValue as Boolean
|
||||
true
|
||||
}
|
||||
|
||||
val fillerToggle = findPreference<SwitchPreferenceCompat>("filler_category_key")
|
||||
fillerToggle?.setOnPreferenceChangeListener { _, newValue ->
|
||||
fillerEnabled = newValue as Boolean
|
||||
true
|
||||
}
|
||||
|
||||
val musicToggle = findPreference<SwitchPreferenceCompat>("music_offtopic_category_key")
|
||||
musicToggle?.setOnPreferenceChangeListener { _, newValue ->
|
||||
musicOfftopicEnabled = newValue as Boolean
|
||||
true
|
||||
}
|
||||
|
||||
val previewToggle = findPreference<SwitchPreferenceCompat>("preview_category_key")
|
||||
previewToggle?.setOnPreferenceChangeListener { _, newValue ->
|
||||
previewEnabled = newValue as Boolean
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import com.google.android.exoplayer2.Player
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import java.net.URL
|
||||
|
||||
// used to show title and thumbnail of the video in the notification
|
||||
class DescriptionAdapter(
|
||||
private val title: String,
|
||||
private val channelName: String,
|
||||
private val thumbnailUrl: String
|
||||
) :
|
||||
PlayerNotificationManager.MediaDescriptionAdapter {
|
||||
override fun getCurrentContentTitle(player: Player): CharSequence {
|
||||
// return controller.metadata.description.title.toString()
|
||||
return title
|
||||
}
|
||||
|
||||
override fun createCurrentContentIntent(player: Player): PendingIntent? {
|
||||
// return controller.sessionActivity
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getCurrentContentText(player: Player): CharSequence? {
|
||||
// return controller.metadata.description.subtitle.toString()
|
||||
return channelName
|
||||
}
|
||||
|
||||
override fun getCurrentLargeIcon(
|
||||
player: Player,
|
||||
callback: PlayerNotificationManager.BitmapCallback
|
||||
): Bitmap? {
|
||||
lateinit var bitmap: Bitmap
|
||||
val thread = Thread {
|
||||
try {
|
||||
// try to parse the thumbnailUrl to a Bitmap
|
||||
val inputStream = URL(thumbnailUrl).openStream()
|
||||
bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
} catch (ex: java.lang.Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
thread.start()
|
||||
thread.join()
|
||||
// return bitmap if initialized
|
||||
return try {
|
||||
bitmap
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@
|
||||
android:height="144dp"
|
||||
android:viewportWidth="143"
|
||||
android:viewportHeight="144"
|
||||
android:tint="?android:attr/colorControlNormal" >
|
||||
<path
|
||||
android:pathData="M35.74,87.66L35.74,41.39c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h45.95v5.4L41.11,41.39L41.11,87.66ZM46.47,98.47L46.47,52.2c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h45.95v5.4L51.83,52.2L51.83,98.47ZM101.89,108.01L62.56,108.01c-1.43,0 -2.68,-0.54 -3.75,-1.62 -1.07,-1.08 -1.61,-2.34 -1.61,-3.78L57.2,63c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h39.33c1.43,0 2.68,0.54 3.75,1.62 1.07,1.08 1.61,2.34 1.61,3.78v39.61c0,1.44 -0.54,2.7 -1.61,3.78 -1.07,1.08 -2.32,1.62 -3.75,1.62zM101.89,102.61L101.89,63L62.56,63L62.56,102.61ZM62.56,63v39.61z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#120807"/>
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M35.74,87.66L35.74,41.39c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h45.95v5.4L41.11,41.39L41.11,87.66ZM46.47,98.47L46.47,52.2c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h45.95v5.4L51.83,52.2L51.83,98.47ZM101.89,108.01L62.56,108.01c-1.43,0 -2.68,-0.54 -3.75,-1.62 -1.07,-1.08 -1.61,-2.34 -1.61,-3.78L57.2,63c0,-1.44 0.54,-2.7 1.61,-3.78 1.07,-1.08 2.32,-1.62 3.75,-1.62h39.33c1.43,0 2.68,0.54 3.75,1.62 1.07,1.08 1.61,2.34 1.61,3.78v39.61c0,1.44 -0.54,2.7 -1.61,3.78 -1.07,1.08 -2.32,1.62 -3.75,1.62zM101.89,102.61L101.89,63L62.56,63L62.56,102.61ZM62.56,63v39.61z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#120807" />
|
||||
</vector>
|
||||
|
24
app/src/main/res/layout/chapter_column.xml
Normal file
24
app/src/main/res/layout/chapter_column.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chapter_image"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="45dp"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chapter_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:text="Title"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
@ -9,105 +9,106 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<LinearLayout
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/commentor_image"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:srcCompat="@mipmap/ic_launcher" />
|
||||
|
||||
<LinearLayout
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/commentor_image"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:srcCompat="@mipmap/ic_launcher" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/comment_infos"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="Author and Time"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verified_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
app:srcCompat="@drawable/ic_verified"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pinned_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
app:srcCompat="@drawable/ic_pinned"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/comment_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:autoLink="web"
|
||||
android:text="Comment Text" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/likes_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:layout_marginTop="2dp"
|
||||
app:srcCompat="@drawable/ic_thumb_up" />
|
||||
<TextView
|
||||
android:id="@+id/comment_infos"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="Author and Time"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verified_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
app:srcCompat="@drawable/ic_verified"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pinned_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
app:srcCompat="@drawable/ic_pinned"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/likes_textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/comment_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="LikeCount" />
|
||||
android:layout_marginBottom="4dp"
|
||||
android:autoLink="web"
|
||||
android:text="Comment Text" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hearted_imageView"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="3dp"
|
||||
app:srcCompat="@drawable/ic_hearted"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/likes_imageView"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:layout_marginTop="2dp"
|
||||
app:srcCompat="@drawable/ic_thumb_up" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/likes_textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="LikeCount" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hearted_imageView"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="3dp"
|
||||
app:srcCompat="@drawable/ic_hearted"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/replies_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/replies_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:nestedScrollingEnabled="false" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -39,7 +39,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp" android:paddingVertical="10dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -59,7 +60,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp" android:paddingVertical="10dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -79,7 +81,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp" android:paddingVertical="10dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -99,7 +102,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp" android:paddingVertical="10dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
@ -119,7 +123,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp" android:paddingVertical="10dp">
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
|
@ -26,15 +26,16 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/player_title_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/player_title_layout"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
@ -54,14 +55,54 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_views_info"
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_views_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="10M views 2 days ago " />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/chapters_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chapters_toggle_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/show_chapters" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/chapters_toggle_arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:src="@drawable/ic_arrow_down" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/chapters_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comments_toggle"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:text="10M views 2 days ago " />
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_description"
|
||||
@ -146,10 +187,10 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:gravity="center"
|
||||
android:text="@string/download"
|
||||
android:maxLines="1"
|
||||
android:autoSizeTextType="uniform" />
|
||||
android:text="@string/download" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@ -203,8 +244,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="8dp"
|
||||
@ -294,10 +335,10 @@
|
||||
android:id="@+id/comments_recView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_below="@id/comments_toggle"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
|
@ -100,6 +100,12 @@
|
||||
<string name="category_intro_description">An interval without actual content. Could be a pause, static frame, repeating animation. Should not be used for transitions containing info.</string>
|
||||
<string name="category_outro">End cards and credits</string>
|
||||
<string name="category_outro_description">Info following the ending. Not for conclusions with info.</string>
|
||||
<string name="category_filler">Filler Tangent/Jokes</string>
|
||||
<string name="category_filler_description">This is for tangential scenes added only for filler or humor that are not required to understand the main content of the video.</string>
|
||||
<string name="category_music_offtopic">Music: Non-Music Section</string>
|
||||
<string name="category_music_offtopic_description">This is only for use in music videos. It should cover parts of the video not part of official mixes. In the end, the video should resemble the Spotify or any other mix version as closely as possible, or should reduce talking or other distractions.</string>
|
||||
<string name="category_preview">Preview/Recap</string>
|
||||
<string name="category_preview_description">For segments that show what is coming up in this or future videos of the same series, but do not provide additional information. If it includes clips that only appear here, this is very likely not the right category.</string>
|
||||
<string name="license">License</string>
|
||||
<string name="color_accent">Accents</string>
|
||||
<string name="color_red">Resting red</string>
|
||||
@ -173,4 +179,6 @@
|
||||
<string name="about_summary">Get to know team LibreTube and how it all happens.</string>
|
||||
<string name="related_streams">Related streams</string>
|
||||
<string name="related_streams_summary">Show related streams to videos.</string>
|
||||
<string name="show_chapters">Show chapters</string>
|
||||
<string name="hide_chapters">Hide chapters</string>
|
||||
</resources>
|
||||
|
@ -5,21 +5,21 @@
|
||||
<PreferenceCategory app:title="@string/player">
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/defres"
|
||||
app:key="default_res"
|
||||
android:icon="@drawable/ic_hd"
|
||||
app:defaultValue=""
|
||||
app:entries="@array/defres"
|
||||
app:entryValues="@array/defresValue"
|
||||
app:defaultValue=""
|
||||
android:icon="@drawable/ic_hd"
|
||||
app:key="default_res"
|
||||
app:title="@string/defres"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/playback_speed"
|
||||
app:key="playback_speed"
|
||||
android:icon="@drawable/ic_play"
|
||||
app:defaultValue="1F"
|
||||
app:entries="@array/playbackSpeed"
|
||||
app:entryValues="@array/playbackSpeedValues"
|
||||
app:defaultValue="1F"
|
||||
android:icon="@drawable/ic_play"
|
||||
app:key="playback_speed"
|
||||
app:title="@string/playback_speed"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
@ -27,55 +27,44 @@
|
||||
<PreferenceCategory app:title="@string/downloads">
|
||||
|
||||
<ListPreference
|
||||
app:key="video_format"
|
||||
app:title="@string/video_format"
|
||||
app:defaultValue=".mp4"
|
||||
app:entries="@array/videoFormats"
|
||||
app:entryValues="@array/videoFormatsValues"
|
||||
app:defaultValue=".mp4"
|
||||
app:icon="@drawable/ic_videocam"
|
||||
app:key="video_format"
|
||||
app:summary="@string/video_format_summary"
|
||||
app:icon="@drawable/ic_videocam" />
|
||||
app:title="@string/video_format" />
|
||||
|
||||
<ListPreference
|
||||
app:key="download_location"
|
||||
app:title="@string/download_directory"
|
||||
app:summary="@string/download_directory_summary"
|
||||
android:defaultValue="downloads"
|
||||
android:entries="@array/downloadLocation"
|
||||
android:entryValues="@array/downloadLocationValues"
|
||||
app:icon="@drawable/ic_download" />
|
||||
app:icon="@drawable/ic_download"
|
||||
app:key="download_location"
|
||||
app:summary="@string/download_directory_summary"
|
||||
app:title="@string/download_directory" />
|
||||
|
||||
<EditTextPreference
|
||||
app:key="download_folder"
|
||||
app:title="@string/download_folder"
|
||||
app:summary="@string/download_folder_summary"
|
||||
android:defaultValue="LibreTube"
|
||||
app:icon="@drawable/ic_folder"
|
||||
android:defaultValue="LibreTube" />
|
||||
app:key="download_folder"
|
||||
app:summary="@string/download_folder_summary"
|
||||
app:title="@string/download_folder" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/search_history">
|
||||
|
||||
<SwitchPreference
|
||||
app:title="@string/search_history"
|
||||
app:key="search_history_toggle"
|
||||
android:defaultValue="true"
|
||||
android:icon="@drawable/ic_history" />
|
||||
android:icon="@drawable/ic_history"
|
||||
app:key="search_history_toggle"
|
||||
app:title="@string/search_history" />
|
||||
|
||||
<Preference
|
||||
app:title="@string/clear_history"
|
||||
android:icon="@drawable/ic_trash"
|
||||
app:key="clear_history"
|
||||
android:icon="@drawable/ic_trash" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="related_streams_toggle"
|
||||
app:title="@string/related_streams"
|
||||
app:summary="@string/related_streams_summary"
|
||||
android:defaultValue="true"
|
||||
android:icon="@drawable/ic_list"/>
|
||||
app:title="@string/clear_history" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
@ -5,30 +5,30 @@
|
||||
<PreferenceCategory app:title="@string/appearance">
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/app_theme"
|
||||
app:key="theme_togglee"
|
||||
android:icon="@drawable/ic_theme"
|
||||
app:defaultValue="A"
|
||||
app:entries="@array/themes"
|
||||
app:entryValues="@array/themesValue"
|
||||
app:defaultValue="A"
|
||||
android:icon="@drawable/ic_theme"
|
||||
app:key="theme_togglee"
|
||||
app:title="@string/app_theme"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/color_accent"
|
||||
app:key="accent_color"
|
||||
android:icon="@drawable/ic_color"
|
||||
app:defaultValue="red"
|
||||
app:entries="@array/accents"
|
||||
app:entryValues="@array/accentsValue"
|
||||
app:defaultValue="red"
|
||||
android:icon="@drawable/ic_color"
|
||||
app:key="accent_color"
|
||||
app:title="@string/color_accent"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/app_icon"
|
||||
app:key="icon_change"
|
||||
android:icon="@drawable/ic_frame"
|
||||
app:defaultValue="MainActivity"
|
||||
app:entries="@array/icons"
|
||||
app:entryValues="@array/iconsValue"
|
||||
app:defaultValue="MainActivity"
|
||||
android:icon="@drawable/ic_frame"
|
||||
app:key="icon_change"
|
||||
app:title="@string/app_icon"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
@ -36,23 +36,34 @@
|
||||
<PreferenceCategory app:title="@string/app_behavior">
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/defaultTab"
|
||||
app:key="default_tab"
|
||||
android:icon="@drawable/ic_home"
|
||||
app:defaultValue="home"
|
||||
app:entries="@array/tabs"
|
||||
app:entryValues="@array/tabsValue"
|
||||
app:defaultValue="home"
|
||||
android:icon="@drawable/ic_home"
|
||||
app:key="default_tab"
|
||||
app:title="@string/defaultTab"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:title="@string/grid"
|
||||
app:key="grid"
|
||||
android:icon="@drawable/ic_grid"
|
||||
app:defaultValue="@integer/grid_items"
|
||||
app:entries="@array/grid"
|
||||
app:entryValues="@array/grid"
|
||||
app:defaultValue="@integer/grid_items"
|
||||
android:icon="@drawable/ic_grid"
|
||||
app:key="grid"
|
||||
app:title="@string/grid"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory>
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:icon="@drawable/ic_list"
|
||||
app:key="related_streams_toggle"
|
||||
app:summary="@string/related_streams_summary"
|
||||
app:title="@string/related_streams" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
@ -41,6 +41,21 @@
|
||||
app:title="@string/category_outro"
|
||||
app:summary="@string/category_outro_description" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="filler_category_key"
|
||||
app:title="@string/category_filler"
|
||||
app:summary="@string/category_filler_description" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="music_offtopic_category_key"
|
||||
app:title="@string/category_music_offtopic"
|
||||
app:summary="@string/category_music_offtopic_description" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="preview_category_key"
|
||||
app:title="@string/category_preview"
|
||||
app:summary="@string/category_preview_description" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@ -6,7 +6,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
Loading…
Reference in New Issue
Block a user