Merge branch 'libre-tube:master' into master

This commit is contained in:
ձռօռყ_սռĸռօառ 2022-06-14 20:28:39 +05:30 committed by GitHub
commit 332c65e51b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 619 additions and 337 deletions

View File

@ -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

View File

@ -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

View File

@ -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,23 +226,26 @@ 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)
// 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()
@ -251,13 +254,12 @@ class MainActivity : AppCompatActivity() {
supportFragmentManager.beginTransaction()
.replace(R.id.container, frag)
.commitNow()
Handler().postDelayed({
Handler(Looper.getMainLooper()).postDelayed({
val motionLayout = findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout.transitionToEnd()
motionLayout.transitionToStart()
}, 100)
}
}
override fun onBackPressed() {
try {

View File

@ -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
}
}
}

View File

@ -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 {
}
}

View File

@ -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()

View File

@ -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
}
}

View File

@ -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 {
if (playerDescription.isVisible) {
val image = view.findViewById<ImageView>(R.id.player_description_arrow)
image.clearAnimation()
image.animate().rotationBy(180F).setDuration(100).start()
if (playerDescription.isVisible) {
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,14 +759,14 @@ 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 ->
videosNameArray
) { _, which ->
whichQuality = which
if (response.subtitles.isNotEmpty()) {
var subtitle =
val subtitle =
mutableListOf<SubtitleConfiguration>()
subtitle.add(
SubtitleConfiguration.Builder(
@ -782,7 +822,6 @@ class PlayerFragment : Fragment() {
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 {

View File

@ -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) {

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -3,9 +3,9 @@
android:height="144dp"
android:viewportWidth="143"
android:viewportHeight="144"
android:tint="?android:attr/colorControlNormal" >
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:fillColor="#120807" />
</vector>

View 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>

View File

@ -9,6 +9,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View File

@ -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"

View File

@ -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,15 +55,55 @@
</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:layout_marginRight="10dp"
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:nestedScrollingEnabled="false"
android:visibility="gone" />
<TextView
android:id="@+id/player_description"
android:layout_width="match_parent"
@ -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" />

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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