mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 13:50:30 +05:30
chore: reformat code using ktlint
This commit is contained in:
parent
5532301aa4
commit
70bfaddb2e
@ -1,7 +1,6 @@
|
||||
package com.github.libretube.api
|
||||
|
||||
import com.github.libretube.api.obj.*
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
|
@ -187,7 +187,9 @@ object PlaylistsHelper {
|
||||
} else {
|
||||
// if not logged in, all video information needs to become fetched manually
|
||||
// Only do so with `MAX_CONCURRENT_IMPORT_CALLS` videos at once to prevent performance issues
|
||||
val streams = playlist.videos.chunked(MAX_CONCURRENT_IMPORT_CALLS).map { videos ->
|
||||
val streams = playlist.videos.chunked(
|
||||
MAX_CONCURRENT_IMPORT_CALLS
|
||||
).map { videos ->
|
||||
videos.parallelMap {
|
||||
runCatching { RetrofitInstance.api.getStreams(it) }
|
||||
.getOrNull()
|
||||
|
@ -20,5 +20,5 @@ data class PipedInstance(
|
||||
@SerialName("uptime_24h") val uptimeToday: Float? = null,
|
||||
@SerialName("uptime_7d") val uptimeWeek: Float? = null,
|
||||
@SerialName("uptime_30d") val uptimeMonth: Float? = null,
|
||||
val isCurrentlyDown: Boolean = false,
|
||||
val isCurrentlyDown: Boolean = false
|
||||
)
|
||||
|
@ -10,7 +10,9 @@ import kotlinx.serialization.Serializable
|
||||
@Entity(tableName = "localSubscription")
|
||||
data class LocalSubscription(
|
||||
@PrimaryKey val channelId: String,
|
||||
@Ignore val url: String = "",
|
||||
@Ignore val url: String = ""
|
||||
) {
|
||||
constructor(channelId: String): this(channelId, "${ShareDialog.YOUTUBE_FRONTEND_URL}/channel/$channelId")
|
||||
constructor(
|
||||
channelId: String
|
||||
) : this(channelId, "${ShareDialog.YOUTUBE_FRONTEND_URL}/channel/$channelId")
|
||||
}
|
||||
|
@ -4,4 +4,4 @@ enum class NotificationId(val id: Int) {
|
||||
PLAYER_PLAYBACK(1),
|
||||
DOWNLOAD_IN_PROGRESS(2),
|
||||
ENQUEUE_PLAYLIST_DOWNLOAD(3)
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,4 @@ import androidx.lifecycle.MutableLiveData
|
||||
|
||||
fun <T> MutableLiveData<T>.updateIfChanged(newValue: T) {
|
||||
if (value != newValue) value = newValue
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
suspend fun <T> runSafely(
|
||||
onSuccess: (List<T>) -> Unit = { },
|
||||
ioBlock: suspend () -> List<T>,
|
||||
ioBlock: suspend () -> List<T>
|
||||
) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val result = runCatching { ioBlock.invoke() }
|
||||
@ -18,4 +18,4 @@ suspend fun <T> runSafely(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import android.widget.ImageView
|
||||
import androidx.core.graphics.drawable.toBitmapOrNull
|
||||
import coil.ImageLoader
|
||||
import coil.disk.DiskCache
|
||||
import coil.load
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.github.libretube.api.CronetHelper
|
||||
@ -17,10 +16,10 @@ import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.extensions.toAndroidUri
|
||||
import com.github.libretube.extensions.toAndroidUriOrNull
|
||||
import com.github.libretube.util.DataSaverMode
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
object ImageHelper {
|
||||
lateinit var imageLoader: ImageLoader
|
||||
|
@ -116,7 +116,11 @@ object ImportHelper {
|
||||
|
||||
ImportFormat.FREETUBE -> {
|
||||
val freeTubeChannels = subs.map {
|
||||
FreetubeSubscription(it.name, "", "${ShareDialog.YOUTUBE_FRONTEND_URL}${it.url}")
|
||||
FreetubeSubscription(
|
||||
it.name,
|
||||
"",
|
||||
"${ShareDialog.YOUTUBE_FRONTEND_URL}${it.url}"
|
||||
)
|
||||
}
|
||||
val freeTubeSubscriptions = FreetubeSubscriptions(subscriptions = freeTubeChannels)
|
||||
activity.contentResolver.openOutputStream(uri)?.use {
|
||||
|
@ -139,5 +139,4 @@ object NavBarHelper {
|
||||
.getString(PreferenceKeys.NAVBAR_ITEMS, "")
|
||||
.split(SEPARATOR)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,7 +73,8 @@ object NavigationHelper {
|
||||
return
|
||||
}
|
||||
|
||||
val playerData = PlayerData(videoUrlOrId.toID(), playlistId, channelId, keepQueue, timestamp)
|
||||
val playerData =
|
||||
PlayerData(videoUrlOrId.toID(), playlistId, channelId, keepQueue, timestamp)
|
||||
val bundle = bundleOf(IntentData.playerData to playerData)
|
||||
|
||||
val activity = ContextHelper.unwrapActivity(context)
|
||||
@ -86,7 +87,9 @@ object NavigationHelper {
|
||||
if (playlistUrlOrId == null) return
|
||||
|
||||
val activity = ContextHelper.unwrapActivity(context)
|
||||
activity.navController.navigate(NavDirections.openPlaylist(playlistUrlOrId.toID(), playlistType))
|
||||
activity.navController.navigate(
|
||||
NavDirections.openPlaylist(playlistUrlOrId.toID(), playlistType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,8 +18,8 @@ object NetworkHelper {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val activeNetwork = connectivityManager.activeNetwork
|
||||
val caps = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
|
||||
return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
|| caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
|
||||
return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ||
|
||||
caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
|
||||
} else {
|
||||
// activeNetworkInfo might return null instead of the VPN, so better check it explicitly
|
||||
val networkInfo = connectivityManager.activeNetworkInfo
|
||||
|
@ -42,14 +42,14 @@ import com.github.libretube.enums.SbSkipOptions
|
||||
import com.github.libretube.extensions.updateParameters
|
||||
import com.github.libretube.obj.VideoStats
|
||||
import com.github.libretube.util.TextUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
object PlayerHelper {
|
||||
private const val ACTION_MEDIA_CONTROL = "media_control"
|
||||
@ -344,11 +344,11 @@ object PlayerHelper {
|
||||
|
||||
fun shouldPlayNextVideo(isPlaylist: Boolean = false): Boolean {
|
||||
return autoPlayEnabled || (
|
||||
isPlaylist && PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.AUTOPLAY_PLAYLISTS,
|
||||
false
|
||||
)
|
||||
)
|
||||
isPlaylist && PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.AUTOPLAY_PLAYLISTS,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val handleAudioFocus
|
||||
@ -760,9 +760,9 @@ object PlayerHelper {
|
||||
*/
|
||||
fun haveAudioTrackRoleFlagSet(@C.RoleFlags roleFlags: Int): Boolean {
|
||||
return isFlagSet(roleFlags, C.ROLE_FLAG_DESCRIBES_VIDEO) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||
|
@ -36,20 +36,30 @@ object ThemeHelper {
|
||||
window.statusBarColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
getThemeColor(context, android.R.attr.colorBackground)
|
||||
} else {
|
||||
if (isDarkMode(context)) getThemeColor(context, android.R.attr.colorBackground)
|
||||
else getThemeColor(context, com.google.android.material.R.attr.colorOnBackground)
|
||||
if (isDarkMode(context)) {
|
||||
getThemeColor(context, android.R.attr.colorBackground)
|
||||
} else {
|
||||
getThemeColor(context, com.google.android.material.R.attr.colorOnBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background color of the navigation bar
|
||||
*/
|
||||
private fun setNavigationBarColor(context: Context, window: Window, isBottomNavVisible: Boolean) {
|
||||
private fun setNavigationBarColor(
|
||||
context: Context,
|
||||
window: Window,
|
||||
isBottomNavVisible: Boolean
|
||||
) {
|
||||
window.navigationBarColor = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M && !isDarkMode(context)) {
|
||||
getThemeColor(context, com.google.android.material.R.attr.colorOnBackground)
|
||||
} else {
|
||||
if (isBottomNavVisible) getThemeColor(context, com.google.android.material.R.attr.colorSurfaceContainer)
|
||||
else getThemeColor(context, android.R.attr.colorBackground)
|
||||
if (isBottomNavVisible) {
|
||||
getThemeColor(context, com.google.android.material.R.attr.colorSurfaceContainer)
|
||||
} else {
|
||||
getThemeColor(context, android.R.attr.colorBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,5 +46,5 @@ data class BackupFile(
|
||||
|
||||
// playlists are exported in two different formats because the formats differ too much unfortunately
|
||||
var localPlaylists: List<LocalPlaylistWithVideos>? = emptyList(),
|
||||
var playlists: List<PipedImportPlaylist>? = emptyList(),
|
||||
var playlists: List<PipedImportPlaylist>? = emptyList()
|
||||
)
|
||||
|
@ -7,4 +7,4 @@ import kotlinx.parcelize.Parcelize
|
||||
data class SelectableOption(
|
||||
val isSelected: Boolean,
|
||||
val name: String
|
||||
): Parcelable
|
||||
) : Parcelable
|
||||
|
@ -10,5 +10,5 @@ import kotlinx.serialization.Serializable
|
||||
data class UpdateInfo(
|
||||
val name: String,
|
||||
val body: String,
|
||||
@SerialName("html_url") val htmlUrl: String,
|
||||
@SerialName("html_url") val htmlUrl: String
|
||||
) : Parcelable
|
||||
|
@ -72,7 +72,9 @@ class PlaylistDownloadEnqueueService : LifecycleService() {
|
||||
private fun buildNotification(): Notification {
|
||||
return NotificationCompat.Builder(this, PLAYLIST_DOWNLOAD_ENQUEUE_CHANNEL_NAME)
|
||||
.setSmallIcon(R.drawable.ic_download)
|
||||
.setContentTitle(getString(R.string.enqueueing_playlist_download, playlistName ?: "..."))
|
||||
.setContentTitle(
|
||||
getString(R.string.enqueueing_playlist_download, playlistName ?: "...")
|
||||
)
|
||||
.setProgress(amountOfVideos, amountOfVideosDone, false)
|
||||
.setOnlyAlertOnce(true)
|
||||
.build()
|
||||
@ -183,4 +185,4 @@ class PlaylistDownloadEnqueueService : LifecycleService() {
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
R.id.searchResultFragment -> {
|
||||
navController.popBackStack(R.id.searchFragment, true) ||
|
||||
navController.popBackStack()
|
||||
navController.popBackStack()
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -112,9 +112,9 @@ class VideosAdapter(
|
||||
}
|
||||
|
||||
val context = (
|
||||
holder.videoRowBinding ?: holder.trendingRowBinding
|
||||
holder.videoRowBinding ?: holder.trendingRowBinding
|
||||
?: holder.allCaughtUpBinding
|
||||
)!!.root.context
|
||||
)!!.root.context
|
||||
val activity = (context as BaseActivity)
|
||||
val fragmentManager = activity.supportFragmentManager
|
||||
|
||||
|
@ -40,8 +40,9 @@ open class BaseActivity : AppCompatActivity() {
|
||||
ThemeHelper.updateTheme(this)
|
||||
|
||||
// Set the navigation and statusBar color if SDK < 23
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
ThemeHelper.setSystemBarColors(this, window, false)
|
||||
}
|
||||
|
||||
// set the apps language
|
||||
LocaleHelper.updateLanguage(this)
|
||||
|
@ -5,7 +5,6 @@ import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
|
@ -24,11 +24,11 @@ import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.parcelable.DownloadData
|
||||
import com.github.libretube.util.TextUtils
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class DownloadDialog : DialogFragment() {
|
||||
private lateinit var videoId: String
|
||||
|
@ -52,21 +52,31 @@ class DownloadPlaylistDialog : DialogFragment() {
|
||||
.setView(binding.root)
|
||||
.setPositiveButton(R.string.download) { _, _ ->
|
||||
with(binding) {
|
||||
val maxVideoQuality = if (videoSpinner.selectedItemPosition >= 1)
|
||||
val maxVideoQuality = if (videoSpinner.selectedItemPosition >= 1) {
|
||||
possibleVideoQualities[videoSpinner.selectedItemPosition - 1]
|
||||
.getWhileDigit() else null
|
||||
.getWhileDigit()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val maxAudioQuality = if (audioSpinner.selectedItemPosition >= 1)
|
||||
val maxAudioQuality = if (audioSpinner.selectedItemPosition >= 1) {
|
||||
possibleAudioQualities[audioSpinner.selectedItemPosition - 1]
|
||||
.getWhileDigit() else null
|
||||
.getWhileDigit()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val captionLanguage = if (subtitleSpinner.selectedItemPosition >= 1)
|
||||
val captionLanguage = if (subtitleSpinner.selectedItemPosition >= 1) {
|
||||
availableLanguages[subtitleSpinner.selectedItemPosition - 1].code
|
||||
else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val audioLanguage = if (audioLanguageSpinner.selectedItemPosition >= 1)
|
||||
val audioLanguage = if (audioLanguageSpinner.selectedItemPosition >= 1) {
|
||||
availableLanguages[audioLanguageSpinner.selectedItemPosition - 1].code
|
||||
else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (maxVideoQuality == null && maxAudioQuality == null) {
|
||||
Toast.makeText(context, R.string.nothing_selected, Toast.LENGTH_SHORT)
|
||||
@ -89,6 +99,5 @@ class DownloadPlaylistDialog : DialogFragment() {
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -5,7 +5,6 @@ import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@ -100,7 +99,7 @@ class VoteForSegmentDialog : DialogFragment() {
|
||||
binding.segmentsDropdown.items = segments.map {
|
||||
val (start, end) = it.segmentStartAndEnd
|
||||
val (startStr, endStr) = DateUtils.formatElapsedTime(start.toLong()) to
|
||||
DateUtils.formatElapsedTime(end.toLong())
|
||||
DateUtils.formatElapsedTime(end.toLong())
|
||||
"${it.category} ($startStr - $endStr)"
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ fun RecyclerView.addOnBottomReachedListener(onBottomReached: () -> Unit) {
|
||||
viewTreeObserver.addOnScrollChangedListener {
|
||||
if (!canScrollVertically(1)) onBottomReached()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,4 +20,4 @@ fun View.animateDown(
|
||||
.y(dy)
|
||||
.setDuration(duration)
|
||||
.start()
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,11 @@ import com.github.libretube.ui.extensions.addOnBottomReachedListener
|
||||
import com.github.libretube.ui.extensions.setupSubscriptionButton
|
||||
import com.github.libretube.ui.sheets.AddChannelToGroupSheet
|
||||
import com.github.libretube.util.deArrow
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ChannelFragment : DynamicLayoutManagerFragment() {
|
||||
private var _binding: FragmentChannelBinding? = null
|
||||
|
@ -62,7 +62,7 @@ class CommentsMainFragment : Fragment() {
|
||||
this,
|
||||
viewModel.videoIdLiveData.value ?: return,
|
||||
viewModel.channelAvatar ?: return,
|
||||
handleLink = viewModel.handleLink,
|
||||
handleLink = viewModel.handleLink
|
||||
) {
|
||||
viewModel.commentsSheetDismiss?.invoke()
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ class CommentsRepliesFragment : Fragment() {
|
||||
videoId,
|
||||
viewModel.channelAvatar,
|
||||
comment,
|
||||
viewModel.handleLink,
|
||||
viewModel.handleLink
|
||||
) {
|
||||
viewModel.commentsSheetDismiss?.invoke()
|
||||
}
|
||||
|
@ -156,15 +156,13 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if (dbDownloads.isNotEmpty()){
|
||||
if (dbDownloads.isNotEmpty()) {
|
||||
binding.deleteAll.isVisible = true
|
||||
binding.deleteAll.setOnClickListener{
|
||||
binding.deleteAll.setOnClickListener {
|
||||
showDeleteAllDialog(binding.root.context, adapter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
binding.shuffleBackground.setOnClickListener {
|
||||
BackgroundHelper.playOnBackgroundOffline(requireContext(), null)
|
||||
}
|
||||
@ -191,7 +189,6 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
@ -199,7 +196,12 @@ class DownloadsFragment : DynamicLayoutManagerFragment() {
|
||||
addAction(DownloadService.ACTION_SERVICE_STARTED)
|
||||
addAction(DownloadService.ACTION_SERVICE_STOPPED)
|
||||
}
|
||||
ContextCompat.registerReceiver(requireContext(), downloadReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
ContextCompat.registerReceiver(
|
||||
requireContext(),
|
||||
downloadReceiver,
|
||||
filter,
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
}
|
||||
|
||||
fun bindDownloadService(ids: IntArray? = null) {
|
||||
|
@ -95,7 +95,7 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun observeChanges() {
|
||||
with (homeViewModel) {
|
||||
with(homeViewModel) {
|
||||
trending.observe(viewLifecycleOwner, ::showTrending)
|
||||
feed.observe(viewLifecycleOwner, ::showFeed)
|
||||
bookmarks.observe(viewLifecycleOwner, ::showBookmarks)
|
||||
@ -111,7 +111,7 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun stopObservingChanges() {
|
||||
with (homeViewModel) {
|
||||
with(homeViewModel) {
|
||||
trending.removeObserver(::showTrending)
|
||||
feed.removeObserver(::showFeed)
|
||||
bookmarks.removeObserver(::showBookmarks)
|
||||
@ -151,11 +151,11 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun showFeed(streamItems: List<StreamItem>?) {
|
||||
if (streamItems == null) return
|
||||
if (streamItems == null) return
|
||||
|
||||
makeVisible(binding.featuredRV, binding.featuredTV)
|
||||
val feedVideos = streamItems.take(20).toMutableList()
|
||||
with (binding.featuredRV) {
|
||||
with(binding.featuredRV) {
|
||||
layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
|
||||
adapter = VideosAdapter(feedVideos, forceMode = LayoutMode.RELATED_COLUMN)
|
||||
}
|
||||
@ -165,7 +165,7 @@ class HomeFragment : Fragment() {
|
||||
if (bookmarks == null) return
|
||||
|
||||
makeVisible(binding.bookmarksTV, binding.bookmarksRV)
|
||||
with (binding.bookmarksRV) {
|
||||
with(binding.bookmarksRV) {
|
||||
layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
|
||||
adapter = PlaylistBookmarkAdapter(
|
||||
bookmarks.toMutableList(),
|
||||
@ -263,4 +263,4 @@ class HomeFragment : Fragment() {
|
||||
private fun makeVisible(vararg views: View) {
|
||||
views.forEach { it.isVisible = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,12 +113,12 @@ import com.github.libretube.util.PlayingQueue
|
||||
import com.github.libretube.util.TextUtils
|
||||
import com.github.libretube.util.TextUtils.toTimeInSeconds
|
||||
import com.github.libretube.util.YoutubeHlsPlaylistParser
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.abs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||
class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
@ -497,7 +497,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
.animateDown(
|
||||
duration = 300L,
|
||||
dy = 500F,
|
||||
onEnd = ::onManualPlayerClose,
|
||||
onEnd = ::onManualPlayerClose
|
||||
)
|
||||
}
|
||||
|
||||
@ -868,7 +868,11 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
handler.postDelayed(this::checkForSegments, 100)
|
||||
if (!sponsorBlockEnabled || viewModel.segments.isEmpty()) return
|
||||
|
||||
exoPlayer.checkForSegments(requireContext(), viewModel.segments, viewModel.sponsorBlockConfig)
|
||||
exoPlayer.checkForSegments(
|
||||
requireContext(),
|
||||
viewModel.segments,
|
||||
viewModel.sponsorBlockConfig
|
||||
)
|
||||
?.let { segment ->
|
||||
if (viewModel.isMiniPlayerVisible.value == true) return@let
|
||||
binding.sbSkipBtn.isVisible = true
|
||||
@ -913,7 +917,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
|
||||
val videoStream = streams.videoStreams.firstOrNull()
|
||||
val isShort = PlayingQueue.getCurrent()?.isShort == true ||
|
||||
(videoStream?.height ?: 0) > (videoStream?.width ?: 0)
|
||||
(videoStream?.height ?: 0) > (videoStream?.width ?: 0)
|
||||
|
||||
PlayingQueue.setOnQueueTapListener { streamItem ->
|
||||
streamItem.url?.toID()?.let { playNextVideo(it) }
|
||||
@ -950,7 +954,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
if (binding.playerMotionLayout.progress != 1.0f) {
|
||||
// show controllers when not in picture in picture mode
|
||||
val inPipMode = PlayerHelper.pipEnabled &&
|
||||
PictureInPictureCompat.isInPictureInPictureMode(requireActivity())
|
||||
PictureInPictureCompat.isInPictureInPictureMode(requireActivity())
|
||||
if (!inPipMode) {
|
||||
binding.player.useController = true
|
||||
}
|
||||
@ -1621,7 +1625,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
|
||||
private fun shouldStartPiP(): Boolean {
|
||||
return shouldUsePip() && exoPlayer.isPlaying &&
|
||||
!BackgroundHelper.isBackgroundServiceRunning(requireContext())
|
||||
!BackgroundHelper.isBackgroundServiceRunning(requireContext())
|
||||
}
|
||||
|
||||
private fun killPlayerFragment() {
|
||||
|
@ -61,20 +61,22 @@ class SearchResultFragment : DynamicLayoutManagerFragment() {
|
||||
|
||||
// filter options
|
||||
binding.filterChipGroup.setOnCheckedStateChangeListener { _, _ ->
|
||||
viewModel.setFilter(when (
|
||||
binding.filterChipGroup.checkedChipId
|
||||
) {
|
||||
R.id.chip_all -> "all"
|
||||
R.id.chip_videos -> "videos"
|
||||
R.id.chip_channels -> "channels"
|
||||
R.id.chip_playlists -> "playlists"
|
||||
R.id.chip_music_songs -> "music_songs"
|
||||
R.id.chip_music_videos -> "music_videos"
|
||||
R.id.chip_music_albums -> "music_albums"
|
||||
R.id.chip_music_playlists -> "music_playlists"
|
||||
R.id.chip_music_artists -> "music_artists"
|
||||
else -> throw IllegalArgumentException("Filter out of range")
|
||||
})
|
||||
viewModel.setFilter(
|
||||
when (
|
||||
binding.filterChipGroup.checkedChipId
|
||||
) {
|
||||
R.id.chip_all -> "all"
|
||||
R.id.chip_videos -> "videos"
|
||||
R.id.chip_channels -> "channels"
|
||||
R.id.chip_playlists -> "playlists"
|
||||
R.id.chip_music_songs -> "music_songs"
|
||||
R.id.chip_music_videos -> "music_videos"
|
||||
R.id.chip_music_albums -> "music_albums"
|
||||
R.id.chip_music_playlists -> "music_playlists"
|
||||
R.id.chip_music_artists -> "music_artists"
|
||||
else -> throw IllegalArgumentException("Filter out of range")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val timeStamp = args.query.toHttpUrlOrNull()?.queryParameter("t")?.toTimeInSeconds()
|
||||
|
@ -17,7 +17,6 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Channel
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.constants.IntentData
|
||||
@ -191,7 +190,10 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
|
||||
val fragManager = activityCompat
|
||||
.supportFragmentManager
|
||||
.apply {
|
||||
setFragmentResultListener(FILTER_SORT_REQUEST_KEY, activityCompat) { _, resultBundle ->
|
||||
setFragmentResultListener(
|
||||
FILTER_SORT_REQUEST_KEY,
|
||||
activityCompat
|
||||
) { _, resultBundle ->
|
||||
selectedSortOrder = resultBundle.getInt(IntentData.sortOptions)
|
||||
hideWatched = resultBundle.getBoolean(IntentData.hideWatched)
|
||||
showFeed()
|
||||
@ -301,9 +303,13 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
return if (hideWatched) runBlocking {
|
||||
DatabaseHelper.filterUnwatched(streamItems)
|
||||
} else streamItems
|
||||
return if (hideWatched) {
|
||||
runBlocking {
|
||||
DatabaseHelper.filterUnwatched(streamItems)
|
||||
}
|
||||
} else {
|
||||
streamItems
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<StreamItem>.sortedBySelectedOrder() = when (selectedSortOrder) {
|
||||
|
@ -25,6 +25,7 @@ class CommentsViewModel : ViewModel() {
|
||||
}.flow
|
||||
}
|
||||
.cachedIn(viewModelScope)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val commentRepliesFlow = videoIdLiveData.asFlow()
|
||||
.combine(selectedCommentLiveData.asFlow()) { videoId, comment -> videoId to comment }
|
||||
|
@ -28,7 +28,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class HomeViewModel: ViewModel() {
|
||||
class HomeViewModel : ViewModel() {
|
||||
private val hideWatched get() = PreferenceHelper.getBoolean(HIDE_WATCHED_FROM_FEED, false)
|
||||
|
||||
val trending: MutableLiveData<List<StreamItem>> = MutableLiveData(null)
|
||||
@ -147,4 +147,4 @@ class HomeViewModel: ViewModel() {
|
||||
private const val BOOKMARKS = "bookmarks"
|
||||
private const val PLAYLISTS = "playlists"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ import com.github.libretube.api.obj.Subtitle
|
||||
import com.github.libretube.helpers.PlayerHelper
|
||||
import com.github.libretube.util.NowPlayingNotification
|
||||
import com.github.libretube.util.deArrow
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.encodeToString
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class PlayerViewModel : ViewModel() {
|
||||
var player: ExoPlayer? = null
|
||||
|
@ -17,11 +17,13 @@ import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
||||
class SearchResultViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
|
||||
private val args = SearchResultFragmentArgs.fromSavedStateHandle(savedStateHandle)
|
||||
|
||||
// parse search URLs from YouTube entered in the search bar
|
||||
private val videoId = TextUtils.getVideoIdFromUri(args.query.toUri()) ?: args.query
|
||||
private val searchQuery = "${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId"
|
||||
|
||||
private val filterMutableData = MutableStateFlow("all")
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val searchResultsFlow = filterMutableData.flatMapLatest {
|
||||
Pager(
|
||||
|
@ -13,24 +13,24 @@ import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.helpers.LocaleHelper
|
||||
import com.github.libretube.util.deArrow
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class TrendsViewModel: ViewModel() {
|
||||
class TrendsViewModel : ViewModel() {
|
||||
val trendingVideos = MutableLiveData<List<StreamItem>>()
|
||||
var recyclerViewState: Parcelable? = null
|
||||
|
||||
fun fetchTrending(context: Context) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val region = LocaleHelper.getTrendingRegion(context)
|
||||
val response = withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getTrending(region).deArrow()
|
||||
}
|
||||
trendingVideos.postValue(response)
|
||||
val region = LocaleHelper.getTrendingRegion(context)
|
||||
val response = withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getTrending(region).deArrow()
|
||||
}
|
||||
trendingVideos.postValue(response)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
|
@ -7,7 +7,7 @@ import com.github.libretube.api.obj.Comment
|
||||
|
||||
class CommentRepliesPagingSource(
|
||||
private val videoId: String,
|
||||
private val commentNextPage: String?,
|
||||
private val commentNextPage: String?
|
||||
) : PagingSource<String, Comment>() {
|
||||
override fun getRefreshKey(state: PagingState<String, Comment>) = null
|
||||
|
||||
|
@ -10,7 +10,7 @@ import retrofit2.HttpException
|
||||
class SearchPagingSource(
|
||||
private val searchQuery: String,
|
||||
private val searchFilter: String
|
||||
): PagingSource<String, ContentItem>() {
|
||||
) : PagingSource<String, ContentItem>() {
|
||||
override fun getRefreshKey(state: PagingState<String, ContentItem>) = null
|
||||
|
||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, ContentItem> {
|
||||
|
@ -30,13 +30,14 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
|
||||
class InstanceSettings : BasePreferenceFragment() {
|
||||
override val titleResourceId: Int = R.string.instance
|
||||
private val token get() = PreferenceHelper.getToken()
|
||||
private var instances = mutableListOf<PipedInstance>()
|
||||
private val authInstanceToggle get() = findPreference<SwitchPreferenceCompat>(PreferenceKeys.AUTH_INSTANCE_TOGGLE)!!
|
||||
private val authInstanceToggle get() = findPreference<SwitchPreferenceCompat>(
|
||||
PreferenceKeys.AUTH_INSTANCE_TOGGLE
|
||||
)!!
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.instance_settings, rootKey)
|
||||
|
@ -21,7 +21,6 @@ class MainSettings : BasePreferenceFragment() {
|
||||
|
||||
// check app update manually
|
||||
update?.setOnPreferenceClickListener {
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
UpdateChecker(requireContext()).checkUpdate(true)
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package com.github.libretube.ui.sheets
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import com.github.libretube.R
|
||||
@ -11,7 +9,6 @@ import com.github.libretube.enums.ShareObjectType
|
||||
import com.github.libretube.helpers.BackgroundHelper
|
||||
import com.github.libretube.helpers.NavigationHelper
|
||||
import com.github.libretube.obj.ShareData
|
||||
import com.github.libretube.services.OfflinePlayerService
|
||||
import com.github.libretube.ui.dialogs.ShareDialog
|
||||
|
||||
class DownloadOptionsBottomSheet : BaseBottomSheet() {
|
||||
|
@ -13,7 +13,7 @@ import com.github.libretube.enums.ContentFilter
|
||||
import com.github.libretube.extensions.parcelableArrayList
|
||||
import com.github.libretube.obj.SelectableOption
|
||||
|
||||
class FilterSortBottomSheet: ExpandedBottomSheet() {
|
||||
class FilterSortBottomSheet : ExpandedBottomSheet() {
|
||||
private var _binding: FilterSortSheetBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
@ -83,7 +83,7 @@ class FilterSortBottomSheet: ExpandedBottomSheet() {
|
||||
|
||||
private fun setInitialFiltersState() {
|
||||
binding.filterVideos.isChecked = ContentFilter.VIDEOS.isEnabled
|
||||
binding.filterShorts.isChecked = ContentFilter.SHORTS.isEnabled
|
||||
binding.filterShorts.isChecked = ContentFilter.SHORTS.isEnabled
|
||||
binding.filterLivestreams.isChecked = ContentFilter.LIVESTREAMS.isEnabled
|
||||
binding.hideWatchedCheckbox.isChecked = hideWatched
|
||||
}
|
||||
@ -115,4 +115,4 @@ class FilterSortBottomSheet: ExpandedBottomSheet() {
|
||||
companion object {
|
||||
const val FILTER_SORT_REQUEST_KEY = "filter_sort_request_key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,9 @@ open class CustomExoPlayerView(
|
||||
Player.EVENT_PLAY_WHEN_READY_CHANGED
|
||||
)
|
||||
) {
|
||||
binding.playPauseBTN.setImageResource(PlayerHelper.getPlayPauseActionIcon(player))
|
||||
binding.playPauseBTN.setImageResource(
|
||||
PlayerHelper.getPlayPauseActionIcon(player)
|
||||
)
|
||||
|
||||
// keep screen on if the video is playing
|
||||
keepScreenOn = player.isPlaying == true
|
||||
|
@ -67,7 +67,6 @@ class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeS
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
|
||||
if (isStrictlyDownSwipe && distanceY > 0) {
|
||||
isStrictlyDownSwipe = false
|
||||
}
|
||||
|
@ -262,13 +262,13 @@ class NowPlayingNotification(
|
||||
|
||||
private fun createPlaybackState(@PlaybackStateCompat.State state: Int): PlaybackStateCompat {
|
||||
val stateActions = PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
|
||||
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
|
||||
PlaybackStateCompat.ACTION_REWIND or
|
||||
PlaybackStateCompat.ACTION_FAST_FORWARD or
|
||||
PlaybackStateCompat.ACTION_PLAY_PAUSE or
|
||||
PlaybackStateCompat.ACTION_PAUSE or
|
||||
PlaybackStateCompat.ACTION_PLAY or
|
||||
PlaybackStateCompat.ACTION_SEEK_TO
|
||||
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
|
||||
PlaybackStateCompat.ACTION_REWIND or
|
||||
PlaybackStateCompat.ACTION_FAST_FORWARD or
|
||||
PlaybackStateCompat.ACTION_PLAY_PAUSE or
|
||||
PlaybackStateCompat.ACTION_PAUSE or
|
||||
PlaybackStateCompat.ACTION_PLAY or
|
||||
PlaybackStateCompat.ACTION_SEEK_TO
|
||||
|
||||
return PlaybackStateCompat.Builder()
|
||||
.setActions(stateActions)
|
||||
@ -302,8 +302,13 @@ class NowPlayingNotification(
|
||||
|
||||
STOP -> {
|
||||
when (notificationType) {
|
||||
NowPlayingNotificationType.AUDIO_ONLINE -> BackgroundHelper.stopBackgroundPlay(context)
|
||||
NowPlayingNotificationType.AUDIO_OFFLINE -> BackgroundHelper.stopBackgroundPlay(context, OfflinePlayerService::class.java)
|
||||
NowPlayingNotificationType.AUDIO_ONLINE -> BackgroundHelper.stopBackgroundPlay(
|
||||
context
|
||||
)
|
||||
NowPlayingNotificationType.AUDIO_OFFLINE -> BackgroundHelper.stopBackgroundPlay(
|
||||
context,
|
||||
OfflinePlayerService::class.java
|
||||
)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
@ -381,7 +386,12 @@ class NowPlayingNotification(
|
||||
addAction(it)
|
||||
}
|
||||
}
|
||||
ContextCompat.registerReceiver(context, notificationActionReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
notificationActionReceiver,
|
||||
filter,
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -417,7 +427,7 @@ class NowPlayingNotification(
|
||||
VIDEO_ONLINE,
|
||||
VIDEO_OFFLINE,
|
||||
AUDIO_ONLINE,
|
||||
AUDIO_OFFLINE,
|
||||
AUDIO_OFFLINE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,15 @@ class PauseableTimer(
|
||||
fun resume() {
|
||||
if (timer == null) timer = Timer()
|
||||
|
||||
timer?.scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
handler.post(onTick)
|
||||
}
|
||||
}, delayMillis, delayMillis)
|
||||
timer?.scheduleAtFixedRate(
|
||||
object : TimerTask() {
|
||||
override fun run() {
|
||||
handler.post(onTick)
|
||||
}
|
||||
},
|
||||
delayMillis,
|
||||
delayMillis
|
||||
)
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
@ -36,4 +40,4 @@ class PauseableTimer(
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ import com.github.libretube.constants.IntentData.appUpdateURL
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||
import com.github.libretube.ui.dialogs.UpdateAvailableDialog
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Locale
|
||||
|
||||
class UpdateChecker(private val context: Context) {
|
||||
suspend fun checkUpdate(isManualCheck: Boolean = false) {
|
||||
@ -40,7 +40,7 @@ class UpdateChecker(private val context: Context) {
|
||||
|
||||
private fun showUpdateAvailableDialog(
|
||||
changelog: String,
|
||||
url: String,
|
||||
url: String
|
||||
) {
|
||||
val dialog = UpdateAvailableDialog()
|
||||
val args =
|
||||
|
@ -7,7 +7,6 @@ import androidx.benchmark.macro.StartupTimingMetric
|
||||
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@ -72,4 +71,4 @@ class StartupBenchmarks {
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user