Merge remote-tracking branch 'origin/master'

This commit is contained in:
Praveen Rajput 2023-02-06 01:08:44 +05:30
commit aa998d80a4
177 changed files with 1611 additions and 1560 deletions

View File

@ -1,18 +1,18 @@
<div align="center"> <div align="center">
<img src="https://libre-tube.github.io/images/gh-banner.png" width="auto" height="auto" alt="LibreTube"> <img src="https://libre-tube.github.io/images/gh-banner.png" width="auto" height="auto" alt="LibreTube">
[![GPL-v3](https://libre-tube.github.io/images/license-widget.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![GPL-v3](https://libre-tube.github.io/assets/widgets/license-widget.svg)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Matrix](https://libre-tube.github.io/images/mat-widget.svg)](https://matrix.to/#/#LibreTube:matrix.org) [![Matrix](https://libre-tube.github.io/assets/widgets/mat-widget.svg)](https://matrix.to/#/#LibreTube:matrix.org)
[![Telegram](https://libre-tube.github.io/images/tg-widget.svg)](https://t.me/libretube) [![Mastodon](https://libre-tube.github.io/assets/widgets/mast-widget.svg)](https://fosstodon.org/@libretube)
[![Twitter](https://libre-tube.github.io/images/tw-widget.svg)](https://twitter.com/libretube) [![Telegram](https://libre-tube.github.io/assets/widgets/tg-widget.svg)](https://t.me/libretube)
[![Reddit](https://libre-tube.github.io/images/rd-widget.svg)](https://www.reddit.com/r/Libretube/) [![Reddit](https://libre-tube.github.io/assets/widgets/rd-widget.svg)](https://www.reddit.com/r/Libretube/)
[![Discord](https://libre-tube.github.io/images/discord-widget.svg)](https://discord.gg/Qc34xCj2GV) [![Discord](https://libre-tube.github.io/assets/widgets/discord-widget.svg)](https://discord.gg/Qc34xCj2GV)
</div><div align="center" style="width:100%; display:flex; justify-content:space-between;"> </div><div align="center" style="width:100%; display:flex; justify-content:space-between;">
[<img src="https://libre-tube.github.io/images/fdrload.png" alt="Get it on F-Droid" width="30%">](https://f-droid.org/en/packages/com.github.libretube/) [<img src="https://libre-tube.github.io/assets/badges/fdrload.png" alt="Get it on F-Droid" width="30%">](https://f-droid.org/en/packages/com.github.libretube/)
[<img src="https://libre-tube.github.io/images/izzyload.png" alt="Get it on IzzyOnDroid" width="30%">](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)<br/> [<img src="https://libre-tube.github.io/assets/badges/izzyload.png" alt="Get it on IzzyOnDroid" width="30%">](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)<br/>
[<img src="https://libre-tube.github.io/images/ghload.png" alt="Get it on GitHub" width="30%">](https://github.com/libre-tube/LibreTube/releases/latest) [<img src="https://libre-tube.github.io/assets/badges/ghload.png" alt="Get it on GitHub" width="30%">](https://github.com/libre-tube/LibreTube/releases/latest)
</div> </div>

View File

@ -46,8 +46,6 @@ android {
} }
debug { debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
debuggable true debuggable true
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
resValue "string", "app_name", "LibreTube Debug" resValue "string", "app_name", "LibreTube Debug"

View File

@ -12,12 +12,12 @@ import com.github.libretube.constants.BACKGROUND_CHANNEL_ID
import com.github.libretube.constants.DOWNLOAD_CHANNEL_ID import com.github.libretube.constants.DOWNLOAD_CHANNEL_ID
import com.github.libretube.constants.PUSH_CHANNEL_ID import com.github.libretube.constants.PUSH_CHANNEL_ID
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NotificationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.helpers.ShortcutHelper
import com.github.libretube.util.ExceptionHandler import com.github.libretube.util.ExceptionHandler
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NotificationHelper
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.ProxyHelper
import com.github.libretube.util.ShortcutHelper
class LibreTubeApp : Application() { class LibreTubeApp : Application() {
override fun onCreate() { override fun onCreate() {

View File

@ -109,7 +109,14 @@ interface PipedApi {
suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem> suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem>
@GET("feed/unauthenticated") @GET("feed/unauthenticated")
suspend fun getUnauthenticatedFeed(@Query("channels") channels: String): List<StreamItem> suspend fun getUnauthenticatedFeed(
@Query("channels") channels: String
): List<StreamItem>
@POST("feed/unauthenticated")
suspend fun getUnauthenticatedFeed(
@Body channels: List<String>
): List<StreamItem>
@GET("subscribed") @GET("subscribed")
suspend fun isSubscribed( suspend fun isSubscribed(
@ -121,7 +128,14 @@ interface PipedApi {
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription> suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
@GET("subscriptions/unauthenticated") @GET("subscriptions/unauthenticated")
suspend fun unauthenticatedSubscriptions(@Query("channels") channels: String): List<Subscription> suspend fun unauthenticatedSubscriptions(
@Query("channels") channels: String
): List<Subscription>
@POST("subscriptions/unauthenticated")
suspend fun unauthenticatedSubscriptions(
@Body channels: List<String>
): List<Subscription>
@POST("subscribe") @POST("subscribe")
suspend fun subscribe( suspend fun subscribe(

View File

@ -2,6 +2,7 @@ package com.github.libretube.api
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.core.text.isDigitsOnly
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.obj.Playlist import com.github.libretube.api.obj.Playlist
import com.github.libretube.api.obj.PlaylistId import com.github.libretube.api.obj.PlaylistId
@ -12,20 +13,17 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.LocalPlaylist import com.github.libretube.db.obj.LocalPlaylist
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toLocalPlaylistItem import com.github.libretube.extensions.toLocalPlaylistItem
import com.github.libretube.extensions.toStreamItem import com.github.libretube.extensions.toStreamItem
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.ImportPlaylist import com.github.libretube.obj.ImportPlaylist
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.ProxyHelper
import java.io.IOException import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
@ -35,26 +33,22 @@ object PlaylistsHelper {
private val token get() = PreferenceHelper.getToken() private val token get() = PreferenceHelper.getToken()
val loggedIn: Boolean get() = token != "" val loggedIn: Boolean get() = token.isNotEmpty()
suspend fun getPlaylists(): List<Playlists> { suspend fun getPlaylists(): List<Playlists> = withContext(Dispatchers.IO) {
if (loggedIn) return RetrofitInstance.authApi.getUserPlaylists(token) if (loggedIn) {
RetrofitInstance.authApi.getUserPlaylists(token)
val localPlaylists = awaitQuery { } else {
DatabaseHolder.Database.localPlaylistsDao().getAll() DatabaseHolder.Database.localPlaylistsDao().getAll()
} .map {
val playlists = mutableListOf<Playlists>()
localPlaylists.forEach {
playlists.add(
Playlists( Playlists(
id = it.playlist.id.toString(), id = it.playlist.id.toString(),
name = it.playlist.name, name = it.playlist.name,
thumbnail = ProxyHelper.rewriteUrl(it.playlist.thumbnailUrl), thumbnail = ProxyHelper.rewriteUrl(it.playlist.thumbnailUrl),
videos = it.videos.size.toLong() videos = it.videos.size.toLong()
) )
)
} }
return playlists }
} }
suspend fun getPlaylist(playlistId: String): Playlist { suspend fun getPlaylist(playlistId: String): Playlist {
@ -63,9 +57,8 @@ object PlaylistsHelper {
PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId) PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId)
PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId) PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId)
PlaylistType.LOCAL -> { PlaylistType.LOCAL -> {
val relation = awaitQuery { val relation = DatabaseHolder.Database.localPlaylistsDao().getAll()
DatabaseHolder.Database.localPlaylistsDao().getAll() .first { it.playlist.id.toString() == playlistId }
}.first { it.playlist.id.toString() == playlistId }
return Playlist( return Playlist(
name = relation.playlist.name, name = relation.playlist.name,
thumbnailUrl = ProxyHelper.rewriteUrl(relation.playlist.thumbnailUrl), thumbnailUrl = ProxyHelper.rewriteUrl(relation.playlist.thumbnailUrl),
@ -76,38 +69,26 @@ object PlaylistsHelper {
} }
} }
suspend fun createPlaylist( suspend fun createPlaylist(playlistName: String, appContext: Context?): String? {
playlistName: String,
appContext: Context
): String? {
if (!loggedIn) { if (!loggedIn) {
awaitQuery { val playlist = LocalPlaylist(name = playlistName, thumbnailUrl = "")
DatabaseHolder.Database.localPlaylistsDao().createPlaylist( DatabaseHolder.Database.localPlaylistsDao().createPlaylist(playlist)
LocalPlaylist( return DatabaseHolder.Database.localPlaylistsDao().getAll()
name = playlistName, .last().playlist.id.toString()
thumbnailUrl = "" } else {
) return try {
)
}
return awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().getAll()
}.last().playlist.id.toString()
}
val response = try {
RetrofitInstance.authApi.createPlaylist(token, Playlists(name = playlistName)) RetrofitInstance.authApi.createPlaylist(token, Playlists(name = playlistName))
} catch (e: IOException) { } catch (e: IOException) {
appContext.toastFromMainThread(R.string.unknown_error) appContext?.toastFromMainThread(R.string.unknown_error)
return null return null
} catch (e: HttpException) { } catch (e: HttpException) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
appContext.toastFromMainThread(R.string.server_error) appContext?.toastFromMainThread(R.string.server_error)
return null return null
}.playlistId.also {
appContext?.toastFromMainThread(R.string.playlistCreated)
} }
if (response.playlistId != null) {
appContext.toastFromMainThread(R.string.playlistCreated)
return response.playlistId
} }
return null
} }
suspend fun addToPlaylist(playlistId: String, vararg videos: StreamItem): Boolean { suspend fun addToPlaylist(playlistId: String, vararg videos: StreamItem): Boolean {
@ -117,7 +98,6 @@ object PlaylistsHelper {
for (video in videos) { for (video in videos) {
val localPlaylistItem = video.toLocalPlaylistItem(playlistId) val localPlaylistItem = video.toLocalPlaylistItem(playlistId)
awaitQuery {
// avoid duplicated videos in a playlist // avoid duplicated videos in a playlist
DatabaseHolder.Database.localPlaylistsDao() DatabaseHolder.Database.localPlaylistsDao()
.deletePlaylistItemsByVideoId(playlistId, localPlaylistItem.videoId) .deletePlaylistItemsByVideoId(playlistId, localPlaylistItem.videoId)
@ -125,14 +105,12 @@ object PlaylistsHelper {
// add the new video to the database // add the new video to the database
DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem) DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem)
if (localPlaylist.playlist.thumbnailUrl == "") { val playlist = localPlaylist.playlist
if (playlist.thumbnailUrl == "") {
// set the new playlist thumbnail URL // set the new playlist thumbnail URL
localPlaylistItem.thumbnailUrl?.let { localPlaylistItem.thumbnailUrl?.let {
localPlaylist.playlist.thumbnailUrl = it playlist.thumbnailUrl = it
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist( DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist)
localPlaylist.playlist
)
}
} }
} }
} }
@ -144,68 +122,46 @@ object PlaylistsHelper {
} }
suspend fun renamePlaylist(playlistId: String, newName: String): Boolean { suspend fun renamePlaylist(playlistId: String, newName: String): Boolean {
if (!loggedIn) { return if (!loggedIn) {
val playlist = awaitQuery { val playlist = DatabaseHolder.Database.localPlaylistsDao().getAll()
DatabaseHolder.Database.localPlaylistsDao().getAll() .first { it.playlist.id.toString() == playlistId }.playlist
}.first { it.playlist.id.toString() == playlistId }.playlist
playlist.name = newName playlist.name = newName
awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist) DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist)
true
} else {
val playlist = PlaylistId(playlistId, newName = newName)
RetrofitInstance.authApi.renamePlaylist(token, playlist).playlistId != null
} }
return true
} }
return RetrofitInstance.authApi.renamePlaylist( suspend fun removeFromPlaylist(playlistId: String, index: Int): Boolean {
token, return if (!loggedIn) {
PlaylistId(playlistId, newName = newName) val transaction = DatabaseHolder.Database.localPlaylistsDao().getAll()
).playlistId != null .first { it.playlist.id.toString() == playlistId }
}
suspend fun removeFromPlaylist(playlistId: String, index: Int) {
if (!loggedIn) {
val transaction = awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().getAll()
}.first { it.playlist.id.toString() == playlistId }
awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().removePlaylistVideo( DatabaseHolder.Database.localPlaylistsDao().removePlaylistVideo(
transaction.videos[index] transaction.videos[index]
) )
} // set a new playlist thumbnail if the first video got removed
if (transaction.videos.size > 1) {
if (index == 0) { if (index == 0) {
transaction.videos[1].thumbnailUrl?.let { transaction.playlist.thumbnailUrl = transaction.videos.getOrNull(1)?.thumbnailUrl ?: ""
transaction.playlist.thumbnailUrl = it
} }
awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(
transaction.playlist
)
}
}
return
}
// remove thumbnail if playlist now empty
awaitQuery {
transaction.playlist.thumbnailUrl = ""
DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(transaction.playlist) DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(transaction.playlist)
} true
return } else {
}
RetrofitInstance.authApi.removeFromPlaylist( RetrofitInstance.authApi.removeFromPlaylist(
PreferenceHelper.getToken(), PreferenceHelper.getToken(),
PlaylistId( PlaylistId(playlistId = playlistId, index = index)
playlistId = playlistId, ).message == "ok"
index = index }
)
)
} }
suspend fun importPlaylists(appContext: Context, playlists: List<ImportPlaylist>) { suspend fun importPlaylists(playlists: List<ImportPlaylist>) = withContext(Dispatchers.IO) {
for (playlist in playlists) { playlists.map { playlist ->
val playlistId = createPlaylist(playlist.name!!, appContext) ?: continue val playlistId = createPlaylist(playlist.name!!, null)
async {
playlistId ?: return@async
// if logged in, add the playlists by their ID via an api call // if logged in, add the playlists by their ID via an api call
val success: Boolean = if (loggedIn) { if (loggedIn) {
addToPlaylist( addToPlaylist(
playlistId, playlistId,
*playlist.videos.map { *playlist.videos.map {
@ -214,19 +170,24 @@ object PlaylistsHelper {
) )
} else { } else {
// if not logged in, all video information needs to become fetched manually // if not logged in, all video information needs to become fetched manually
try { runCatching {
val streamItems = playlist.videos.map { val streamItems = playlist.videos.map {
async {
try {
RetrofitInstance.api.getStreams(it).toStreamItem(it) RetrofitInstance.api.getStreams(it).toStreamItem(it)
}
addToPlaylist(playlistId, *streamItems.toTypedArray())
} catch (e: Exception) { } catch (e: Exception) {
false null
} }
} }
appContext.toastFromMainThread(
if (success) R.string.importsuccess else R.string.server_error
)
} }
.awaitAll()
.filterNotNull()
addToPlaylist(playlistId, *streamItems.toTypedArray())
}
}
}
}.awaitAll()
} }
suspend fun exportPlaylists(): List<ImportPlaylist> = withContext(Dispatchers.IO) { suspend fun exportPlaylists(): List<ImportPlaylist> = withContext(Dispatchers.IO) {
@ -241,18 +202,16 @@ object PlaylistsHelper {
} }
} }
fun clonePlaylist(context: Context, playlistId: String) { suspend fun clonePlaylist(context: Context, playlistId: String): String? {
val appContext = context.applicationContext val appContext = context.applicationContext
if (!loggedIn) { if (!loggedIn) {
CoroutineScope(Dispatchers.IO).launch {
val playlist = try { val playlist = try {
RetrofitInstance.api.getPlaylist(playlistId) RetrofitInstance.api.getPlaylist(playlistId)
} catch (e: Exception) { } catch (e: Exception) {
appContext.toastFromMainThread(R.string.server_error) appContext.toastFromMainThread(R.string.server_error)
return@launch return null
} }
val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext) val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext) ?: return null
newPlaylist ?: return@launch
addToPlaylist(newPlaylist, *playlist.relatedStreams.toTypedArray()) addToPlaylist(newPlaylist, *playlist.relatedStreams.toTypedArray())
@ -263,27 +222,15 @@ object PlaylistsHelper {
addToPlaylist(newPlaylist, *relatedStreams.toTypedArray()) addToPlaylist(newPlaylist, *relatedStreams.toTypedArray())
}.nextpage }.nextpage
} catch (e: Exception) { } catch (e: Exception) {
return@launch break
} }
} }
} return playlistId
return
} }
CoroutineScope(Dispatchers.IO).launch { return runCatching {
val response = try { RetrofitInstance.authApi.clonePlaylist(token, PlaylistId(playlistId))
RetrofitInstance.authApi.clonePlaylist( }.getOrNull()?.playlistId
token,
PlaylistId(playlistId)
)
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
appContext?.toastFromMainThread(
if (response.playlistId != null) R.string.playlistCloned else R.string.server_error
)
}
} }
fun getPrivatePlaylistType(): PlaylistType { fun getPrivatePlaylistType(): PlaylistType {
@ -291,8 +238,12 @@ object PlaylistsHelper {
} }
private fun getPrivatePlaylistType(playlistId: String): PlaylistType { private fun getPrivatePlaylistType(playlistId: String): PlaylistType {
if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL return if (playlistId.isDigitsOnly()) {
if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE PlaylistType.LOCAL
return PlaylistType.PUBLIC } else if (playlistId.matches(pipedPlaylistRegex)) {
PlaylistType.PRIVATE
} else {
PlaylistType.PUBLIC
}
} }
} }

View File

@ -2,7 +2,7 @@ package com.github.libretube.api
import com.github.libretube.constants.PIPED_API_URL import com.github.libretube.constants.PIPED_API_URL
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit import retrofit2.Retrofit

View File

@ -10,13 +10,15 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.LocalSubscription import com.github.libretube.db.obj.LocalSubscription
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
object SubscriptionHelper { object SubscriptionHelper {
private const val GET_SUBSCRIPTIONS_LIMIT = 100
suspend fun subscribe(channelId: String) { suspend fun subscribe(channelId: String) {
val token = PreferenceHelper.getToken() val token = PreferenceHelper.getToken()
if (token.isNotEmpty()) { if (token.isNotEmpty()) {
@ -102,17 +104,20 @@ object SubscriptionHelper {
} }
} }
suspend fun getFormattedLocalSubscriptions(): String {
return Database.localSubscriptionDao().getAll()
.joinToString(",") { it.channelId }
}
suspend fun getSubscriptions(): List<Subscription> { suspend fun getSubscriptions(): List<Subscription> {
val token = PreferenceHelper.getToken() val token = PreferenceHelper.getToken()
return if (token.isNotEmpty()) { return if (token.isNotEmpty()) {
RetrofitInstance.authApi.subscriptions(token) RetrofitInstance.authApi.subscriptions(token)
} else { } else {
RetrofitInstance.authApi.unauthenticatedSubscriptions(getFormattedLocalSubscriptions()) val subscriptions = Database.localSubscriptionDao().getAll().map { it.channelId }
when {
subscriptions.size > GET_SUBSCRIPTIONS_LIMIT -> RetrofitInstance.authApi.unauthenticatedSubscriptions(
subscriptions
)
else -> RetrofitInstance.authApi.unauthenticatedSubscriptions(
subscriptions.joinToString(",")
)
}
} }
} }
@ -121,7 +126,15 @@ object SubscriptionHelper {
return if (token.isNotEmpty()) { return if (token.isNotEmpty()) {
RetrofitInstance.authApi.getFeed(token) RetrofitInstance.authApi.getFeed(token)
} else { } else {
RetrofitInstance.authApi.getUnauthenticatedFeed(getFormattedLocalSubscriptions()) val subscriptions = Database.localSubscriptionDao().getAll().map { it.channelId }
when {
subscriptions.size > GET_SUBSCRIPTIONS_LIMIT -> RetrofitInstance.authApi.getUnauthenticatedFeed(
subscriptions
)
else -> RetrofitInstance.authApi.getUnauthenticatedFeed(
subscriptions.joinToString(",")
)
}
} }
} }
} }

View File

@ -18,11 +18,11 @@ const val FAQ_URL = "https://libre-tube.github.io/#faq"
/** /**
* Social media links for the community fragment * Social media links for the community fragment
*/ */
const val TELEGRAM_URL = "https://t.me/libretube"
const val MATRIX_URL = "https://matrix.to/#/#LibreTube:matrix.org" const val MATRIX_URL = "https://matrix.to/#/#LibreTube:matrix.org"
const val MASTODON_URL = "https://fosstodon.org/@libretube"
const val TELEGRAM_URL = "https://t.me/libretube"
const val DISCORD_URL = "https://discord.com/invite/Qc34xCj2GV" const val DISCORD_URL = "https://discord.com/invite/Qc34xCj2GV"
const val REDDIT_URL = "https://www.reddit.com/r/Libretube/" const val REDDIT_URL = "https://www.reddit.com/r/Libretube/"
const val TWITTER_URL = "https://twitter.com/libretube"
/** /**
* Share Dialog * Share Dialog

View File

@ -7,7 +7,7 @@ import com.github.libretube.db.obj.SearchHistoryItem
import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
object DatabaseHelper { object DatabaseHelper {
private const val MAX_SEARCH_HISTORY_SIZE = 20 private const val MAX_SEARCH_HISTORY_SIZE = 20

View File

@ -14,29 +14,26 @@ import com.github.libretube.db.obj.LocalPlaylistWithVideos
interface LocalPlaylistsDao { interface LocalPlaylistsDao {
@Transaction @Transaction
@Query("SELECT * FROM LocalPlaylist") @Query("SELECT * FROM LocalPlaylist")
fun getAll(): List<LocalPlaylistWithVideos> suspend fun getAll(): List<LocalPlaylistWithVideos>
@Insert @Insert
fun createPlaylist(playlist: LocalPlaylist) suspend fun createPlaylist(playlist: LocalPlaylist)
@Update @Update
fun updatePlaylist(playlist: LocalPlaylist) suspend fun updatePlaylist(playlist: LocalPlaylist)
@Delete
fun deletePlaylist(playlist: LocalPlaylist)
@Query("DELETE FROM localPlaylist WHERE id = :playlistId") @Query("DELETE FROM localPlaylist WHERE id = :playlistId")
fun deletePlaylistById(playlistId: String) suspend fun deletePlaylistById(playlistId: String)
@Insert @Insert
fun addPlaylistVideo(playlistVideo: LocalPlaylistItem) suspend fun addPlaylistVideo(playlistVideo: LocalPlaylistItem)
@Delete @Delete
fun removePlaylistVideo(playlistVideo: LocalPlaylistItem) suspend fun removePlaylistVideo(playlistVideo: LocalPlaylistItem)
@Query("DELETE FROM localPlaylistItem WHERE playlistId = :playlistId") @Query("DELETE FROM localPlaylistItem WHERE playlistId = :playlistId")
fun deletePlaylistItemsByPlaylistId(playlistId: String) suspend fun deletePlaylistItemsByPlaylistId(playlistId: String)
@Query("DELETE FROM localPlaylistItem WHERE playlistId = :playlistId AND videoId = :videoId") @Query("DELETE FROM localPlaylistItem WHERE playlistId = :playlistId AND videoId = :videoId")
fun deletePlaylistItemsByVideoId(playlistId: String, videoId: String) suspend fun deletePlaylistItemsByVideoId(playlistId: String, videoId: String)
} }

View File

@ -3,7 +3,7 @@ package com.github.libretube.extensions
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
import com.github.libretube.db.obj.LocalPlaylistItem import com.github.libretube.db.obj.LocalPlaylistItem
import com.github.libretube.util.ProxyHelper import com.github.libretube.helpers.ProxyHelper
fun Streams.toStreamItem(videoId: String): StreamItem { fun Streams.toStreamItem(videoId: String): StreamItem {
return StreamItem( return StreamItem(

View File

@ -4,6 +4,8 @@ import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.widget.Toast import android.widget.Toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
fun Context.toastFromMainThread(text: String) { fun Context.toastFromMainThread(text: String) {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
@ -18,3 +20,11 @@ fun Context.toastFromMainThread(text: String) {
fun Context.toastFromMainThread(stringId: Int) { fun Context.toastFromMainThread(stringId: Int) {
toastFromMainThread(getString(stringId)) toastFromMainThread(getString(stringId))
} }
suspend fun Context.toastFromMainDispatcher(text: String) = withContext(Dispatchers.Main) {
Toast.makeText(this@toastFromMainDispatcher, text, Toast.LENGTH_SHORT).show()
}
suspend fun Context.toastFromMainDispatcher(stringId: Int) {
toastFromMainDispatcher(getString(stringId))
}

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.ActivityManager import android.app.ActivityManager
import android.content.Context import android.content.Context

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
@ -11,8 +11,7 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.obj.BackupFile import com.github.libretube.obj.BackupFile
import com.github.libretube.obj.PreferenceItem import com.github.libretube.obj.PreferenceItem
import kotlinx.coroutines.Dispatchers import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
@ -23,33 +22,30 @@ import kotlinx.serialization.json.longOrNull
/** /**
* Backup and restore the preferences * Backup and restore the preferences
*/ */
class BackupHelper(private val context: Context) { object BackupHelper {
/** /**
* Write a [BackupFile] containing the database content as well as the preferences * Write a [BackupFile] containing the database content as well as the preferences
*/ */
fun createAdvancedBackup(uri: Uri?, backupFile: BackupFile) { @OptIn(ExperimentalSerializationApi::class)
uri?.let { fun createAdvancedBackup(context: Context, uri: Uri, backupFile: BackupFile) {
try { try {
context.contentResolver.openOutputStream(it)?.use { outputStream -> context.contentResolver.openOutputStream(uri)?.use { outputStream ->
JsonHelper.json.encodeToStream(backupFile, outputStream) JsonHelper.json.encodeToStream(backupFile, outputStream)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), "Error while writing backup: $e") Log.e(TAG(), "Error while writing backup: $e")
} }
} }
}
/** /**
* Restore data from a [BackupFile] * Restore data from a [BackupFile]
*/ */
fun restoreAdvancedBackup(uri: Uri?) { @OptIn(ExperimentalSerializationApi::class)
val backupFile = uri?.let { suspend fun restoreAdvancedBackup(context: Context, uri: Uri) {
context.contentResolver.openInputStream(it)?.use { inputStream -> val backupFile = context.contentResolver.openInputStream(uri)?.use {
JsonHelper.json.decodeFromStream<BackupFile>(inputStream) JsonHelper.json.decodeFromStream<BackupFile>(it)
}
} ?: return } ?: return
runBlocking(Dispatchers.IO) {
Database.watchHistoryDao().insertAll( Database.watchHistoryDao().insertAll(
*backupFile.watchHistory.orEmpty().toTypedArray() *backupFile.watchHistory.orEmpty().toTypedArray()
) )
@ -76,14 +72,13 @@ class BackupHelper(private val context: Context) {
} }
} }
restorePreferences(backupFile.preferences) restorePreferences(context, backupFile.preferences)
}
} }
/** /**
* Restore the shared preferences from a backup file * Restore the shared preferences from a backup file
*/ */
private fun restorePreferences(preferences: List<PreferenceItem>?) { private fun restorePreferences(context: Context, preferences: List<PreferenceItem>?) {
if (preferences == null) return if (preferences == null) return
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) { PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) {
// clear the previous settings // clear the previous settings

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.Activity import android.app.Activity
import android.view.WindowManager import android.view.WindowManager

View File

@ -0,0 +1,14 @@
package com.github.libretube.helpers
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.core.content.getSystemService
import com.github.libretube.R
object ClipboardHelper {
fun save(context: Context, text: String) {
val clip = ClipData.newPlainText(context.getString(R.string.copied), text)
context.getSystemService<ClipboardManager>()!!.setPrimaryClip(clip)
}
}

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import com.github.libretube.api.obj.PipedStream import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
@ -13,6 +13,7 @@ import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import com.github.libretube.api.CronetHelper import com.github.libretube.api.CronetHelper
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.util.DataSaverMode
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import okio.use import okio.use

View File

@ -1,60 +1,51 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.Activity import android.app.Activity
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import android.widget.Toast
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.JsonHelper import com.github.libretube.api.JsonHelper
import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.PlaylistsHelper
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.SubscriptionHelper import com.github.libretube.api.SubscriptionHelper
import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.obj.ImportPlaylist import com.github.libretube.obj.ImportPlaylist
import com.github.libretube.obj.ImportPlaylistFile import com.github.libretube.obj.ImportPlaylistFile
import com.github.libretube.obj.NewPipeSubscription import com.github.libretube.obj.NewPipeSubscription
import com.github.libretube.obj.NewPipeSubscriptions import com.github.libretube.obj.NewPipeSubscriptions
import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
import okio.use import okio.use
class ImportHelper( object ImportHelper {
private val activity: Activity
) {
/** /**
* Import subscriptions by a file uri * Import subscriptions by a file uri
*/ */
fun importSubscriptions(uri: Uri?) { suspend fun importSubscriptions(activity: Activity, uri: Uri) {
if (uri == null) return
try { try {
val applicationContext = activity.applicationContext SubscriptionHelper.importSubscriptions(getChannelsFromUri(activity, uri))
val channels = getChannelsFromUri(uri) activity.toastFromMainDispatcher(R.string.importsuccess)
CoroutineScope(Dispatchers.IO).launch {
SubscriptionHelper.importSubscriptions(channels)
}.invokeOnCompletion {
applicationContext.toastFromMainThread(R.string.importsuccess)
}
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
activity.toastFromMainThread( activity.toastFromMainDispatcher(
activity.getString(R.string.unsupported_file_format) + activity.getString(R.string.unsupported_file_format) +
" (${activity.contentResolver.getType(uri)})" " (${activity.contentResolver.getType(uri)})"
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
Toast.makeText(activity, e.localizedMessage, Toast.LENGTH_SHORT).show() e.localizedMessage?.let {
activity.toastFromMainDispatcher(it)
}
} }
} }
/** /**
* Get a list of channel IDs from a file [Uri] * Get a list of channel IDs from a file [Uri]
*/ */
private fun getChannelsFromUri(uri: Uri): List<String> { private fun getChannelsFromUri(activity: Activity, uri: Uri): List<String> {
return when (val fileType = activity.contentResolver.getType(uri)) { return when (val fileType = activity.contentResolver.getType(uri)) {
"application/json", "application/*", "application/octet-stream" -> { "application/json", "application/*", "application/octet-stream" -> {
// NewPipe subscriptions format // NewPipe subscriptions format
@ -82,16 +73,13 @@ class ImportHelper(
/** /**
* Write the text to the document * Write the text to the document
*/ */
fun exportSubscriptions(uri: Uri?) { suspend fun exportSubscriptions(activity: Activity, uri: Uri) {
if (uri == null) return
runBlocking(Dispatchers.IO) {
val token = PreferenceHelper.getToken() val token = PreferenceHelper.getToken()
val subs = if (token.isNotEmpty()) { val subs = if (token.isNotEmpty()) {
RetrofitInstance.authApi.subscriptions(token) RetrofitInstance.authApi.subscriptions(token)
} else { } else {
RetrofitInstance.authApi.unauthenticatedSubscriptions( val subscriptions = Database.localSubscriptionDao().getAll().map { it.channelId }
SubscriptionHelper.getFormattedLocalSubscriptions() RetrofitInstance.authApi.unauthenticatedSubscriptions(subscriptions)
)
} }
val newPipeChannels = subs.map { val newPipeChannels = subs.map {
NewPipeSubscription(it.name, 0, "https://www.youtube.com${it.url}") NewPipeSubscription(it.name, 0, "https://www.youtube.com${it.url}")
@ -102,16 +90,14 @@ class ImportHelper(
JsonHelper.json.encodeToStream(newPipeSubscriptions, it) JsonHelper.json.encodeToStream(newPipeSubscriptions, it)
} }
activity.toastFromMainThread(R.string.exportsuccess) activity.toastFromMainDispatcher(R.string.exportsuccess)
}
} }
/** /**
* Import Playlists * Import Playlists
*/ */
fun importPlaylists(uri: Uri?) { @OptIn(ExperimentalSerializationApi::class)
if (uri == null) return suspend fun importPlaylists(activity: Activity, uri: Uri) {
val importPlaylists = mutableListOf<ImportPlaylist>() val importPlaylists = mutableListOf<ImportPlaylist>()
when (val fileType = activity.contentResolver.getType(uri)) { when (val fileType = activity.contentResolver.getType(uri)) {
@ -136,20 +122,22 @@ class ImportHelper(
importPlaylists.addAll(playlistFile?.playlists.orEmpty()) importPlaylists.addAll(playlistFile?.playlists.orEmpty())
} }
else -> { else -> {
activity.applicationContext.toastFromMainThread("Unsupported file type $fileType") activity.toastFromMainDispatcher("Unsupported file type $fileType")
return return
} }
} }
CoroutineScope(Dispatchers.IO).launch { // convert the YouTube URLs to videoIds
importPlaylists.forEach { playlist ->
playlist.videos = playlist.videos.map { it.takeLast(11) }
}
try { try {
PlaylistsHelper.importPlaylists(activity, importPlaylists) PlaylistsHelper.importPlaylists(importPlaylists)
activity.applicationContext.toastFromMainThread(R.string.success) activity.toastFromMainDispatcher(R.string.success)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
e.localizedMessage?.let { e.localizedMessage?.let {
activity.applicationContext.toastFromMainThread(it) activity.toastFromMainDispatcher(it)
}
} }
} }
} }
@ -157,10 +145,7 @@ class ImportHelper(
/** /**
* Export Playlists * Export Playlists
*/ */
fun exportPlaylists(uri: Uri?) { suspend fun exportPlaylists(activity: Activity, uri: Uri) {
if (uri == null) return
runBlocking {
val playlists = PlaylistsHelper.exportPlaylists() val playlists = PlaylistsHelper.exportPlaylists()
val playlistFile = ImportPlaylistFile("Piped", 1, playlists) val playlistFile = ImportPlaylistFile("Piped", 1, playlists)
@ -168,7 +153,6 @@ class ImportHelper(
JsonHelper.json.encodeToStream(playlistFile, it) JsonHelper.json.encodeToStream(playlistFile, it)
} }
activity.toastFromMainThread(R.string.exportsuccess) activity.toastFromMainDispatcher(R.string.exportsuccess)
}
} }
} }

View File

@ -0,0 +1,13 @@
package com.github.libretube.helpers
import android.content.Context
import android.content.Intent
import android.net.Uri
object IntentHelper {
fun openLinkFromHref(context: Context, link: String) {
val uri = Uri.parse(link)
val launchIntent = Intent(Intent.ACTION_VIEW).setData(uri)
context.startActivity(launchIntent)
}
}

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
@ -10,6 +10,8 @@ import android.os.Looper
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.os.postDelayed import androidx.core.os.postDelayed
import androidx.fragment.app.commitNow
import androidx.fragment.app.replace
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
@ -90,17 +92,9 @@ object NavigationHelper {
) )
val activity = context as AppCompatActivity val activity = context as AppCompatActivity
activity.supportFragmentManager.beginTransaction() activity.supportFragmentManager.commitNow {
.remove(PlayerFragment()) replace<PlayerFragment>(R.id.container, args = bundle)
.commit()
activity.supportFragmentManager.beginTransaction()
.replace(
R.id.container,
PlayerFragment().apply {
arguments = bundle
} }
)
.commitNow()
} }
fun navigatePlaylist( fun navigatePlaylist(

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.net.ConnectivityManager import android.net.ConnectivityManager

View File

@ -1,10 +1,10 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import com.github.libretube.constants.NOTIFICATION_WORK_NAME import com.github.libretube.constants.NOTIFICATION_WORK_NAME
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
@ -56,8 +56,7 @@ object NotificationHelper {
.build() .build()
// create the worker // create the worker
val notificationWorker = PeriodicWorkRequest.Builder( val notificationWorker = PeriodicWorkRequestBuilder<NotificationWorker>(
NotificationWorker::class.java,
checkingFrequency, checkingFrequency,
TimeUnit.MINUTES TimeUnit.MINUTES
) )

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.Activity import android.app.Activity
import android.app.PendingIntent import android.app.PendingIntent

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.app.Activity import android.app.Activity
import android.content.ComponentName import android.content.ComponentName

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.helpers
import android.os.Build import android.os.Build
import android.view.WindowManager import android.view.WindowManager

View File

@ -29,9 +29,9 @@ import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toStreamItem import com.github.libretube.extensions.toStreamItem
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem

View File

@ -25,14 +25,14 @@ import com.github.libretube.extensions.getContentLength
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.extensions.toDownloadItems import com.github.libretube.extensions.toDownloadItems
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.helpers.DownloadHelper.getNotificationId
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.obj.DownloadStatus import com.github.libretube.obj.DownloadStatus
import com.github.libretube.receivers.NotificationReceiver import com.github.libretube.receivers.NotificationReceiver
import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_PAUSE import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_PAUSE
import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_RESUME import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_RESUME
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.util.DownloadHelper
import com.github.libretube.util.DownloadHelper.getNotificationId
import com.github.libretube.util.ImageHelper
import java.io.File import java.io.File
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.SocketTimeoutException import java.net.SocketTimeoutException

View File

@ -11,7 +11,7 @@ import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.widget.Toast import android.widget.Toast
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.util.DownloadHelper import com.github.libretube.helpers.DownloadHelper
import java.io.File import java.io.File
class UpdateService : Service() { class UpdateService : Service() {

View File

@ -1,10 +1,6 @@
package com.github.libretube.ui.activities package com.github.libretube.ui.activities
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
@ -16,7 +12,10 @@ import com.github.libretube.constants.PIPED_GITHUB_URL
import com.github.libretube.constants.WEBLATE_URL import com.github.libretube.constants.WEBLATE_URL
import com.github.libretube.constants.WEBSITE_URL import com.github.libretube.constants.WEBSITE_URL
import com.github.libretube.databinding.ActivityAboutBinding import com.github.libretube.databinding.ActivityAboutBinding
import com.github.libretube.helpers.ClipboardHelper
import com.github.libretube.helpers.IntentHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.google.android.material.card.MaterialCardView
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -34,7 +33,7 @@ class AboutActivity : BaseActivity() {
} }
binding.appIcon.setOnClickListener { binding.appIcon.setOnClickListener {
val sendIntent: Intent = Intent().apply { val sendIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, GITHUB_URL) putExtra(Intent.EXTRA_TEXT, GITHUB_URL)
type = "text/plain" type = "text/plain"
@ -44,37 +43,10 @@ class AboutActivity : BaseActivity() {
startActivity(shareIntent) startActivity(shareIntent)
} }
binding.website.setOnClickListener { setupCard(binding.website, WEBSITE_URL)
openLinkFromHref(WEBSITE_URL) setupCard(binding.piped, PIPED_GITHUB_URL)
} setupCard(binding.translate, WEBLATE_URL)
binding.website.setOnLongClickListener { setupCard(binding.github, GITHUB_URL)
onLongClick(WEBSITE_URL)
true
}
binding.piped.setOnClickListener {
openLinkFromHref(PIPED_GITHUB_URL)
}
binding.piped.setOnLongClickListener {
onLongClick(PIPED_GITHUB_URL)
true
}
binding.translate.setOnClickListener {
openLinkFromHref(WEBLATE_URL)
}
binding.translate.setOnLongClickListener {
onLongClick(WEBLATE_URL)
true
}
binding.github.setOnClickListener {
openLinkFromHref(GITHUB_URL)
}
binding.github.setOnLongClickListener {
onLongClick(GITHUB_URL)
true
}
binding.license.setOnClickListener { binding.license.setOnClickListener {
showLicense() showLicense()
@ -89,18 +61,19 @@ class AboutActivity : BaseActivity() {
} }
} }
private fun openLinkFromHref(link: String) { private fun setupCard(card: MaterialCardView, link: String) {
val uri = Uri.parse(link) card.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW).setData(uri) IntentHelper.openLinkFromHref(this, link)
startActivity(intent) }
card.setOnLongClickListener {
onLongClick(link)
true
}
} }
private fun onLongClick(href: String) { private fun onLongClick(href: String) {
// copy the link to the clipboard // copy the link to the clipboard
val clipboard: ClipboardManager = ClipboardHelper.save(this, href)
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.copied), href)
clipboard.setPrimaryClip(clip)
// show the snackBar with open action // show the snackBar with open action
Snackbar.make( Snackbar.make(
binding.root, binding.root,
@ -108,7 +81,7 @@ class AboutActivity : BaseActivity() {
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG
) )
.setAction(R.string.open_copied) { .setAction(R.string.open_copied) {
openLinkFromHref(href) IntentHelper.openLinkFromHref(this, href)
} }
.setAnimationMode(Snackbar.ANIMATION_MODE_FADE) .setAnimationMode(Snackbar.ANIMATION_MODE_FADE)
.show() .show()

View File

@ -1,16 +1,16 @@
package com.github.libretube.ui.activities package com.github.libretube.ui.activities
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import com.github.libretube.constants.DISCORD_URL import com.github.libretube.constants.DISCORD_URL
import com.github.libretube.constants.FAQ_URL import com.github.libretube.constants.FAQ_URL
import com.github.libretube.constants.MASTODON_URL
import com.github.libretube.constants.MATRIX_URL import com.github.libretube.constants.MATRIX_URL
import com.github.libretube.constants.REDDIT_URL import com.github.libretube.constants.REDDIT_URL
import com.github.libretube.constants.TELEGRAM_URL import com.github.libretube.constants.TELEGRAM_URL
import com.github.libretube.constants.TWITTER_URL
import com.github.libretube.databinding.ActivityHelpBinding import com.github.libretube.databinding.ActivityHelpBinding
import com.github.libretube.helpers.IntentHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.google.android.material.card.MaterialCardView
class HelpActivity : BaseActivity() { class HelpActivity : BaseActivity() {
private lateinit var binding: ActivityHelpBinding private lateinit var binding: ActivityHelpBinding
@ -25,34 +25,17 @@ class HelpActivity : BaseActivity() {
onBackPressedDispatcher.onBackPressed() onBackPressedDispatcher.onBackPressed()
} }
binding.faq.setOnClickListener { setupCard(binding.faq, FAQ_URL)
openLinkFromHref(FAQ_URL) setupCard(binding.matrix, MATRIX_URL)
setupCard(binding.mastodon, MASTODON_URL)
setupCard(binding.telegram, TELEGRAM_URL)
setupCard(binding.discord, DISCORD_URL)
setupCard(binding.reddit, REDDIT_URL)
} }
binding.matrix.setOnClickListener { private fun setupCard(card: MaterialCardView, link: String) {
openLinkFromHref(MATRIX_URL) card.setOnClickListener {
} IntentHelper.openLinkFromHref(this, link)
binding.telegram.setOnClickListener {
openLinkFromHref(TELEGRAM_URL)
}
binding.discord.setOnClickListener {
openLinkFromHref(DISCORD_URL)
}
binding.reddit.setOnClickListener {
openLinkFromHref(REDDIT_URL)
}
binding.twitter.setOnClickListener {
openLinkFromHref(TWITTER_URL)
} }
} }
private fun openLinkFromHref(link: String) {
val uri = Uri.parse(link)
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
startActivity(intent)
}
} }

View File

@ -27,6 +27,12 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.ActivityMainBinding import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.NetworkHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.helpers.WindowHelper
import com.github.libretube.services.ClosingService import com.github.libretube.services.ClosingService
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.dialogs.ErrorDialog import com.github.libretube.ui.dialogs.ErrorDialog
@ -36,12 +42,6 @@ import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.models.SearchViewModel import com.github.libretube.ui.models.SearchViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.tools.SleepTimer import com.github.libretube.ui.tools.SleepTimer
import com.github.libretube.util.NavBarHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.NetworkHelper
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.ThemeHelper
import com.github.libretube.util.WindowHelper
import com.google.android.material.elevation.SurfaceColors import com.google.android.material.elevation.SurfaceColors
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {

View File

@ -4,12 +4,13 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.fragment.app.replace
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ActivityNointernetBinding import com.github.libretube.databinding.ActivityNointernetBinding
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.NetworkHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.fragments.DownloadsFragment import com.github.libretube.ui.fragments.DownloadsFragment
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.NetworkHelper
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class NoInternetActivity : BaseActivity() { class NoInternetActivity : BaseActivity() {
@ -33,10 +34,10 @@ class NoInternetActivity : BaseActivity() {
} }
binding.downloads.setOnClickListener { binding.downloads.setOnClickListener {
supportFragmentManager.beginTransaction() supportFragmentManager.commit {
.replace(R.id.noInternet_container, DownloadsFragment()) replace<DownloadsFragment>(R.id.noInternet_container)
.addToBackStack(null) addToBackStack(null)
.commit() }
} }
setContentView(binding.root) setContentView(binding.root)

View File

@ -16,12 +16,12 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.enums.FileType import com.github.libretube.enums.FileType
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.helpers.WindowHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setAspectRatio import com.github.libretube.ui.extensions.setAspectRatio
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
import com.github.libretube.util.WindowHelper
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem

View File

@ -7,8 +7,8 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
class RouterActivity : BaseActivity() { class RouterActivity : BaseActivity() {

View File

@ -24,10 +24,9 @@ class SettingsActivity : BaseActivity() {
} }
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager supportFragmentManager.commit {
.beginTransaction() replace<MainSettings>(R.id.settings)
.replace(R.id.settings, MainSettings()) }
.commit()
} }
// new way of dealing with back presses instead of onBackPressed() // new way of dealing with back presses instead of onBackPressed()

View File

@ -1,11 +1,11 @@
package com.github.libretube.ui.adapters package com.github.libretube.ui.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.BottomSheetItemBinding import com.github.libretube.databinding.BottomSheetItemBinding
import com.github.libretube.obj.BottomSheetItem import com.github.libretube.obj.BottomSheetItem
import com.github.libretube.ui.extensions.setDrawables
import com.github.libretube.ui.viewholders.BottomSheetViewHolder import com.github.libretube.ui.viewholders.BottomSheetViewHolder
class BottomSheetAdapter( class BottomSheetAdapter(
@ -23,17 +23,12 @@ class BottomSheetAdapter(
override fun onBindViewHolder(holder: BottomSheetViewHolder, position: Int) { override fun onBindViewHolder(holder: BottomSheetViewHolder, position: Int) {
val item = items[position] val item = items[position]
holder.binding.apply { holder.binding.root.apply {
val current = item.getCurrent() val current = item.getCurrent()
title.text = text = if (current != null) "${item.title} ($current)" else item.title
if (current != null) "${item.title} ($current)" else item.title setDrawables(start = item.drawable)
if (item.drawable != null) {
drawable.setImageResource(item.drawable)
} else {
drawable.visibility = View.GONE
}
root.setOnClickListener { setOnClickListener {
item.onClick.invoke() item.onClick.invoke()
listener.invoke(position) listener.invoke(position)
} }

View File

@ -7,9 +7,9 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.api.obj.ChapterSegment import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.databinding.ChapterColumnBinding import com.github.libretube.databinding.ChapterColumnBinding
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.viewholders.ChaptersViewHolder import com.github.libretube.ui.viewholders.ChaptersViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.ThemeHelper
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
class ChaptersAdapter( class ChaptersAdapter(

View File

@ -1,17 +1,19 @@
package com.github.libretube.ui.adapters package com.github.libretube.ui.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.os.bundleOf
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.replace
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.JsonHelper import com.github.libretube.api.JsonHelper
@ -19,13 +21,14 @@ import com.github.libretube.api.obj.Comment
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.CommentsRowBinding import com.github.libretube.databinding.CommentsRowBinding
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.helpers.ClipboardHelper
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.fragments.CommentsRepliesFragment import com.github.libretube.ui.fragments.CommentsRepliesFragment
import com.github.libretube.ui.viewholders.CommentsViewHolder import com.github.libretube.ui.viewholders.CommentsViewHolder
import com.github.libretube.util.ClipboardHelper
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import com.github.libretube.util.ThemeHelper import kotlinx.serialization.encodeToString
class CommentsAdapter( class CommentsAdapter(
private val fragment: Fragment?, private val fragment: Fragment?,
@ -65,7 +68,7 @@ class CommentsAdapter(
if (comment.verified) verifiedImageView.visibility = View.VISIBLE if (comment.verified) verifiedImageView.visibility = View.VISIBLE
if (comment.pinned) pinnedImageView.visibility = View.VISIBLE if (comment.pinned) pinnedImageView.visibility = View.VISIBLE
if (comment.hearted) heartedImageView.visibility = View.VISIBLE if (comment.hearted) heartedImageView.visibility = View.VISIBLE
if (comment.repliesPage != null) repliesAvailable.visibility = View.VISIBLE if (comment.repliesPage != null) repliesCount.visibility = View.VISIBLE
if (comment.replyCount > 0L) { if (comment.replyCount > 0L) {
repliesCount.text = comment.replyCount.formatShort() repliesCount.text = comment.replyCount.formatShort()
} }
@ -77,7 +80,6 @@ class CommentsAdapter(
if (isRepliesAdapter) { if (isRepliesAdapter) {
repliesCount.visibility = View.GONE repliesCount.visibility = View.GONE
repliesAvailable.visibility = View.GONE
// highlight the comment that is being replied to // highlight the comment that is being replied to
if (comment == comments.firstOrNull()) { if (comment == comments.firstOrNull()) {
@ -87,31 +89,28 @@ class CommentsAdapter(
root.updatePadding(top = 20) root.updatePadding(top = 20)
root.updateLayoutParams<MarginLayoutParams> { bottomMargin = 20 } root.updateLayoutParams<MarginLayoutParams> { bottomMargin = 20 }
} else { } else {
root.background = AppCompatResources.getDrawable(root.context, R.drawable.rounded_ripple) root.background = AppCompatResources.getDrawable(
root.context,
R.drawable.rounded_ripple
)
} }
} }
if (!isRepliesAdapter && comment.repliesPage != null) { if (!isRepliesAdapter && comment.repliesPage != null) {
val repliesFragment = CommentsRepliesFragment().apply {
arguments = Bundle().apply {
putString(IntentData.videoId, videoId)
putString(
IntentData.comment,
JsonHelper.json.encodeToString(Comment.serializer(), comment)
)
}
}
root.setOnClickListener { root.setOnClickListener {
fragment!!.parentFragmentManager val args = bundleOf(
.beginTransaction() IntentData.videoId to videoId,
.replace(R.id.commentFragContainer, repliesFragment) IntentData.comment to JsonHelper.json.encodeToString(comment)
.addToBackStack(null) )
.commit() fragment!!.parentFragmentManager.commit {
replace<CommentsRepliesFragment>(R.id.commentFragContainer, args = args)
addToBackStack(null)
}
} }
} }
root.setOnLongClickListener { root.setOnLongClickListener {
ClipboardHelper(root.context).save(comment.commentText ?: "") ClipboardHelper.save(root.context, comment.commentText ?: "")
Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show() Toast.makeText(root.context, R.string.copied, Toast.LENGTH_SHORT).show()
true true
} }

View File

@ -14,9 +14,9 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.DownloadWithItems import com.github.libretube.db.obj.DownloadWithItems
import com.github.libretube.extensions.formatAsFileSize import com.github.libretube.extensions.formatAsFileSize
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.ui.activities.OfflinePlayerActivity import com.github.libretube.ui.activities.OfflinePlayerActivity
import com.github.libretube.ui.viewholders.DownloadsViewHolder import com.github.libretube.ui.viewholders.DownloadsViewHolder
import com.github.libretube.util.ImageHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File import java.io.File

View File

@ -8,9 +8,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.AppIconItemBinding import com.github.libretube.databinding.AppIconItemBinding
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.viewholders.IconsSheetViewHolder import com.github.libretube.ui.viewholders.IconsSheetViewHolder
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.ThemeHelper
class IconsSheetAdapter : RecyclerView.Adapter<IconsSheetViewHolder>() { class IconsSheetAdapter : RecyclerView.Adapter<IconsSheetViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IconsSheetViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IconsSheetViewHolder {

View File

@ -5,11 +5,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.LegacySubscriptionChannelBinding import com.github.libretube.databinding.LegacySubscriptionChannelBinding
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
import com.github.libretube.ui.viewholders.LegacySubscriptionViewHolder import com.github.libretube.ui.viewholders.LegacySubscriptionViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class LegacySubscriptionAdapter( class LegacySubscriptionAdapter(
private val subscriptions: List<com.github.libretube.api.obj.Subscription> private val subscriptions: List<com.github.libretube.api.obj.Subscription>

View File

@ -8,10 +8,10 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.QueueRowBinding import com.github.libretube.databinding.QueueRowBinding
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.viewholders.PlayingQueueViewHolder import com.github.libretube.ui.viewholders.PlayingQueueViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.ThemeHelper
class PlayingQueueAdapter : RecyclerView.Adapter<PlayingQueueViewHolder>() { class PlayingQueueAdapter : RecyclerView.Adapter<PlayingQueueViewHolder>() {

View File

@ -13,13 +13,13 @@ import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.extensions.setWatchProgressLength
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.PlaylistViewHolder import com.github.libretube.ui.viewholders.PlaylistViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import java.io.IOException import java.io.IOException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -84,9 +84,7 @@ class PlaylistAdapter(
if (!streamItem.uploaderUrl.isNullOrBlank()) { if (!streamItem.uploaderUrl.isNullOrBlank()) {
channelContainer.setOnClickListener { channelContainer.setOnClickListener {
streamItem.uploaderUrl?.toID()?.let { NavigationHelper.navigateChannel(root.context, streamItem.uploaderUrl.toID())
NavigationHelper.navigateChannel(root.context, it)
}
} }
} }
@ -96,7 +94,7 @@ class PlaylistAdapter(
removeFromPlaylist(root.context, position) removeFromPlaylist(root.context, position)
} }
} }
watchProgress.setWatchProgressLength(videoId, streamItem.duration!!) watchProgress.setWatchProgressLength(videoId, streamItem.duration)
} }
} }

View File

@ -12,10 +12,10 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.PlaylistBookmark import com.github.libretube.db.obj.PlaylistBookmark
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.ui.viewholders.PlaylistBookmarkViewHolder import com.github.libretube.ui.viewholders.PlaylistBookmarkViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class PlaylistBookmarkAdapter( class PlaylistBookmarkAdapter(
private val bookmarks: List<PlaylistBookmark>, private val bookmarks: List<PlaylistBookmark>,

View File

@ -7,12 +7,12 @@ import com.github.libretube.R
import com.github.libretube.api.obj.Playlists import com.github.libretube.api.obj.Playlists
import com.github.libretube.databinding.PlaylistsRowBinding import com.github.libretube.databinding.PlaylistsRowBinding
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.dialogs.DeletePlaylistDialog import com.github.libretube.ui.dialogs.DeletePlaylistDialog
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.ui.viewholders.PlaylistsViewHolder import com.github.libretube.ui.viewholders.PlaylistsViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class PlaylistsAdapter( class PlaylistsAdapter(
private val playlists: MutableList<Playlists>, private val playlists: MutableList<Playlists>,

View File

@ -4,7 +4,8 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.obj.ContentItem import com.github.libretube.api.obj.ContentItem
import com.github.libretube.databinding.ChannelRowBinding import com.github.libretube.databinding.ChannelRowBinding
@ -13,6 +14,8 @@ import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.extensions.setWatchProgressLength
@ -21,25 +24,9 @@ import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.SearchViewHolder import com.github.libretube.ui.viewholders.SearchViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
class SearchAdapter( class SearchAdapter : ListAdapter<ContentItem, SearchViewHolder>(SearchCallback) {
private val searchItems: MutableList<ContentItem>
) :
RecyclerView.Adapter<SearchViewHolder>() {
fun updateItems(newItems: List<ContentItem>) {
val searchItemsSize = searchItems.size
searchItems.addAll(newItems)
notifyItemRangeInserted(searchItemsSize, newItems.size)
}
override fun getItemCount(): Int {
return searchItems.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
@ -58,7 +45,7 @@ class SearchAdapter(
} }
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) { override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
val searchItem = searchItems[position] val searchItem = currentList[position]
val videoRowBinding = holder.videoRowBinding val videoRowBinding = holder.videoRowBinding
val channelRowBinding = holder.channelRowBinding val channelRowBinding = holder.channelRowBinding
@ -68,11 +55,13 @@ class SearchAdapter(
bindWatch(searchItem, videoRowBinding) bindWatch(searchItem, videoRowBinding)
} else if (channelRowBinding != null) { } else if (channelRowBinding != null) {
bindChannel(searchItem, channelRowBinding) bindChannel(searchItem, channelRowBinding)
} else if (playlistRowBinding != null) bindPlaylist(searchItem, playlistRowBinding) } else if (playlistRowBinding != null) {
bindPlaylist(searchItem, playlistRowBinding)
}
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return when (searchItems[position].type) { return when (currentList[position].type) {
"stream" -> 0 "stream" -> 0
"channel" -> 1 "channel" -> 1
"playlist" -> 2 "playlist" -> 2
@ -116,10 +105,7 @@ class SearchAdapter(
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun bindChannel( private fun bindChannel(item: ContentItem, binding: ChannelRowBinding) {
item: ContentItem,
binding: ChannelRowBinding
) {
binding.apply { binding.apply {
ImageHelper.loadImage(item.thumbnail, searchChannelImage) ImageHelper.loadImage(item.thumbnail, searchChannelImage)
searchChannelName.text = item.name searchChannelName.text = item.name
@ -144,10 +130,7 @@ class SearchAdapter(
} }
} }
private fun bindPlaylist( private fun bindPlaylist(item: ContentItem, binding: PlaylistsRowBinding) {
item: ContentItem,
binding: PlaylistsRowBinding
) {
binding.apply { binding.apply {
ImageHelper.loadImage(item.thumbnail, playlistThumbnail) ImageHelper.loadImage(item.thumbnail, playlistThumbnail)
if (item.videos != -1L) videoCount.text = item.videos.toString() if (item.videos != -1L) videoCount.text = item.videos.toString()
@ -169,4 +152,14 @@ class SearchAdapter(
} }
} }
} }
private object SearchCallback : DiffUtil.ItemCallback<ContentItem>() {
override fun areItemsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean {
return true
}
}
} }

View File

@ -6,12 +6,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.api.obj.Subscription import com.github.libretube.api.obj.Subscription
import com.github.libretube.databinding.ChannelSubscriptionRowBinding import com.github.libretube.databinding.ChannelSubscriptionRowBinding
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet
import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class SubscriptionChannelAdapter( class SubscriptionChannelAdapter(
private val subscriptions: MutableList<Subscription> private val subscriptions: MutableList<Subscription>

View File

@ -20,14 +20,14 @@ import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.extensions.setWatchProgressLength
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.VideosViewHolder import com.github.libretube.ui.viewholders.VideosViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
class VideosAdapter( class VideosAdapter(

View File

@ -8,13 +8,13 @@ import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setFormattedDuration
import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.extensions.setWatchProgressLength
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.ui.viewholders.WatchHistoryViewHolder import com.github.libretube.ui.viewholders.WatchHistoryViewHolder
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
class WatchHistoryAdapter( class WatchHistoryAdapter(
private val watchHistory: MutableList<WatchHistoryItem> private val watchHistory: MutableList<WatchHistoryItem>

View File

@ -2,8 +2,8 @@ package com.github.libretube.ui.base
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.github.libretube.util.LocaleHelper import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.util.ThemeHelper import com.github.libretube.helpers.ThemeHelper
/** /**
* Activity that applies the LibreTube theme and the in-app language * Activity that applies the LibreTube theme and the in-app language

View File

@ -7,8 +7,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.DialogTextPreferenceBinding import com.github.libretube.databinding.DialogTextPreferenceBinding
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.SettingsActivity import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
/** /**

View File

@ -7,9 +7,9 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.BackupFile import com.github.libretube.obj.BackupFile
import com.github.libretube.obj.PreferenceItem import com.github.libretube.obj.PreferenceItem
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -7,6 +7,7 @@ import androidx.fragment.app.DialogFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.PlaylistsHelper
import com.github.libretube.databinding.DialogCreatePlaylistBinding import com.github.libretube.databinding.DialogCreatePlaylistBinding
import com.github.libretube.extensions.toastFromMainThread
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -24,8 +25,19 @@ class CreatePlaylistDialog(
binding.clonePlaylist.setOnClickListener { binding.clonePlaylist.setOnClickListener {
val playlistUrl = binding.playlistUrl.text.toString().toHttpUrlOrNull() val playlistUrl = binding.playlistUrl.text.toString().toHttpUrlOrNull()
val appContext = context?.applicationContext
playlistUrl?.queryParameter("list")?.let { playlistUrl?.queryParameter("list")?.let {
PlaylistsHelper.clonePlaylist(requireContext(), it) CoroutineScope(Dispatchers.IO).launch {
val playlistId = PlaylistsHelper.clonePlaylist(requireContext(), it)?.also {
withContext(Dispatchers.Main) {
onSuccess.invoke()
}
}
appContext?.toastFromMainThread(
if (playlistId != null) R.string.playlistCloned else R.string.server_error
)
}
dismiss() dismiss()
} ?: run { } ?: run {
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show()

View File

@ -12,7 +12,7 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.DeleteUserRequest import com.github.libretube.api.obj.DeleteUserRequest
import com.github.libretube.databinding.DialogDeleteAccountBinding import com.github.libretube.databinding.DialogDeleteAccountBinding
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class DeleteAccountDialog : DialogFragment() { class DeleteAccountDialog : DialogFragment() {

View File

@ -4,16 +4,15 @@ import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.PlaylistId import com.github.libretube.api.obj.PlaylistId
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -28,26 +27,24 @@ class DeletePlaylistDialog(
.setTitle(R.string.deletePlaylist) .setTitle(R.string.deletePlaylist)
.setMessage(R.string.areYouSure) .setMessage(R.string.areYouSure)
.setPositiveButton(R.string.yes) { _, _ -> .setPositiveButton(R.string.yes) { _, _ ->
PreferenceHelper.getToken() lifecycleScope.launch(Dispatchers.IO) {
deletePlaylist() deletePlaylist()
} }
}
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
} }
private fun deletePlaylist() { private suspend fun deletePlaylist() {
if (playlistType == PlaylistType.LOCAL) { if (playlistType == PlaylistType.LOCAL) {
awaitQuery {
DatabaseHolder.Database.localPlaylistsDao().deletePlaylistById(playlistId) DatabaseHolder.Database.localPlaylistsDao().deletePlaylistById(playlistId)
DatabaseHolder.Database.localPlaylistsDao().deletePlaylistItemsByPlaylistId( DatabaseHolder.Database.localPlaylistsDao().deletePlaylistItemsByPlaylistId(playlistId)
playlistId withContext(Dispatchers.Main) {
) onSuccess()
} }
onSuccess.invoke()
return return
} }
CoroutineScope(Dispatchers.IO).launch {
val response = try { val response = try {
RetrofitInstance.authApi.deletePlaylist( RetrofitInstance.authApi.deletePlaylist(
PreferenceHelper.getToken(), PreferenceHelper.getToken(),
@ -55,12 +52,12 @@ class DeletePlaylistDialog(
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
return@launch return
} }
try { try {
if (response.message == "ok") { if (response.message == "ok") {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onSuccess.invoke() onSuccess()
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -68,4 +65,3 @@ class DeletePlaylistDialog(
} }
} }
} }
}

View File

@ -15,7 +15,7 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
import com.github.libretube.databinding.DialogDownloadBinding import com.github.libretube.databinding.DialogDownloadBinding
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.util.DownloadHelper import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.IOException import java.io.IOException

View File

@ -6,8 +6,8 @@ import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.util.ClipboardHelper import com.github.libretube.helpers.ClipboardHelper
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class ErrorDialog : DialogFragment() { class ErrorDialog : DialogFragment() {
@ -22,7 +22,7 @@ class ErrorDialog : DialogFragment() {
.setMessage(errorLog) .setMessage(errorLog)
.setNegativeButton(R.string.okay, null) .setNegativeButton(R.string.okay, null)
.setPositiveButton(R.string.copy) { _, _ -> .setPositiveButton(R.string.copy) { _, _ ->
ClipboardHelper(requireContext()).save(errorLog) ClipboardHelper.save(requireContext(), errorLog)
Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show()
} }
.show() .show()

View File

@ -12,7 +12,7 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Login import com.github.libretube.api.obj.Login
import com.github.libretube.databinding.DialogLoginBinding import com.github.libretube.databinding.DialogLoginBinding
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder

View File

@ -8,7 +8,7 @@ import androidx.core.app.ActivityCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.DialogLogoutBinding import com.github.libretube.databinding.DialogLogoutBinding
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class LogoutDialog : DialogFragment() { class LogoutDialog : DialogFragment() {

View File

@ -8,8 +8,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.DialogNavbarOptionsBinding import com.github.libretube.databinding.DialogNavbarOptionsBinding
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.ui.adapters.NavBarOptionsAdapter import com.github.libretube.ui.adapters.NavBarOptionsAdapter
import com.github.libretube.util.NavBarHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class NavBarOptionsDialog : DialogFragment() { class NavBarOptionsDialog : DialogFragment() {

View File

@ -5,7 +5,7 @@ import android.os.Bundle
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.util.NavigationHelper import com.github.libretube.helpers.NavigationHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class RequireRestartDialog : DialogFragment() { class RequireRestartDialog : DialogFragment() {

View File

@ -13,8 +13,8 @@ import com.github.libretube.databinding.DialogShareBinding
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class ShareDialog( class ShareDialog(

View File

@ -0,0 +1,18 @@
package com.github.libretube.ui.extensions
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
fun TextView.setDrawables(
start: Int? = null,
top: Int? = null,
end: Int? = null,
bottom: Int? = null
) {
setCompoundDrawablesRelativeWithIntrinsicBounds(
start?.let { AppCompatResources.getDrawable(context, it) },
top?.let { AppCompatResources.getDrawable(context, it) },
end?.let { AppCompatResources.getDrawable(context, it) },
bottom?.let { AppCompatResources.getDrawable(context, it) }
)
}

View File

@ -1,8 +1,7 @@
package com.github.libretube.ui.extensions package com.github.libretube.ui.extensions
import android.view.View import android.view.View
import android.view.ViewTreeObserver import androidx.constraintlayout.widget.ConstraintLayout
import android.widget.LinearLayout
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
@ -13,8 +12,11 @@ import com.github.libretube.extensions.awaitQuery
* @param duration The duration of the video in seconds * @param duration The duration of the video in seconds
* @return Whether the video is already watched more than 90% * @return Whether the video is already watched more than 90%
*/ */
fun View?.setWatchProgressLength(videoId: String, duration: Long): Boolean { fun View.setWatchProgressLength(videoId: String, duration: Long): Boolean {
val view = this!! updateLayoutParams<ConstraintLayout.LayoutParams> {
matchConstraintPercentWidth = 0f
}
visibility = View.GONE
val progress = try { val progress = try {
awaitQuery { awaitQuery {
@ -26,22 +28,13 @@ fun View?.setWatchProgressLength(videoId: String, duration: Long): Boolean {
?.toFloat()?.div(1000) ?.toFloat()?.div(1000)
if (progress == null || duration == 0L) { if (progress == null || duration == 0L) {
view.visibility = View.GONE
return false return false
} }
view.viewTreeObserver updateLayoutParams<ConstraintLayout.LayoutParams> {
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { matchConstraintPercentWidth = progress / duration.toFloat()
override fun onGlobalLayout() {
this@setWatchProgressLength.viewTreeObserver.removeOnGlobalLayoutListener(this)
val fullWidth = (parent as LinearLayout).width
val newWidth = fullWidth * (progress / duration.toFloat())
view.updateLayoutParams {
width = newWidth.toInt()
} }
view.visibility = View.VISIBLE visibility = View.VISIBLE
}
})
return progress / duration.toFloat() > 0.9 return progress / duration.toFloat() > 0.9
} }

View File

@ -1,7 +1,7 @@
package com.github.libretube.ui.extensions package com.github.libretube.ui.extensions
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
fun MaterialButton.setupNotificationBell(channelId: String) { fun MaterialButton.setupNotificationBell(channelId: String) {

View File

@ -1,5 +1,6 @@
package com.github.libretube.ui.fragments package com.github.libretube.ui.fragments
import android.annotation.SuppressLint
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -17,22 +18,28 @@ import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.databinding.FragmentAudioPlayerBinding import com.github.libretube.databinding.FragmentAudioPlayerBinding
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.normalize
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.AudioHelper
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.services.BackgroundMode import com.github.libretube.services.BackgroundMode
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.interfaces.AudioPlayerOptions
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
import com.github.libretube.ui.sheets.PlaybackOptionsSheet import com.github.libretube.ui.sheets.PlaybackOptionsSheet
import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.ui.sheets.PlayingQueueSheet
import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.sheets.VideoOptionsBottomSheet
import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
class AudioPlayerFragment : BaseFragment() { class AudioPlayerFragment : BaseFragment(), AudioPlayerOptions {
private lateinit var binding: FragmentAudioPlayerBinding private lateinit var binding: FragmentAudioPlayerBinding
private lateinit var audioHelper: AudioHelper
private val onTrackChangeListener: (StreamItem) -> Unit = { private val onTrackChangeListener: (StreamItem) -> Unit = {
updateStreamInfo() updateStreamInfo()
} }
@ -64,6 +71,7 @@ class AudioPlayerFragment : BaseFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
audioHelper = AudioHelper(requireContext())
Intent(activity, BackgroundMode::class.java).also { intent -> Intent(activity, BackgroundMode::class.java).also { intent ->
activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE) activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
} }
@ -78,6 +86,7 @@ class AudioPlayerFragment : BaseFragment() {
return binding.root return binding.root
} }
@SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -129,13 +138,8 @@ class AudioPlayerFragment : BaseFragment() {
).show(childFragmentManager, null) ).show(childFragmentManager, null)
} }
binding.thumbnail.setOnClickListener { val listener = AudioPlayerThumbnailListener(requireContext(), this)
val current = PlayingQueue.getCurrent() binding.thumbnail.setOnTouchListener(listener)
current?.let {
VideoOptionsBottomSheet(it.url!!.toID(), it.title!!)
.show(childFragmentManager)
}
}
// Listen for track changes due to autoplay or the notification // Listen for track changes due to autoplay or the notification
PlayingQueue.addOnTrackChangedListener(onTrackChangeListener) PlayingQueue.addOnTrackChangedListener(onTrackChangeListener)
@ -146,6 +150,11 @@ class AudioPlayerFragment : BaseFragment() {
// load the stream info into the UI // load the stream info into the UI
updateStreamInfo() updateStreamInfo()
// update the currently shown volume
binding.volumeProgressBar.let { bar ->
bar.progress = audioHelper.getVolumeWithScale(bar.max)
}
} }
/** /**
@ -232,4 +241,48 @@ class AudioPlayerFragment : BaseFragment() {
super.onDestroy() super.onDestroy()
} }
override fun onSingleTap() {
if (isPaused) playerService?.play() else playerService?.pause()
}
override fun onLongTap() {
val current = PlayingQueue.getCurrent()
VideoOptionsBottomSheet(current?.url?.toID() ?: return, current.title ?: return)
.show(childFragmentManager)
}
override fun onSwipe(distanceY: Float) {
binding.volumeControls.visibility = View.VISIBLE
updateVolume(distanceY)
}
override fun onSwipeEnd() {
binding.volumeControls.visibility = View.GONE
}
private fun updateVolume(distance: Float) {
val bar = binding.volumeProgressBar
binding.volumeControls.apply {
if (visibility == View.GONE) {
visibility = View.VISIBLE
// Volume could be changed using other mediums, sync progress
// bar with new value.
bar.progress = audioHelper.getVolumeWithScale(bar.max)
}
}
if (bar.progress == 0) {
binding.volumeImageView.setImageResource(
when {
distance > 0 -> R.drawable.ic_volume_up
else -> R.drawable.ic_volume_off
}
)
}
bar.incrementProgressBy(distance.toInt() / 3)
audioHelper.setVolumeWithScale(bar.progress, bar.max)
binding.volumeTextView.text = "${bar.progress.normalize(0, bar.max, 0, 100)}"
}
} }

View File

@ -19,6 +19,7 @@ import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.obj.ChannelTabs import com.github.libretube.obj.ChannelTabs
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.ui.adapters.SearchAdapter import com.github.libretube.ui.adapters.SearchAdapter
@ -26,11 +27,10 @@ import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.util.ImageHelper
import java.io.IOException import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
class ChannelFragment : BaseFragment() { class ChannelFragment : BaseFragment() {
@ -45,8 +45,6 @@ class ChannelFragment : BaseFragment() {
private var onScrollEnd: () -> Unit = {} private var onScrollEnd: () -> Unit = {}
private val scope = CoroutineScope(Dispatchers.IO)
val possibleTabs = listOf( val possibleTabs = listOf(
ChannelTabs.Channels, ChannelTabs.Channels,
ChannelTabs.Playlists, ChannelTabs.Playlists,
@ -227,18 +225,18 @@ class ChannelFragment : BaseFragment() {
} }
private fun loadTab(tab: ChannelTab) { private fun loadTab(tab: ChannelTab) {
scope.launch { lifecycleScope.launch {
val response = try { val response = try {
withContext(Dispatchers.IO) {
RetrofitInstance.api.getChannelTab(tab.data) RetrofitInstance.api.getChannelTab(tab.data)
}
} catch (e: Exception) { } catch (e: Exception) {
return@launch return@launch
} }
val adapter = SearchAdapter(response.content.toMutableList()) val adapter = SearchAdapter()
runOnUiThread {
binding.channelRecView.adapter = adapter binding.channelRecView.adapter = adapter
} adapter.submitList(response.content)
var tabNextPage = response.nextpage var tabNextPage = response.nextpage
onScrollEnd = { onScrollEnd = {
@ -284,18 +282,18 @@ class ChannelFragment : BaseFragment() {
adapter: SearchAdapter, adapter: SearchAdapter,
onNewNextPage: (String?) -> Unit onNewNextPage: (String?) -> Unit
) { ) {
scope.launch { lifecycleScope.launch {
val newContent = try { val newContent = try {
withContext(Dispatchers.IO) {
RetrofitInstance.api.getChannelTab(tab.data, nextPage) RetrofitInstance.api.getChannelTab(tab.data, nextPage)
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), "Exception: $e") Log.e(TAG(), "Exception: $e")
null null
} }
onNewNextPage.invoke(newContent?.nextpage) onNewNextPage(newContent?.nextpage)
runOnUiThread {
newContent?.content?.let { newContent?.content?.let {
adapter.updateItems(it) adapter.submitList(adapter.currentList + it)
}
} }
} }
} }

View File

@ -58,7 +58,7 @@ class CommentsMainFragment : Fragment() {
viewModel.commentsPage.observe(viewLifecycleOwner) { viewModel.commentsPage.observe(viewLifecycleOwner) {
it ?: return@observe it ?: return@observe
binding.progress.visibility = View.GONE binding.progress.visibility = View.GONE
if (it.disabled == true) { if (it.disabled) {
binding.errorTV.visibility = View.VISIBLE binding.errorTV.visibility = View.VISIBLE
return@observe return@observe
} }

View File

@ -19,13 +19,13 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.DownloadWithItems import com.github.libretube.db.obj.DownloadWithItems
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.formatAsFileSize import com.github.libretube.extensions.formatAsFileSize
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.obj.DownloadStatus import com.github.libretube.obj.DownloadStatus
import com.github.libretube.receivers.DownloadReceiver import com.github.libretube.receivers.DownloadReceiver
import com.github.libretube.services.DownloadService import com.github.libretube.services.DownloadService
import com.github.libretube.ui.adapters.DownloadsAdapter import com.github.libretube.ui.adapters.DownloadsAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.viewholders.DownloadsViewHolder import com.github.libretube.ui.viewholders.DownloadsViewHolder
import com.github.libretube.util.DownloadHelper
import java.io.File import java.io.File
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest

View File

@ -18,11 +18,11 @@ import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
import com.github.libretube.ui.adapters.PlaylistsAdapter import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.util.LocaleHelper
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -21,13 +21,13 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
import com.github.libretube.ui.adapters.PlaylistsAdapter import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.dialogs.CreatePlaylistDialog import com.github.libretube.ui.dialogs.CreatePlaylistDialog
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.util.NavBarHelper
import com.github.libretube.util.PreferenceHelper
class LibraryFragment : BaseFragment() { class LibraryFragment : BaseFragment() {

View File

@ -35,6 +35,7 @@ import androidx.core.os.postDelayed
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.fragment.app.commit
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -67,6 +68,13 @@ import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toStreamItem import com.github.libretube.extensions.toStreamItem
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.DashHelper
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.obj.VideoResolution import com.github.libretube.obj.VideoResolution
import com.github.libretube.services.BackgroundMode import com.github.libretube.services.BackgroundMode
@ -81,24 +89,17 @@ import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.extensions.setAspectRatio import com.github.libretube.ui.extensions.setAspectRatio
import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.interfaces.OnlinePlayerOptions import com.github.libretube.ui.interfaces.OnlinePlayerOptions
import com.github.libretube.ui.listeners.SeekbarPreviewListener
import com.github.libretube.ui.models.CommentsViewModel import com.github.libretube.ui.models.CommentsViewModel
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.sheets.CommentsSheet import com.github.libretube.ui.sheets.CommentsSheet
import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.ui.sheets.PlayingQueueSheet
import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.DashHelper
import com.github.libretube.util.DataSaverMode import com.github.libretube.util.DataSaverMode
import com.github.libretube.util.HtmlParser import com.github.libretube.util.HtmlParser
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.LinkHandler import com.github.libretube.util.LinkHandler
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.PlayerHelper.loadPlaybackParams
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.PreferenceHelper
import com.github.libretube.util.SeekbarPreviewListener
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
@ -1510,9 +1511,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
private fun killPlayerFragment() { private fun killPlayerFragment() {
viewModel.isFullscreen.value = false viewModel.isFullscreen.value = false
binding.playerMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd()
mainActivity.supportFragmentManager.beginTransaction() mainActivity.supportFragmentManager.commit {
.remove(this) remove(this@PlayerFragment)
.commit() }
onDestroy() onDestroy()
} }

View File

@ -26,13 +26,13 @@ import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toPlaylistBookmark import com.github.libretube.extensions.toPlaylistBookmark
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.ui.adapters.PlaylistAdapter import com.github.libretube.ui.adapters.PlaylistAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.extensions.serializable import com.github.libretube.ui.extensions.serializable
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils

View File

@ -16,9 +16,11 @@ import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.obj.SearchHistoryItem import com.github.libretube.db.obj.SearchHistoryItem
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.hideKeyboard import com.github.libretube.extensions.hideKeyboard
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.SearchAdapter import com.github.libretube.ui.adapters.SearchAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.util.PreferenceHelper import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException import java.io.IOException
import retrofit2.HttpException import retrofit2.HttpException
@ -85,7 +87,9 @@ class SearchResultFragment : BaseFragment() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
view?.let { context?.hideKeyboard(it) } view?.let { context?.hideKeyboard(it) }
val response = try { val response = try {
withContext(Dispatchers.IO) {
RetrofitInstance.api.getSearchResults(query, apiSearchFilter) RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
}
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG(), "IOException, you might not have internet connection $e") Log.e(TAG(), "IOException, you might not have internet connection $e")
@ -94,11 +98,10 @@ class SearchResultFragment : BaseFragment() {
Log.e(TAG(), "HttpException, unexpected response") Log.e(TAG(), "HttpException, unexpected response")
return@launchWhenCreated return@launchWhenCreated
} }
runOnUiThread { searchAdapter = SearchAdapter()
searchAdapter = SearchAdapter(response.items.toMutableList())
binding.searchRecycler.adapter = searchAdapter binding.searchRecycler.adapter = searchAdapter
searchAdapter.submitList(response.items)
binding.noSearchResult.isVisible = response.items.isEmpty() binding.noSearchResult.isVisible = response.items.isEmpty()
}
nextPage = response.nextpage nextPage = response.nextpage
} }
} }
@ -106,11 +109,13 @@ class SearchResultFragment : BaseFragment() {
private fun fetchNextSearchItems() { private fun fetchNextSearchItems() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
withContext(Dispatchers.IO) {
RetrofitInstance.api.getSearchResultsNextPage( RetrofitInstance.api.getSearchResultsNextPage(
query, query,
apiSearchFilter, apiSearchFilter,
nextPage!! nextPage!!
) )
}
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG(), "IOException, you might not have internet connection") Log.e(TAG(), "IOException, you might not have internet connection")
@ -120,10 +125,8 @@ class SearchResultFragment : BaseFragment() {
return@launchWhenCreated return@launchWhenCreated
} }
nextPage = response.nextpage!! nextPage = response.nextpage!!
kotlin.runCatching {
if (response.items.isNotEmpty()) { if (response.items.isNotEmpty()) {
searchAdapter.updateItems(response.items) searchAdapter.submitList(searchAdapter.currentList + response.items)
}
} }
} }
} }

View File

@ -13,13 +13,13 @@ import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentSubscriptionsBinding import com.github.libretube.databinding.FragmentSubscriptionsBinding
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.LegacySubscriptionAdapter import com.github.libretube.ui.adapters.LegacySubscriptionAdapter
import com.github.libretube.ui.adapters.SubscriptionChannelAdapter import com.github.libretube.ui.adapters.SubscriptionChannelAdapter
import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.util.PreferenceHelper
class SubscriptionsFragment : BaseFragment() { class SubscriptionsFragment : BaseFragment() {
private lateinit var binding: FragmentSubscriptionsBinding private lateinit var binding: FragmentSubscriptionsBinding

View File

@ -12,10 +12,10 @@ import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.databinding.FragmentTrendsBinding import com.github.libretube.databinding.FragmentTrendsBinding
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.ui.activities.SettingsActivity import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.util.LocaleHelper
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import java.io.IOException import java.io.IOException
import retrofit2.HttpException import retrofit2.HttpException

View File

@ -19,12 +19,12 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.query import com.github.libretube.extensions.query
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.ui.adapters.WatchHistoryAdapter import com.github.libretube.ui.adapters.WatchHistoryAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.ProxyHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class WatchHistoryFragment : BaseFragment() { class WatchHistoryFragment : BaseFragment() {

View File

@ -0,0 +1,12 @@
package com.github.libretube.ui.interfaces
interface AudioPlayerOptions {
fun onSingleTap()
fun onLongTap()
fun onSwipe(distanceY: Float)
fun onSwipeEnd()
}

View File

@ -0,0 +1,81 @@
package com.github.libretube.ui.listeners
import android.annotation.SuppressLint
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.core.os.postDelayed
import com.github.libretube.ui.interfaces.AudioPlayerOptions
import kotlin.math.abs
class AudioPlayerThumbnailListener(context: Context, private val listener: AudioPlayerOptions) :
View.OnTouchListener {
private val handler = Handler(Looper.getMainLooper())
private val gestureDetector = GestureDetector(context, GestureListener(), handler)
private var isMoving = false
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP && isMoving) {
isMoving = false
listener.onSwipeEnd()
return false
}
runCatching {
gestureDetector.onTouchEvent(event)
}
return true
}
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean {
if (isMoving) return false
handler.postDelayed(ACTION_INTERVAL, SINGLE_PRESS_TOKEN) {
if (!isMoving) listener.onSingleTap()
}
return true
}
override fun onScroll(
e1: MotionEvent,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
val insideThreshHold = abs(e2.y - e1.y) <= MOVEMENT_THRESHOLD
// If the movement is inside threshold or scroll is horizontal then return false
if (!isMoving && (insideThreshHold || abs(distanceX) > abs(distanceY))) {
return false
}
isMoving = true
listener.onSwipe(distanceY)
return true
}
override fun onLongPress(e: MotionEvent) {
// remove to single press action from the queue
handler.removeCallbacksAndMessages(SINGLE_PRESS_TOKEN)
listener.onLongTap()
}
}
companion object {
private const val MOVEMENT_THRESHOLD = 10
private val ACTION_INTERVAL = ViewConfiguration.getLongPressTimeout().toLong()
private const val SINGLE_PRESS_TOKEN = "singlePress"
}
}

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.ui.listeners
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.res.Configuration import android.content.res.Configuration
@ -12,6 +12,7 @@ import android.view.ScaleGestureDetector
import android.view.View import android.view.View
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.os.postDelayed import androidx.core.os.postDelayed
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.interfaces.PlayerGestureOptions import com.github.libretube.ui.interfaces.PlayerGestureOptions
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel

View File

@ -1,4 +1,4 @@
package com.github.libretube.util package com.github.libretube.ui.listeners
import android.graphics.Bitmap import android.graphics.Bitmap
import android.view.View import android.view.View
@ -9,6 +9,7 @@ import androidx.core.math.MathUtils
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import coil.request.ImageRequest import coil.request.ImageRequest
import com.github.libretube.api.obj.PreviewFrames import com.github.libretube.api.obj.PreviewFrames
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.obj.PreviewFrame import com.github.libretube.obj.PreviewFrame
import com.google.android.exoplayer2.ui.TimeBar import com.google.android.exoplayer2.ui.TimeBar

View File

@ -9,7 +9,7 @@ import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Subscription import com.github.libretube.api.obj.Subscription
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -6,9 +6,9 @@ import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.util.ImageHelper
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class AdvancedSettings : BasePreferenceFragment() { class AdvancedSettings : BasePreferenceFragment() {

View File

@ -7,12 +7,12 @@ import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.IconsSheetAdapter import com.github.libretube.ui.adapters.IconsSheetAdapter
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.ui.dialogs.NavBarOptionsDialog import com.github.libretube.ui.dialogs.NavBarOptionsDialog
import com.github.libretube.ui.dialogs.RequireRestartDialog import com.github.libretube.ui.dialogs.RequireRestartDialog
import com.github.libretube.ui.sheets.IconsBottomSheet import com.github.libretube.ui.sheets.IconsBottomSheet
import com.github.libretube.util.PreferenceHelper
class AppearanceSettings : BasePreferenceFragment() { class AppearanceSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.appearance override val titleResourceId: Int = R.string.appearance

View File

@ -1,79 +1,79 @@
package com.github.libretube.ui.preferences package com.github.libretube.ui.preferences
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.helpers.BackupHelper
import com.github.libretube.helpers.ImportHelper
import com.github.libretube.obj.BackupFile import com.github.libretube.obj.BackupFile
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.ui.dialogs.BackupDialog import com.github.libretube.ui.dialogs.BackupDialog
import com.github.libretube.util.BackupHelper import kotlinx.coroutines.Dispatchers
import com.github.libretube.util.ImportHelper import kotlinx.coroutines.launch
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
class BackupRestoreSettings : BasePreferenceFragment() { class BackupRestoreSettings : BasePreferenceFragment() {
private val backupDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH:mm:ss") private val backupDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH:mm:ss")
private var backupFile = BackupFile()
override val titleResourceId: Int = R.string.backup_restore override val titleResourceId: Int = R.string.backup_restore
// backup and restore database // backup and restore database
private lateinit var getBackupFile: ActivityResultLauncher<String> private val getBackupFile = registerForActivityResult(ActivityResultContracts.GetContent()) {
private lateinit var createBackupFile: ActivityResultLauncher<String> it?.let {
private var backupFile = BackupFile() lifecycleScope.launch(Dispatchers.IO) {
BackupHelper.restoreAdvancedBackup(requireContext(), it)
}
}
}
private val createBackupFile = registerForActivityResult(CreateDocument(JSON)) {
it?.let {
lifecycleScope.launch(Dispatchers.IO) {
BackupHelper.createAdvancedBackup(requireContext(), it, backupFile)
}
}
}
/** /**
* result listeners for importing and exporting subscriptions * result listeners for importing and exporting subscriptions
*/ */
private lateinit var getSubscriptionsFile: ActivityResultLauncher<String> private val getSubscriptionsFile = registerForActivityResult(
private lateinit var createSubscriptionsFile: ActivityResultLauncher<String> ActivityResultContracts.GetContent()
) {
it?.let {
lifecycleScope.launch(Dispatchers.IO) {
ImportHelper.importSubscriptions(requireActivity(), it)
}
}
}
private val createSubscriptionsFile = registerForActivityResult(CreateDocument(JSON)) {
it?.let {
lifecycleScope.launch(Dispatchers.IO) {
ImportHelper.exportSubscriptions(requireActivity(), it)
}
}
}
/** /**
* result listeners for importing and exporting playlists * result listeners for importing and exporting playlists
*/ */
private lateinit var getPlaylistsFile: ActivityResultLauncher<String> private val getPlaylistsFile = registerForActivityResult(ActivityResultContracts.GetContent()) {
private lateinit var createPlaylistsFile: ActivityResultLauncher<String> it?.let {
lifecycleScope.launch(Dispatchers.IO) {
override fun onCreate(savedInstanceState: Bundle?) { ImportHelper.importPlaylists(requireActivity(), it)
getSubscriptionsFile =
registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri ->
ImportHelper(requireActivity()).importSubscriptions(uri)
} }
createSubscriptionsFile = registerForActivityResult(
CreateDocument("application/json")
) { uri ->
ImportHelper(requireActivity()).exportSubscriptions(uri)
} }
getPlaylistsFile = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
ImportHelper(requireActivity()).importPlaylists(uri)
} }
private val createPlaylistsFile = registerForActivityResult(CreateDocument(JSON)) {
createPlaylistsFile = registerForActivityResult( it?.let {
CreateDocument("application/json") lifecycleScope.launch(Dispatchers.IO) {
) { uri -> ImportHelper.exportPlaylists(requireActivity(), it)
ImportHelper(requireActivity()).exportPlaylists(uri)
} }
getBackupFile =
registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri: Uri? ->
BackupHelper(requireContext()).restoreAdvancedBackup(uri)
} }
createBackupFile = registerForActivityResult(
CreateDocument("application/json")
) { uri: Uri? ->
BackupHelper(requireContext()).createAdvancedBackup(uri, backupFile)
}
super.onCreate(savedInstanceState)
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -116,8 +116,12 @@ class BackupRestoreSettings : BasePreferenceFragment() {
val restoreAdvancedBackup = findPreference<Preference>("restore") val restoreAdvancedBackup = findPreference<Preference>("restore")
restoreAdvancedBackup?.setOnPreferenceClickListener { restoreAdvancedBackup?.setOnPreferenceClickListener {
getBackupFile.launch("application/json") getBackupFile.launch(JSON)
true true
} }
} }
companion object {
private const val JSON = "application/json"
}
} }

View File

@ -7,10 +7,10 @@ import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.ui.dialogs.RequireRestartDialog import com.github.libretube.ui.dialogs.RequireRestartDialog
import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.PreferenceHelper
class GeneralSettings : BasePreferenceFragment() { class GeneralSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.general override val titleResourceId: Int = R.string.general

View File

@ -15,12 +15,12 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.ui.dialogs.CustomInstanceDialog import com.github.libretube.ui.dialogs.CustomInstanceDialog
import com.github.libretube.ui.dialogs.DeleteAccountDialog import com.github.libretube.ui.dialogs.DeleteAccountDialog
import com.github.libretube.ui.dialogs.LoginDialog import com.github.libretube.ui.dialogs.LoginDialog
import com.github.libretube.ui.dialogs.LogoutDialog import com.github.libretube.ui.dialogs.LogoutDialog
import com.github.libretube.util.PreferenceHelper
class InstanceSettings : BasePreferenceFragment() { class InstanceSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.instance override val titleResourceId: Int = R.string.instance

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commitNow
import androidx.preference.Preference import androidx.preference.Preference
import com.github.libretube.BuildConfig import com.github.libretube.BuildConfig
import com.github.libretube.R import com.github.libretube.R
@ -124,9 +125,9 @@ class MainSettings : BasePreferenceFragment() {
} }
private fun navigateToSettingsFragment(newFragment: Fragment): Boolean { private fun navigateToSettingsFragment(newFragment: Fragment): Boolean {
parentFragmentManager.beginTransaction() parentFragmentManager.commitNow {
.replace(R.id.settings, newFragment) replace(R.id.settings, newFragment)
.commitNow() }
return true return true
} }
} }

View File

@ -6,9 +6,9 @@ import androidx.preference.SwitchPreferenceCompat
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.NotificationHelper
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.ui.views.TimePickerPreference import com.github.libretube.ui.views.TimePickerPreference
import com.github.libretube.util.NotificationHelper
class NotificationSettings : BasePreferenceFragment() { class NotificationSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.notifications override val titleResourceId: Int = R.string.notifications

View File

@ -12,9 +12,9 @@ import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.LocaleHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.base.BasePreferenceFragment
import com.github.libretube.util.LocaleHelper
import com.github.libretube.util.PreferenceHelper
class PlayerSettings : BasePreferenceFragment() { class PlayerSettings : BasePreferenceFragment() {
override val titleResourceId: Int = R.string.player override val titleResourceId: Int = R.string.player

View File

@ -7,10 +7,10 @@ import com.github.libretube.api.RetrofitInstance
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.util.BackgroundHelper
import com.github.libretube.util.NavigationHelper
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
/** /**

View File

@ -7,7 +7,7 @@ import android.view.ViewGroup
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.PlaybackBottomSheetBinding import com.github.libretube.databinding.PlaybackBottomSheetBinding
import com.github.libretube.extensions.round import com.github.libretube.extensions.round
import com.github.libretube.util.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.PlaybackParameters import com.google.android.exoplayer2.PlaybackParameters

Some files were not shown because too many files have changed in this diff Show More