diff --git a/README.md b/README.md index 2fb35c46c..b8127dd49 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@
LibreTube -[![GPL-v3](https://libre-tube.github.io/images/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) -[![Telegram](https://libre-tube.github.io/images/tg-widget.svg)](https://t.me/libretube) -[![Twitter](https://libre-tube.github.io/images/tw-widget.svg)](https://twitter.com/libretube) -[![Reddit](https://libre-tube.github.io/images/rd-widget.svg)](https://www.reddit.com/r/Libretube/) -[![Discord](https://libre-tube.github.io/images/discord-widget.svg)](https://discord.gg/Qc34xCj2GV) +[![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/assets/widgets/mat-widget.svg)](https://matrix.to/#/#LibreTube:matrix.org) +[![Mastodon](https://libre-tube.github.io/assets/widgets/mast-widget.svg)](https://fosstodon.org/@libretube) +[![Telegram](https://libre-tube.github.io/assets/widgets/tg-widget.svg)](https://t.me/libretube) +[![Reddit](https://libre-tube.github.io/assets/widgets/rd-widget.svg)](https://www.reddit.com/r/Libretube/) +[![Discord](https://libre-tube.github.io/assets/widgets/discord-widget.svg)](https://discord.gg/Qc34xCj2GV)
-[Get it on F-Droid](https://f-droid.org/en/packages/com.github.libretube/) -[Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)
-[Get it on GitHub](https://github.com/libre-tube/LibreTube/releases/latest) +[Get it on F-Droid](https://f-droid.org/en/packages/com.github.libretube/) +[Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.github.libretube)
+[Get it on GitHub](https://github.com/libre-tube/LibreTube/releases/latest)
diff --git a/app/build.gradle b/app/build.gradle index c931cc13d..ff5d81a1a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,8 +46,6 @@ android { } debug { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' debuggable true applicationIdSuffix ".debug" resValue "string", "app_name", "LibreTube Debug" @@ -127,4 +125,4 @@ dependencies { static def getUnixTime() { return Instant.now().getEpochSecond() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/github/libretube/LibreTubeApp.kt b/app/src/main/java/com/github/libretube/LibreTubeApp.kt index 791d5771d..282d1f6c1 100644 --- a/app/src/main/java/com/github/libretube/LibreTubeApp.kt +++ b/app/src/main/java/com/github/libretube/LibreTubeApp.kt @@ -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.PUSH_CHANNEL_ID 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.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() { override fun onCreate() { diff --git a/app/src/main/java/com/github/libretube/api/PipedApi.kt b/app/src/main/java/com/github/libretube/api/PipedApi.kt index 98e032389..c8fb8fdf7 100644 --- a/app/src/main/java/com/github/libretube/api/PipedApi.kt +++ b/app/src/main/java/com/github/libretube/api/PipedApi.kt @@ -109,7 +109,14 @@ interface PipedApi { suspend fun getFeed(@Query("authToken") token: String?): List @GET("feed/unauthenticated") - suspend fun getUnauthenticatedFeed(@Query("channels") channels: String): List + suspend fun getUnauthenticatedFeed( + @Query("channels") channels: String + ): List + + @POST("feed/unauthenticated") + suspend fun getUnauthenticatedFeed( + @Body channels: List + ): List @GET("subscribed") suspend fun isSubscribed( @@ -121,7 +128,14 @@ interface PipedApi { suspend fun subscriptions(@Header("Authorization") token: String): List @GET("subscriptions/unauthenticated") - suspend fun unauthenticatedSubscriptions(@Query("channels") channels: String): List + suspend fun unauthenticatedSubscriptions( + @Query("channels") channels: String + ): List + + @POST("subscriptions/unauthenticated") + suspend fun unauthenticatedSubscriptions( + @Body channels: List + ): List @POST("subscribe") suspend fun subscribe( diff --git a/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt b/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt index 2930cbcf4..95579ed12 100644 --- a/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt +++ b/app/src/main/java/com/github/libretube/api/PlaylistsHelper.kt @@ -2,6 +2,7 @@ package com.github.libretube.api import android.content.Context import android.util.Log +import androidx.core.text.isDigitsOnly import com.github.libretube.R import com.github.libretube.api.obj.Playlist 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.enums.PlaylistType import com.github.libretube.extensions.TAG -import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.toID import com.github.libretube.extensions.toLocalPlaylistItem import com.github.libretube.extensions.toStreamItem 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.util.PreferenceHelper -import com.github.libretube.util.ProxyHelper import java.io.IOException -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.HttpException @@ -35,26 +33,22 @@ object PlaylistsHelper { private val token get() = PreferenceHelper.getToken() - val loggedIn: Boolean get() = token != "" + val loggedIn: Boolean get() = token.isNotEmpty() - suspend fun getPlaylists(): List { - if (loggedIn) return RetrofitInstance.authApi.getUserPlaylists(token) - - val localPlaylists = awaitQuery { + suspend fun getPlaylists(): List = withContext(Dispatchers.IO) { + if (loggedIn) { + RetrofitInstance.authApi.getUserPlaylists(token) + } else { DatabaseHolder.Database.localPlaylistsDao().getAll() + .map { + Playlists( + id = it.playlist.id.toString(), + name = it.playlist.name, + thumbnail = ProxyHelper.rewriteUrl(it.playlist.thumbnailUrl), + videos = it.videos.size.toLong() + ) + } } - val playlists = mutableListOf() - localPlaylists.forEach { - playlists.add( - Playlists( - id = it.playlist.id.toString(), - name = it.playlist.name, - thumbnail = ProxyHelper.rewriteUrl(it.playlist.thumbnailUrl), - videos = it.videos.size.toLong() - ) - ) - } - return playlists } suspend fun getPlaylist(playlistId: String): Playlist { @@ -63,9 +57,8 @@ object PlaylistsHelper { PlaylistType.PRIVATE -> RetrofitInstance.authApi.getPlaylist(playlistId) PlaylistType.PUBLIC -> RetrofitInstance.api.getPlaylist(playlistId) PlaylistType.LOCAL -> { - val relation = awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().getAll() - }.first { it.playlist.id.toString() == playlistId } + val relation = DatabaseHolder.Database.localPlaylistsDao().getAll() + .first { it.playlist.id.toString() == playlistId } return Playlist( name = relation.playlist.name, thumbnailUrl = ProxyHelper.rewriteUrl(relation.playlist.thumbnailUrl), @@ -76,38 +69,26 @@ object PlaylistsHelper { } } - suspend fun createPlaylist( - playlistName: String, - appContext: Context - ): String? { + suspend fun createPlaylist(playlistName: String, appContext: Context?): String? { if (!loggedIn) { - awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().createPlaylist( - LocalPlaylist( - name = playlistName, - thumbnailUrl = "" - ) - ) + val playlist = LocalPlaylist(name = playlistName, thumbnailUrl = "") + DatabaseHolder.Database.localPlaylistsDao().createPlaylist(playlist) + return DatabaseHolder.Database.localPlaylistsDao().getAll() + .last().playlist.id.toString() + } else { + return try { + RetrofitInstance.authApi.createPlaylist(token, Playlists(name = playlistName)) + } catch (e: IOException) { + appContext?.toastFromMainThread(R.string.unknown_error) + return null + } catch (e: HttpException) { + Log.e(TAG(), e.toString()) + appContext?.toastFromMainThread(R.string.server_error) + return null + }.playlistId.also { + appContext?.toastFromMainThread(R.string.playlistCreated) } - return awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().getAll() - }.last().playlist.id.toString() } - val response = try { - RetrofitInstance.authApi.createPlaylist(token, Playlists(name = playlistName)) - } catch (e: IOException) { - appContext.toastFromMainThread(R.string.unknown_error) - return null - } catch (e: HttpException) { - Log.e(TAG(), e.toString()) - appContext.toastFromMainThread(R.string.server_error) - return null - } - if (response.playlistId != null) { - appContext.toastFromMainThread(R.string.playlistCreated) - return response.playlistId - } - return null } suspend fun addToPlaylist(playlistId: String, vararg videos: StreamItem): Boolean { @@ -117,22 +98,19 @@ object PlaylistsHelper { for (video in videos) { val localPlaylistItem = video.toLocalPlaylistItem(playlistId) - awaitQuery { - // avoid duplicated videos in a playlist - DatabaseHolder.Database.localPlaylistsDao() - .deletePlaylistItemsByVideoId(playlistId, localPlaylistItem.videoId) + // avoid duplicated videos in a playlist + DatabaseHolder.Database.localPlaylistsDao() + .deletePlaylistItemsByVideoId(playlistId, localPlaylistItem.videoId) - // add the new video to the database - DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem) + // add the new video to the database + DatabaseHolder.Database.localPlaylistsDao().addPlaylistVideo(localPlaylistItem) - if (localPlaylist.playlist.thumbnailUrl == "") { - // set the new playlist thumbnail URL - localPlaylistItem.thumbnailUrl?.let { - localPlaylist.playlist.thumbnailUrl = it - DatabaseHolder.Database.localPlaylistsDao().updatePlaylist( - localPlaylist.playlist - ) - } + val playlist = localPlaylist.playlist + if (playlist.thumbnailUrl == "") { + // set the new playlist thumbnail URL + localPlaylistItem.thumbnailUrl?.let { + playlist.thumbnailUrl = it + DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist) } } } @@ -144,89 +122,72 @@ object PlaylistsHelper { } suspend fun renamePlaylist(playlistId: String, newName: String): Boolean { - if (!loggedIn) { - val playlist = awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().getAll() - }.first { it.playlist.id.toString() == playlistId }.playlist + return if (!loggedIn) { + val playlist = DatabaseHolder.Database.localPlaylistsDao().getAll() + .first { it.playlist.id.toString() == playlistId }.playlist playlist.name = newName - awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist) - } - return true + DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(playlist) + true + } else { + val playlist = PlaylistId(playlistId, newName = newName) + RetrofitInstance.authApi.renamePlaylist(token, playlist).playlistId != null } - - return RetrofitInstance.authApi.renamePlaylist( - token, - PlaylistId(playlistId, newName = newName) - ).playlistId != null } - 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( - transaction.videos[index] - ) - } - if (transaction.videos.size > 1) { - if (index == 0) { - transaction.videos[1].thumbnailUrl?.let { - 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) - } - return - } - - RetrofitInstance.authApi.removeFromPlaylist( - PreferenceHelper.getToken(), - PlaylistId( - playlistId = playlistId, - index = index + suspend fun removeFromPlaylist(playlistId: String, index: Int): Boolean { + return if (!loggedIn) { + val transaction = DatabaseHolder.Database.localPlaylistsDao().getAll() + .first { it.playlist.id.toString() == playlistId } + DatabaseHolder.Database.localPlaylistsDao().removePlaylistVideo( + transaction.videos[index] ) - ) + // set a new playlist thumbnail if the first video got removed + if (index == 0) { + transaction.playlist.thumbnailUrl = transaction.videos.getOrNull(1)?.thumbnailUrl ?: "" + } + DatabaseHolder.Database.localPlaylistsDao().updatePlaylist(transaction.playlist) + true + } else { + RetrofitInstance.authApi.removeFromPlaylist( + PreferenceHelper.getToken(), + PlaylistId(playlistId = playlistId, index = index) + ).message == "ok" + } } - suspend fun importPlaylists(appContext: Context, playlists: List) { - for (playlist in playlists) { - val playlistId = createPlaylist(playlist.name!!, appContext) ?: continue - // if logged in, add the playlists by their ID via an api call - val success: Boolean = if (loggedIn) { - addToPlaylist( - playlistId, - *playlist.videos.map { - StreamItem(url = it) - }.toTypedArray() - ) - } else { - // if not logged in, all video information needs to become fetched manually - try { - val streamItems = playlist.videos.map { - RetrofitInstance.api.getStreams(it).toStreamItem(it) + suspend fun importPlaylists(playlists: List) = withContext(Dispatchers.IO) { + playlists.map { playlist -> + val playlistId = createPlaylist(playlist.name!!, null) + async { + playlistId ?: return@async + // if logged in, add the playlists by their ID via an api call + if (loggedIn) { + addToPlaylist( + playlistId, + *playlist.videos.map { + StreamItem(url = it) + }.toTypedArray() + ) + } else { + // if not logged in, all video information needs to become fetched manually + runCatching { + val streamItems = playlist.videos.map { + async { + try { + RetrofitInstance.api.getStreams(it).toStreamItem(it) + } catch (e: Exception) { + null + } + } + } + .awaitAll() + .filterNotNull() + + addToPlaylist(playlistId, *streamItems.toTypedArray()) } - addToPlaylist(playlistId, *streamItems.toTypedArray()) - } catch (e: Exception) { - false } } - appContext.toastFromMainThread( - if (success) R.string.importsuccess else R.string.server_error - ) - } + }.awaitAll() } suspend fun exportPlaylists(): List = withContext(Dispatchers.IO) { @@ -241,49 +202,35 @@ object PlaylistsHelper { } } - fun clonePlaylist(context: Context, playlistId: String) { + suspend fun clonePlaylist(context: Context, playlistId: String): String? { val appContext = context.applicationContext if (!loggedIn) { - CoroutineScope(Dispatchers.IO).launch { - val playlist = try { - RetrofitInstance.api.getPlaylist(playlistId) - } catch (e: Exception) { - appContext.toastFromMainThread(R.string.server_error) - return@launch - } - val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext) - newPlaylist ?: return@launch - - addToPlaylist(newPlaylist, *playlist.relatedStreams.toTypedArray()) - - var nextPage = playlist.nextpage - while (nextPage != null) { - nextPage = try { - RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage).apply { - addToPlaylist(newPlaylist, *relatedStreams.toTypedArray()) - }.nextpage - } catch (e: Exception) { - return@launch - } - } - } - return - } - - CoroutineScope(Dispatchers.IO).launch { - val response = try { - RetrofitInstance.authApi.clonePlaylist( - token, - PlaylistId(playlistId) - ) + val playlist = try { + RetrofitInstance.api.getPlaylist(playlistId) } catch (e: Exception) { - Log.e(TAG(), e.toString()) - return@launch + appContext.toastFromMainThread(R.string.server_error) + return null } - appContext?.toastFromMainThread( - if (response.playlistId != null) R.string.playlistCloned else R.string.server_error - ) + val newPlaylist = createPlaylist(playlist.name ?: "Unknown name", appContext) ?: return null + + addToPlaylist(newPlaylist, *playlist.relatedStreams.toTypedArray()) + + var nextPage = playlist.nextpage + while (nextPage != null) { + nextPage = try { + RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage).apply { + addToPlaylist(newPlaylist, *relatedStreams.toTypedArray()) + }.nextpage + } catch (e: Exception) { + break + } + } + return playlistId } + + return runCatching { + RetrofitInstance.authApi.clonePlaylist(token, PlaylistId(playlistId)) + }.getOrNull()?.playlistId } fun getPrivatePlaylistType(): PlaylistType { @@ -291,8 +238,12 @@ object PlaylistsHelper { } private fun getPrivatePlaylistType(playlistId: String): PlaylistType { - if (playlistId.all { it.isDigit() }) return PlaylistType.LOCAL - if (playlistId.matches(pipedPlaylistRegex)) return PlaylistType.PRIVATE - return PlaylistType.PUBLIC + return if (playlistId.isDigitsOnly()) { + PlaylistType.LOCAL + } else if (playlistId.matches(pipedPlaylistRegex)) { + PlaylistType.PRIVATE + } else { + PlaylistType.PUBLIC + } } } diff --git a/app/src/main/java/com/github/libretube/api/RetrofitInstance.kt b/app/src/main/java/com/github/libretube/api/RetrofitInstance.kt index 743280989..34e852e63 100644 --- a/app/src/main/java/com/github/libretube/api/RetrofitInstance.kt +++ b/app/src/main/java/com/github/libretube/api/RetrofitInstance.kt @@ -2,7 +2,7 @@ package com.github.libretube.api import com.github.libretube.constants.PIPED_API_URL 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 okhttp3.MediaType.Companion.toMediaType import retrofit2.Retrofit diff --git a/app/src/main/java/com/github/libretube/api/SubscriptionHelper.kt b/app/src/main/java/com/github/libretube/api/SubscriptionHelper.kt index 945020b7a..a54e8ffa4 100644 --- a/app/src/main/java/com/github/libretube/api/SubscriptionHelper.kt +++ b/app/src/main/java/com/github/libretube/api/SubscriptionHelper.kt @@ -10,13 +10,15 @@ import com.github.libretube.constants.PreferenceKeys import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.obj.LocalSubscription 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 kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext object SubscriptionHelper { + private const val GET_SUBSCRIPTIONS_LIMIT = 100 + suspend fun subscribe(channelId: String) { val token = PreferenceHelper.getToken() if (token.isNotEmpty()) { @@ -102,17 +104,20 @@ object SubscriptionHelper { } } - suspend fun getFormattedLocalSubscriptions(): String { - return Database.localSubscriptionDao().getAll() - .joinToString(",") { it.channelId } - } - suspend fun getSubscriptions(): List { val token = PreferenceHelper.getToken() return if (token.isNotEmpty()) { RetrofitInstance.authApi.subscriptions(token) } 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()) { RetrofitInstance.authApi.getFeed(token) } 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(",") + ) + } } } } diff --git a/app/src/main/java/com/github/libretube/constants/Constants.kt b/app/src/main/java/com/github/libretube/constants/Constants.kt index 0cb67f250..1b3d36c96 100644 --- a/app/src/main/java/com/github/libretube/constants/Constants.kt +++ b/app/src/main/java/com/github/libretube/constants/Constants.kt @@ -18,11 +18,11 @@ const val FAQ_URL = "https://libre-tube.github.io/#faq" /** * 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 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 REDDIT_URL = "https://www.reddit.com/r/Libretube/" -const val TWITTER_URL = "https://twitter.com/libretube" /** * Share Dialog diff --git a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt index a71f51057..35b58768a 100644 --- a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt +++ b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt @@ -7,7 +7,7 @@ import com.github.libretube.db.obj.SearchHistoryItem import com.github.libretube.db.obj.WatchHistoryItem import com.github.libretube.extensions.query import com.github.libretube.extensions.toID -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.PreferenceHelper object DatabaseHelper { private const val MAX_SEARCH_HISTORY_SIZE = 20 diff --git a/app/src/main/java/com/github/libretube/db/dao/LocalPlaylistsDao.kt b/app/src/main/java/com/github/libretube/db/dao/LocalPlaylistsDao.kt index f0eb47950..bba3429fe 100644 --- a/app/src/main/java/com/github/libretube/db/dao/LocalPlaylistsDao.kt +++ b/app/src/main/java/com/github/libretube/db/dao/LocalPlaylistsDao.kt @@ -14,29 +14,26 @@ import com.github.libretube.db.obj.LocalPlaylistWithVideos interface LocalPlaylistsDao { @Transaction @Query("SELECT * FROM LocalPlaylist") - fun getAll(): List + suspend fun getAll(): List @Insert - fun createPlaylist(playlist: LocalPlaylist) + suspend fun createPlaylist(playlist: LocalPlaylist) @Update - fun updatePlaylist(playlist: LocalPlaylist) - - @Delete - fun deletePlaylist(playlist: LocalPlaylist) + suspend fun updatePlaylist(playlist: LocalPlaylist) @Query("DELETE FROM localPlaylist WHERE id = :playlistId") - fun deletePlaylistById(playlistId: String) + suspend fun deletePlaylistById(playlistId: String) @Insert - fun addPlaylistVideo(playlistVideo: LocalPlaylistItem) + suspend fun addPlaylistVideo(playlistVideo: LocalPlaylistItem) @Delete - fun removePlaylistVideo(playlistVideo: LocalPlaylistItem) + suspend fun removePlaylistVideo(playlistVideo: LocalPlaylistItem) @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") - fun deletePlaylistItemsByVideoId(playlistId: String, videoId: String) + suspend fun deletePlaylistItemsByVideoId(playlistId: String, videoId: String) } diff --git a/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt index 7f1e718b7..14b42969e 100644 --- a/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt +++ b/app/src/main/java/com/github/libretube/extensions/ToStreamItem.kt @@ -3,7 +3,7 @@ package com.github.libretube.extensions import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.Streams 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 { return StreamItem( diff --git a/app/src/main/java/com/github/libretube/extensions/ToastFromMainThread.kt b/app/src/main/java/com/github/libretube/extensions/ToastFromMainThread.kt index 2be369899..22d6337ab 100644 --- a/app/src/main/java/com/github/libretube/extensions/ToastFromMainThread.kt +++ b/app/src/main/java/com/github/libretube/extensions/ToastFromMainThread.kt @@ -4,6 +4,8 @@ import android.content.Context import android.os.Handler import android.os.Looper import android.widget.Toast +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext fun Context.toastFromMainThread(text: String) { Handler(Looper.getMainLooper()).post { @@ -18,3 +20,11 @@ fun Context.toastFromMainThread(text: String) { fun Context.toastFromMainThread(stringId: Int) { 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)) +} diff --git a/app/src/main/java/com/github/libretube/util/AudioHelper.kt b/app/src/main/java/com/github/libretube/helpers/AudioHelper.kt similarity index 97% rename from app/src/main/java/com/github/libretube/util/AudioHelper.kt rename to app/src/main/java/com/github/libretube/helpers/AudioHelper.kt index 603933cd5..d373ac6b3 100644 --- a/app/src/main/java/com/github/libretube/util/AudioHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/AudioHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.media.AudioManager diff --git a/app/src/main/java/com/github/libretube/util/BackgroundHelper.kt b/app/src/main/java/com/github/libretube/helpers/BackgroundHelper.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/BackgroundHelper.kt rename to app/src/main/java/com/github/libretube/helpers/BackgroundHelper.kt index 77ae845e8..662e29396 100644 --- a/app/src/main/java/com/github/libretube/util/BackgroundHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/BackgroundHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.ActivityManager import android.content.Context diff --git a/app/src/main/java/com/github/libretube/util/BackupHelper.kt b/app/src/main/java/com/github/libretube/helpers/BackupHelper.kt similarity index 52% rename from app/src/main/java/com/github/libretube/util/BackupHelper.kt rename to app/src/main/java/com/github/libretube/helpers/BackupHelper.kt index 21411cec4..b520066ff 100644 --- a/app/src/main/java/com/github/libretube/util/BackupHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/BackupHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context 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.obj.BackupFile import com.github.libretube.obj.PreferenceItem -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.encodeToStream @@ -23,67 +22,63 @@ import kotlinx.serialization.json.longOrNull /** * 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 */ - fun createAdvancedBackup(uri: Uri?, backupFile: BackupFile) { - uri?.let { - try { - context.contentResolver.openOutputStream(it)?.use { outputStream -> - JsonHelper.json.encodeToStream(backupFile, outputStream) - } - } catch (e: Exception) { - Log.e(TAG(), "Error while writing backup: $e") + @OptIn(ExperimentalSerializationApi::class) + fun createAdvancedBackup(context: Context, uri: Uri, backupFile: BackupFile) { + try { + context.contentResolver.openOutputStream(uri)?.use { outputStream -> + JsonHelper.json.encodeToStream(backupFile, outputStream) } + } catch (e: Exception) { + Log.e(TAG(), "Error while writing backup: $e") } } /** * Restore data from a [BackupFile] */ - fun restoreAdvancedBackup(uri: Uri?) { - val backupFile = uri?.let { - context.contentResolver.openInputStream(it)?.use { inputStream -> - JsonHelper.json.decodeFromStream(inputStream) - } + @OptIn(ExperimentalSerializationApi::class) + suspend fun restoreAdvancedBackup(context: Context, uri: Uri) { + val backupFile = context.contentResolver.openInputStream(uri)?.use { + JsonHelper.json.decodeFromStream(it) } ?: return - runBlocking(Dispatchers.IO) { - Database.watchHistoryDao().insertAll( - *backupFile.watchHistory.orEmpty().toTypedArray() - ) - Database.searchHistoryDao().insertAll( - *backupFile.searchHistory.orEmpty().toTypedArray() - ) - Database.watchPositionDao().insertAll( - *backupFile.watchPositions.orEmpty().toTypedArray() - ) - Database.localSubscriptionDao().insertAll(backupFile.localSubscriptions.orEmpty()) - Database.customInstanceDao().insertAll( - *backupFile.customInstances.orEmpty().toTypedArray() - ) - Database.playlistBookmarkDao().insertAll( - *backupFile.playlistBookmarks.orEmpty().toTypedArray() - ) + Database.watchHistoryDao().insertAll( + *backupFile.watchHistory.orEmpty().toTypedArray() + ) + Database.searchHistoryDao().insertAll( + *backupFile.searchHistory.orEmpty().toTypedArray() + ) + Database.watchPositionDao().insertAll( + *backupFile.watchPositions.orEmpty().toTypedArray() + ) + Database.localSubscriptionDao().insertAll(backupFile.localSubscriptions.orEmpty()) + Database.customInstanceDao().insertAll( + *backupFile.customInstances.orEmpty().toTypedArray() + ) + Database.playlistBookmarkDao().insertAll( + *backupFile.playlistBookmarks.orEmpty().toTypedArray() + ) - backupFile.localPlaylists.orEmpty().forEach { - Database.localPlaylistsDao().createPlaylist(it.playlist) - val playlistId = Database.localPlaylistsDao().getAll().last().playlist.id - it.videos.forEach { - it.playlistId = playlistId - Database.localPlaylistsDao().addPlaylistVideo(it) - } + backupFile.localPlaylists.orEmpty().forEach { + Database.localPlaylistsDao().createPlaylist(it.playlist) + val playlistId = Database.localPlaylistsDao().getAll().last().playlist.id + it.videos.forEach { + it.playlistId = playlistId + Database.localPlaylistsDao().addPlaylistVideo(it) } - - restorePreferences(backupFile.preferences) } + + restorePreferences(context, backupFile.preferences) } /** * Restore the shared preferences from a backup file */ - private fun restorePreferences(preferences: List?) { + private fun restorePreferences(context: Context, preferences: List?) { if (preferences == null) return PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) { // clear the previous settings diff --git a/app/src/main/java/com/github/libretube/util/BrightnessHelper.kt b/app/src/main/java/com/github/libretube/helpers/BrightnessHelper.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/BrightnessHelper.kt rename to app/src/main/java/com/github/libretube/helpers/BrightnessHelper.kt index 09b3ec427..f96a7b04c 100644 --- a/app/src/main/java/com/github/libretube/util/BrightnessHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/BrightnessHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.Activity import android.view.WindowManager diff --git a/app/src/main/java/com/github/libretube/helpers/ClipboardHelper.kt b/app/src/main/java/com/github/libretube/helpers/ClipboardHelper.kt new file mode 100644 index 000000000..fd20a9039 --- /dev/null +++ b/app/src/main/java/com/github/libretube/helpers/ClipboardHelper.kt @@ -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()!!.setPrimaryClip(clip) + } +} diff --git a/app/src/main/java/com/github/libretube/util/DashHelper.kt b/app/src/main/java/com/github/libretube/helpers/DashHelper.kt similarity index 99% rename from app/src/main/java/com/github/libretube/util/DashHelper.kt rename to app/src/main/java/com/github/libretube/helpers/DashHelper.kt index 2f44f07e5..f26d7e726 100644 --- a/app/src/main/java/com/github/libretube/util/DashHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/DashHelper.kt @@ -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.Streams diff --git a/app/src/main/java/com/github/libretube/util/DownloadHelper.kt b/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/DownloadHelper.kt rename to app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt index a9f513519..fcf315785 100644 --- a/app/src/main/java/com/github/libretube/util/DownloadHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.content.Intent diff --git a/app/src/main/java/com/github/libretube/util/ImageHelper.kt b/app/src/main/java/com/github/libretube/helpers/ImageHelper.kt similarity index 97% rename from app/src/main/java/com/github/libretube/util/ImageHelper.kt rename to app/src/main/java/com/github/libretube/helpers/ImageHelper.kt index 31fc77438..a5151d44f 100644 --- a/app/src/main/java/com/github/libretube/util/ImageHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ImageHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.graphics.Bitmap @@ -13,6 +13,7 @@ import coil.request.CachePolicy import coil.request.ImageRequest import com.github.libretube.api.CronetHelper import com.github.libretube.constants.PreferenceKeys +import com.github.libretube.util.DataSaverMode import java.io.File import java.io.FileOutputStream import okio.use diff --git a/app/src/main/java/com/github/libretube/util/ImportHelper.kt b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt similarity index 57% rename from app/src/main/java/com/github/libretube/util/ImportHelper.kt rename to app/src/main/java/com/github/libretube/helpers/ImportHelper.kt index 3163f341f..6674f1bf5 100644 --- a/app/src/main/java/com/github/libretube/util/ImportHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt @@ -1,60 +1,51 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.Activity import android.net.Uri import android.util.Log -import android.widget.Toast import com.github.libretube.R import com.github.libretube.api.JsonHelper import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.RetrofitInstance 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.toastFromMainThread +import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.obj.ImportPlaylist import com.github.libretube.obj.ImportPlaylistFile import com.github.libretube.obj.NewPipeSubscription import com.github.libretube.obj.NewPipeSubscriptions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.encodeToStream import okio.use -class ImportHelper( - private val activity: Activity -) { +object ImportHelper { /** * Import subscriptions by a file uri */ - fun importSubscriptions(uri: Uri?) { - if (uri == null) return + suspend fun importSubscriptions(activity: Activity, uri: Uri) { try { - val applicationContext = activity.applicationContext - val channels = getChannelsFromUri(uri) - CoroutineScope(Dispatchers.IO).launch { - SubscriptionHelper.importSubscriptions(channels) - }.invokeOnCompletion { - applicationContext.toastFromMainThread(R.string.importsuccess) - } + SubscriptionHelper.importSubscriptions(getChannelsFromUri(activity, uri)) + activity.toastFromMainDispatcher(R.string.importsuccess) } catch (e: IllegalArgumentException) { Log.e(TAG(), e.toString()) - activity.toastFromMainThread( + activity.toastFromMainDispatcher( activity.getString(R.string.unsupported_file_format) + " (${activity.contentResolver.getType(uri)})" ) } catch (e: Exception) { 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] */ - private fun getChannelsFromUri(uri: Uri): List { + private fun getChannelsFromUri(activity: Activity, uri: Uri): List { return when (val fileType = activity.contentResolver.getType(uri)) { "application/json", "application/*", "application/octet-stream" -> { // NewPipe subscriptions format @@ -82,36 +73,31 @@ class ImportHelper( /** * Write the text to the document */ - fun exportSubscriptions(uri: Uri?) { - if (uri == null) return - runBlocking(Dispatchers.IO) { - val token = PreferenceHelper.getToken() - val subs = if (token.isNotEmpty()) { - RetrofitInstance.authApi.subscriptions(token) - } else { - RetrofitInstance.authApi.unauthenticatedSubscriptions( - SubscriptionHelper.getFormattedLocalSubscriptions() - ) - } - val newPipeChannels = subs.map { - NewPipeSubscription(it.name, 0, "https://www.youtube.com${it.url}") - } - val newPipeSubscriptions = NewPipeSubscriptions(subscriptions = newPipeChannels) - - activity.contentResolver.openOutputStream(uri)?.use { - JsonHelper.json.encodeToStream(newPipeSubscriptions, it) - } - - activity.toastFromMainThread(R.string.exportsuccess) + suspend fun exportSubscriptions(activity: Activity, uri: Uri) { + val token = PreferenceHelper.getToken() + val subs = if (token.isNotEmpty()) { + RetrofitInstance.authApi.subscriptions(token) + } else { + val subscriptions = Database.localSubscriptionDao().getAll().map { it.channelId } + RetrofitInstance.authApi.unauthenticatedSubscriptions(subscriptions) } + val newPipeChannels = subs.map { + NewPipeSubscription(it.name, 0, "https://www.youtube.com${it.url}") + } + val newPipeSubscriptions = NewPipeSubscriptions(subscriptions = newPipeChannels) + + activity.contentResolver.openOutputStream(uri)?.use { + JsonHelper.json.encodeToStream(newPipeSubscriptions, it) + } + + activity.toastFromMainDispatcher(R.string.exportsuccess) } /** * Import Playlists */ - fun importPlaylists(uri: Uri?) { - if (uri == null) return - + @OptIn(ExperimentalSerializationApi::class) + suspend fun importPlaylists(activity: Activity, uri: Uri) { val importPlaylists = mutableListOf() when (val fileType = activity.contentResolver.getType(uri)) { @@ -136,20 +122,22 @@ class ImportHelper( importPlaylists.addAll(playlistFile?.playlists.orEmpty()) } else -> { - activity.applicationContext.toastFromMainThread("Unsupported file type $fileType") + activity.toastFromMainDispatcher("Unsupported file type $fileType") return } } - CoroutineScope(Dispatchers.IO).launch { - try { - PlaylistsHelper.importPlaylists(activity, importPlaylists) - activity.applicationContext.toastFromMainThread(R.string.success) - } catch (e: Exception) { - Log.e(TAG(), e.toString()) - e.localizedMessage?.let { - activity.applicationContext.toastFromMainThread(it) - } + // convert the YouTube URLs to videoIds + importPlaylists.forEach { playlist -> + playlist.videos = playlist.videos.map { it.takeLast(11) } + } + try { + PlaylistsHelper.importPlaylists(importPlaylists) + activity.toastFromMainDispatcher(R.string.success) + } catch (e: Exception) { + Log.e(TAG(), e.toString()) + e.localizedMessage?.let { + activity.toastFromMainDispatcher(it) } } } @@ -157,18 +145,14 @@ class ImportHelper( /** * Export Playlists */ - fun exportPlaylists(uri: Uri?) { - if (uri == null) return + suspend fun exportPlaylists(activity: Activity, uri: Uri) { + val playlists = PlaylistsHelper.exportPlaylists() + val playlistFile = ImportPlaylistFile("Piped", 1, playlists) - runBlocking { - val playlists = PlaylistsHelper.exportPlaylists() - val playlistFile = ImportPlaylistFile("Piped", 1, playlists) - - activity.contentResolver.openOutputStream(uri)?.use { - JsonHelper.json.encodeToStream(playlistFile, it) - } - - activity.toastFromMainThread(R.string.exportsuccess) + activity.contentResolver.openOutputStream(uri)?.use { + JsonHelper.json.encodeToStream(playlistFile, it) } + + activity.toastFromMainDispatcher(R.string.exportsuccess) } } diff --git a/app/src/main/java/com/github/libretube/helpers/IntentHelper.kt b/app/src/main/java/com/github/libretube/helpers/IntentHelper.kt new file mode 100644 index 000000000..0c3c3f095 --- /dev/null +++ b/app/src/main/java/com/github/libretube/helpers/IntentHelper.kt @@ -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) + } +} diff --git a/app/src/main/java/com/github/libretube/util/LocaleHelper.kt b/app/src/main/java/com/github/libretube/helpers/LocaleHelper.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/LocaleHelper.kt rename to app/src/main/java/com/github/libretube/helpers/LocaleHelper.kt index 9f28e1915..f6d65cd25 100644 --- a/app/src/main/java/com/github/libretube/util/LocaleHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/LocaleHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.content.res.Configuration diff --git a/app/src/main/java/com/github/libretube/util/NavBarHelper.kt b/app/src/main/java/com/github/libretube/helpers/NavBarHelper.kt similarity index 99% rename from app/src/main/java/com/github/libretube/util/NavBarHelper.kt rename to app/src/main/java/com/github/libretube/helpers/NavBarHelper.kt index 4e7d61d96..e2788b5b3 100644 --- a/app/src/main/java/com/github/libretube/util/NavBarHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NavBarHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.util.Log diff --git a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt b/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt similarity index 91% rename from app/src/main/java/com/github/libretube/util/NavigationHelper.kt rename to app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt index 07d59eb33..794ef2e3a 100644 --- a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.NotificationManager import android.content.Context @@ -10,6 +10,8 @@ import android.os.Looper import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf import androidx.core.os.postDelayed +import androidx.fragment.app.commitNow +import androidx.fragment.app.replace import com.github.libretube.R import com.github.libretube.constants.IntentData import com.github.libretube.constants.PreferenceKeys @@ -90,17 +92,9 @@ object NavigationHelper { ) val activity = context as AppCompatActivity - activity.supportFragmentManager.beginTransaction() - .remove(PlayerFragment()) - .commit() - activity.supportFragmentManager.beginTransaction() - .replace( - R.id.container, - PlayerFragment().apply { - arguments = bundle - } - ) - .commitNow() + activity.supportFragmentManager.commitNow { + replace(R.id.container, args = bundle) + } } fun navigatePlaylist( diff --git a/app/src/main/java/com/github/libretube/util/NetworkHelper.kt b/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt similarity index 97% rename from app/src/main/java/com/github/libretube/util/NetworkHelper.kt rename to app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt index 757fc584c..8f303230e 100644 --- a/app/src/main/java/com/github/libretube/util/NetworkHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.net.ConnectivityManager diff --git a/app/src/main/java/com/github/libretube/util/NotificationHelper.kt b/app/src/main/java/com/github/libretube/helpers/NotificationHelper.kt similarity index 93% rename from app/src/main/java/com/github/libretube/util/NotificationHelper.kt rename to app/src/main/java/com/github/libretube/helpers/NotificationHelper.kt index 04dd98243..4d44b1e70 100644 --- a/app/src/main/java/com/github/libretube/util/NotificationHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NotificationHelper.kt @@ -1,10 +1,10 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType -import androidx.work.PeriodicWorkRequest +import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import com.github.libretube.constants.NOTIFICATION_WORK_NAME import com.github.libretube.constants.PreferenceKeys @@ -56,8 +56,7 @@ object NotificationHelper { .build() // create the worker - val notificationWorker = PeriodicWorkRequest.Builder( - NotificationWorker::class.java, + val notificationWorker = PeriodicWorkRequestBuilder( checkingFrequency, TimeUnit.MINUTES ) diff --git a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt similarity index 99% rename from app/src/main/java/com/github/libretube/util/PlayerHelper.kt rename to app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt index 91bf6b7b5..915bd7c40 100644 --- a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.Activity import android.app.PendingIntent diff --git a/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt b/app/src/main/java/com/github/libretube/helpers/PreferenceHelper.kt similarity index 99% rename from app/src/main/java/com/github/libretube/util/PreferenceHelper.kt rename to app/src/main/java/com/github/libretube/helpers/PreferenceHelper.kt index d72174b5a..c3f54fb8a 100644 --- a/app/src/main/java/com/github/libretube/util/PreferenceHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/PreferenceHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.content.SharedPreferences diff --git a/app/src/main/java/com/github/libretube/util/ProxyHelper.kt b/app/src/main/java/com/github/libretube/helpers/ProxyHelper.kt similarity index 97% rename from app/src/main/java/com/github/libretube/util/ProxyHelper.kt rename to app/src/main/java/com/github/libretube/helpers/ProxyHelper.kt index b91a354a5..a1f05117d 100644 --- a/app/src/main/java/com/github/libretube/util/ProxyHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ProxyHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import com.github.libretube.api.RetrofitInstance import com.github.libretube.constants.PreferenceKeys diff --git a/app/src/main/java/com/github/libretube/util/ShortcutHelper.kt b/app/src/main/java/com/github/libretube/helpers/ShortcutHelper.kt similarity index 97% rename from app/src/main/java/com/github/libretube/util/ShortcutHelper.kt rename to app/src/main/java/com/github/libretube/helpers/ShortcutHelper.kt index ea1b14279..5f66735bb 100644 --- a/app/src/main/java/com/github/libretube/util/ShortcutHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ShortcutHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.content.Context import android.content.Intent diff --git a/app/src/main/java/com/github/libretube/util/ThemeHelper.kt b/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt similarity index 99% rename from app/src/main/java/com/github/libretube/util/ThemeHelper.kt rename to app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt index b75544bd8..1796966c8 100644 --- a/app/src/main/java/com/github/libretube/util/ThemeHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.app.Activity import android.content.ComponentName diff --git a/app/src/main/java/com/github/libretube/util/WindowHelper.kt b/app/src/main/java/com/github/libretube/helpers/WindowHelper.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/WindowHelper.kt rename to app/src/main/java/com/github/libretube/helpers/WindowHelper.kt index 3a36279d2..3220320c8 100644 --- a/app/src/main/java/com/github/libretube/util/WindowHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/WindowHelper.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.helpers import android.os.Build import android.view.WindowManager diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index d1413db69..01541746a 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -29,9 +29,9 @@ import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.query import com.github.libretube.extensions.toID 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.PlayerHelper -import com.github.libretube.util.PlayerHelper.loadPlaybackParams import com.github.libretube.util.PlayingQueue import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem diff --git a/app/src/main/java/com/github/libretube/services/DownloadService.kt b/app/src/main/java/com/github/libretube/services/DownloadService.kt index 64a75c03a..8f04bf6a6 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -25,14 +25,14 @@ import com.github.libretube.extensions.getContentLength import com.github.libretube.extensions.query import com.github.libretube.extensions.toDownloadItems 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.receivers.NotificationReceiver import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_PAUSE import com.github.libretube.receivers.NotificationReceiver.Companion.ACTION_DOWNLOAD_RESUME 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.net.HttpURLConnection import java.net.SocketTimeoutException diff --git a/app/src/main/java/com/github/libretube/services/UpdateService.kt b/app/src/main/java/com/github/libretube/services/UpdateService.kt index ba836c593..9ed9be389 100644 --- a/app/src/main/java/com/github/libretube/services/UpdateService.kt +++ b/app/src/main/java/com/github/libretube/services/UpdateService.kt @@ -11,7 +11,7 @@ import android.os.Environment import android.os.IBinder import android.widget.Toast import com.github.libretube.R -import com.github.libretube.util.DownloadHelper +import com.github.libretube.helpers.DownloadHelper import java.io.File class UpdateService : Service() { diff --git a/app/src/main/java/com/github/libretube/ui/activities/AboutActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/AboutActivity.kt index bc4cf4b13..c9e2aecc9 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/AboutActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/AboutActivity.kt @@ -1,10 +1,6 @@ package com.github.libretube.ui.activities -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Build import android.os.Bundle 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.WEBSITE_URL 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.google.android.material.card.MaterialCardView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar @@ -34,7 +33,7 @@ class AboutActivity : BaseActivity() { } binding.appIcon.setOnClickListener { - val sendIntent: Intent = Intent().apply { + val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, GITHUB_URL) type = "text/plain" @@ -44,37 +43,10 @@ class AboutActivity : BaseActivity() { startActivity(shareIntent) } - binding.website.setOnClickListener { - openLinkFromHref(WEBSITE_URL) - } - binding.website.setOnLongClickListener { - 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 - } + setupCard(binding.website, WEBSITE_URL) + setupCard(binding.piped, PIPED_GITHUB_URL) + setupCard(binding.translate, WEBLATE_URL) + setupCard(binding.github, GITHUB_URL) binding.license.setOnClickListener { showLicense() @@ -89,18 +61,19 @@ class AboutActivity : BaseActivity() { } } - private fun openLinkFromHref(link: String) { - val uri = Uri.parse(link) - val intent = Intent(Intent.ACTION_VIEW).setData(uri) - startActivity(intent) + private fun setupCard(card: MaterialCardView, link: String) { + card.setOnClickListener { + IntentHelper.openLinkFromHref(this, link) + } + card.setOnLongClickListener { + onLongClick(link) + true + } } private fun onLongClick(href: String) { // copy the link to the clipboard - val clipboard: ClipboardManager = - getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText(getString(R.string.copied), href) - clipboard.setPrimaryClip(clip) + ClipboardHelper.save(this, href) // show the snackBar with open action Snackbar.make( binding.root, @@ -108,7 +81,7 @@ class AboutActivity : BaseActivity() { Snackbar.LENGTH_LONG ) .setAction(R.string.open_copied) { - openLinkFromHref(href) + IntentHelper.openLinkFromHref(this, href) } .setAnimationMode(Snackbar.ANIMATION_MODE_FADE) .show() diff --git a/app/src/main/java/com/github/libretube/ui/activities/HelpActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/HelpActivity.kt index 56fae56d2..bff219596 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/HelpActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/HelpActivity.kt @@ -1,16 +1,16 @@ package com.github.libretube.ui.activities -import android.content.Intent -import android.net.Uri import android.os.Bundle import com.github.libretube.constants.DISCORD_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.REDDIT_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.helpers.IntentHelper import com.github.libretube.ui.base.BaseActivity +import com.google.android.material.card.MaterialCardView class HelpActivity : BaseActivity() { private lateinit var binding: ActivityHelpBinding @@ -25,34 +25,17 @@ class HelpActivity : BaseActivity() { onBackPressedDispatcher.onBackPressed() } - binding.faq.setOnClickListener { - openLinkFromHref(FAQ_URL) - } - - binding.matrix.setOnClickListener { - openLinkFromHref(MATRIX_URL) - } - - binding.telegram.setOnClickListener { - openLinkFromHref(TELEGRAM_URL) - } - - binding.discord.setOnClickListener { - openLinkFromHref(DISCORD_URL) - } - - binding.reddit.setOnClickListener { - openLinkFromHref(REDDIT_URL) - } - - binding.twitter.setOnClickListener { - openLinkFromHref(TWITTER_URL) - } + setupCard(binding.faq, 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) } - private fun openLinkFromHref(link: String) { - val uri = Uri.parse(link) - val intent = Intent(Intent.ACTION_VIEW).setData(uri) - startActivity(intent) + private fun setupCard(card: MaterialCardView, link: String) { + card.setOnClickListener { + IntentHelper.openLinkFromHref(this, link) + } } } diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index 05f4d25e5..5c13ab6ca 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -27,6 +27,12 @@ import com.github.libretube.constants.IntentData import com.github.libretube.constants.PreferenceKeys import com.github.libretube.databinding.ActivityMainBinding 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.ui.base.BaseActivity 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.SubscriptionsViewModel 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 class MainActivity : BaseActivity() { diff --git a/app/src/main/java/com/github/libretube/ui/activities/NoInternetActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/NoInternetActivity.kt index 4ab4ce987..83a4580d5 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/NoInternetActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/NoInternetActivity.kt @@ -4,12 +4,13 @@ import android.content.Intent import android.os.Bundle import androidx.activity.addCallback import androidx.fragment.app.commit +import androidx.fragment.app.replace import com.github.libretube.R 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.fragments.DownloadsFragment -import com.github.libretube.util.NavigationHelper -import com.github.libretube.util.NetworkHelper import com.google.android.material.snackbar.Snackbar class NoInternetActivity : BaseActivity() { @@ -33,10 +34,10 @@ class NoInternetActivity : BaseActivity() { } binding.downloads.setOnClickListener { - supportFragmentManager.beginTransaction() - .replace(R.id.noInternet_container, DownloadsFragment()) - .addToBackStack(null) - .commit() + supportFragmentManager.commit { + replace(R.id.noInternet_container) + addToBackStack(null) + } } setContentView(binding.root) diff --git a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt index d56276ea0..04367ea19 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt @@ -16,12 +16,12 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.enums.FileType import com.github.libretube.extensions.awaitQuery 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.extensions.setAspectRatio 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.ExoPlayer import com.google.android.exoplayer2.MediaItem diff --git a/app/src/main/java/com/github/libretube/ui/activities/RouterActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/RouterActivity.kt index 1af65395b..a6d42a91f 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/RouterActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/RouterActivity.kt @@ -7,8 +7,8 @@ import android.os.Bundle import android.util.Log import com.github.libretube.constants.IntentData import com.github.libretube.extensions.TAG +import com.github.libretube.helpers.NavigationHelper import com.github.libretube.ui.base.BaseActivity -import com.github.libretube.util.NavigationHelper import com.github.libretube.util.TextUtils class RouterActivity : BaseActivity() { diff --git a/app/src/main/java/com/github/libretube/ui/activities/SettingsActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/SettingsActivity.kt index 68d4605f4..d8eaa2e8f 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/SettingsActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/SettingsActivity.kt @@ -24,10 +24,9 @@ class SettingsActivity : BaseActivity() { } if (savedInstanceState == null) { - supportFragmentManager - .beginTransaction() - .replace(R.id.settings, MainSettings()) - .commit() + supportFragmentManager.commit { + replace(R.id.settings) + } } // new way of dealing with back presses instead of onBackPressed() diff --git a/app/src/main/java/com/github/libretube/ui/adapters/BottomSheetAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/BottomSheetAdapter.kt index f17ca2535..b58462ce4 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/BottomSheetAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/BottomSheetAdapter.kt @@ -1,11 +1,11 @@ package com.github.libretube.ui.adapters import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.libretube.databinding.BottomSheetItemBinding import com.github.libretube.obj.BottomSheetItem +import com.github.libretube.ui.extensions.setDrawables import com.github.libretube.ui.viewholders.BottomSheetViewHolder class BottomSheetAdapter( @@ -23,17 +23,12 @@ class BottomSheetAdapter( override fun onBindViewHolder(holder: BottomSheetViewHolder, position: Int) { val item = items[position] - holder.binding.apply { + holder.binding.root.apply { val current = item.getCurrent() - title.text = - if (current != null) "${item.title} ($current)" else item.title - if (item.drawable != null) { - drawable.setImageResource(item.drawable) - } else { - drawable.visibility = View.GONE - } + text = if (current != null) "${item.title} ($current)" else item.title + setDrawables(start = item.drawable) - root.setOnClickListener { + setOnClickListener { item.onClick.invoke() listener.invoke(position) } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/ChaptersAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/ChaptersAdapter.kt index 8b7f64a68..3dc32e5a2 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/ChaptersAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/ChaptersAdapter.kt @@ -7,9 +7,9 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.libretube.api.obj.ChapterSegment 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.util.ImageHelper -import com.github.libretube.util.ThemeHelper import com.google.android.exoplayer2.ExoPlayer class ChaptersAdapter( diff --git a/app/src/main/java/com/github/libretube/ui/adapters/CommentsAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/CommentsAdapter.kt index 1b4d75f6b..15301c038 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/CommentsAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/CommentsAdapter.kt @@ -1,17 +1,19 @@ package com.github.libretube.ui.adapters import android.annotation.SuppressLint -import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewGroup.MarginLayoutParams import android.widget.Toast import androidx.appcompat.content.res.AppCompatResources +import androidx.core.os.bundleOf import androidx.core.text.parseAsHtml import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.Fragment +import androidx.fragment.app.commit +import androidx.fragment.app.replace import androidx.recyclerview.widget.RecyclerView import com.github.libretube.R 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.databinding.CommentsRowBinding 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.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.ThemeHelper +import kotlinx.serialization.encodeToString class CommentsAdapter( private val fragment: Fragment?, @@ -65,7 +68,7 @@ class CommentsAdapter( if (comment.verified) verifiedImageView.visibility = View.VISIBLE if (comment.pinned) pinnedImageView.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) { repliesCount.text = comment.replyCount.formatShort() } @@ -77,7 +80,6 @@ class CommentsAdapter( if (isRepliesAdapter) { repliesCount.visibility = View.GONE - repliesAvailable.visibility = View.GONE // highlight the comment that is being replied to if (comment == comments.firstOrNull()) { @@ -87,31 +89,28 @@ class CommentsAdapter( root.updatePadding(top = 20) root.updateLayoutParams { bottomMargin = 20 } } 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) { - val repliesFragment = CommentsRepliesFragment().apply { - arguments = Bundle().apply { - putString(IntentData.videoId, videoId) - putString( - IntentData.comment, - JsonHelper.json.encodeToString(Comment.serializer(), comment) - ) - } - } root.setOnClickListener { - fragment!!.parentFragmentManager - .beginTransaction() - .replace(R.id.commentFragContainer, repliesFragment) - .addToBackStack(null) - .commit() + val args = bundleOf( + IntentData.videoId to videoId, + IntentData.comment to JsonHelper.json.encodeToString(comment) + ) + fragment!!.parentFragmentManager.commit { + replace(R.id.commentFragContainer, args = args) + addToBackStack(null) + } } } 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() true } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt index a61a25b07..df46893c3 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/DownloadsAdapter.kt @@ -14,9 +14,9 @@ import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.obj.DownloadWithItems import com.github.libretube.extensions.formatAsFileSize import com.github.libretube.extensions.query +import com.github.libretube.helpers.ImageHelper import com.github.libretube.ui.activities.OfflinePlayerActivity import com.github.libretube.ui.viewholders.DownloadsViewHolder -import com.github.libretube.util.ImageHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.io.File diff --git a/app/src/main/java/com/github/libretube/ui/adapters/IconsSheetAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/IconsSheetAdapter.kt index a1805b683..00daa1f59 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/IconsSheetAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/IconsSheetAdapter.kt @@ -8,9 +8,9 @@ import androidx.recyclerview.widget.RecyclerView import com.github.libretube.R import com.github.libretube.constants.PreferenceKeys 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.util.PreferenceHelper -import com.github.libretube.util.ThemeHelper class IconsSheetAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IconsSheetViewHolder { diff --git a/app/src/main/java/com/github/libretube/ui/adapters/LegacySubscriptionAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/LegacySubscriptionAdapter.kt index d339f00ba..f4ecf65f7 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/LegacySubscriptionAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/LegacySubscriptionAdapter.kt @@ -5,11 +5,11 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.libretube.databinding.LegacySubscriptionChannelBinding 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.sheets.ChannelOptionsBottomSheet import com.github.libretube.ui.viewholders.LegacySubscriptionViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper class LegacySubscriptionAdapter( private val subscriptions: List diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt index d0875c073..dec3d6921 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlayingQueueAdapter.kt @@ -8,10 +8,10 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.libretube.databinding.QueueRowBinding 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.util.ImageHelper import com.github.libretube.util.PlayingQueue -import com.github.libretube.util.ThemeHelper class PlayingQueueAdapter : RecyclerView.Adapter() { diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt index 5fba1f429..93d331783 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistAdapter.kt @@ -13,13 +13,13 @@ import com.github.libretube.databinding.VideoRowBinding import com.github.libretube.enums.PlaylistType import com.github.libretube.extensions.TAG 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.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.viewholders.PlaylistViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper import java.io.IOException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -84,9 +84,7 @@ class PlaylistAdapter( if (!streamItem.uploaderUrl.isNullOrBlank()) { channelContainer.setOnClickListener { - streamItem.uploaderUrl?.toID()?.let { - NavigationHelper.navigateChannel(root.context, it) - } + NavigationHelper.navigateChannel(root.context, streamItem.uploaderUrl.toID()) } } @@ -96,7 +94,7 @@ class PlaylistAdapter( removeFromPlaylist(root.context, position) } } - watchProgress.setWatchProgressLength(videoId, streamItem.duration!!) + watchProgress.setWatchProgressLength(videoId, streamItem.duration) } } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistBookmarkAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistBookmarkAdapter.kt index 08863c48c..d96cf9703 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistBookmarkAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistBookmarkAdapter.kt @@ -12,10 +12,10 @@ import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.obj.PlaylistBookmark import com.github.libretube.enums.PlaylistType 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.viewholders.PlaylistBookmarkViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper class PlaylistBookmarkAdapter( private val bookmarks: List, diff --git a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistsAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistsAdapter.kt index 4c4f4b7b6..2a650a922 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/PlaylistsAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/PlaylistsAdapter.kt @@ -7,12 +7,12 @@ import com.github.libretube.R import com.github.libretube.api.obj.Playlists import com.github.libretube.databinding.PlaylistsRowBinding 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.dialogs.DeletePlaylistDialog import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet import com.github.libretube.ui.viewholders.PlaylistsViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper class PlaylistsAdapter( private val playlists: MutableList, diff --git a/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt index c99229616..05f5f11f1 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/SearchAdapter.kt @@ -4,7 +4,8 @@ import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View 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.api.obj.ContentItem 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.extensions.formatShort 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.extensions.setFormattedDuration 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.VideoOptionsBottomSheet 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 -class SearchAdapter( - private val searchItems: MutableList -) : - RecyclerView.Adapter() { - - fun updateItems(newItems: List) { - val searchItemsSize = searchItems.size - searchItems.addAll(newItems) - notifyItemRangeInserted(searchItemsSize, newItems.size) - } - - override fun getItemCount(): Int { - return searchItems.size - } - +class SearchAdapter : ListAdapter(SearchCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder { val layoutInflater = LayoutInflater.from(parent.context) @@ -58,7 +45,7 @@ class SearchAdapter( } override fun onBindViewHolder(holder: SearchViewHolder, position: Int) { - val searchItem = searchItems[position] + val searchItem = currentList[position] val videoRowBinding = holder.videoRowBinding val channelRowBinding = holder.channelRowBinding @@ -68,11 +55,13 @@ class SearchAdapter( bindWatch(searchItem, videoRowBinding) } else if (channelRowBinding != null) { bindChannel(searchItem, channelRowBinding) - } else if (playlistRowBinding != null) bindPlaylist(searchItem, playlistRowBinding) + } else if (playlistRowBinding != null) { + bindPlaylist(searchItem, playlistRowBinding) + } } override fun getItemViewType(position: Int): Int { - return when (searchItems[position].type) { + return when (currentList[position].type) { "stream" -> 0 "channel" -> 1 "playlist" -> 2 @@ -116,10 +105,7 @@ class SearchAdapter( } @SuppressLint("SetTextI18n") - private fun bindChannel( - item: ContentItem, - binding: ChannelRowBinding - ) { + private fun bindChannel(item: ContentItem, binding: ChannelRowBinding) { binding.apply { ImageHelper.loadImage(item.thumbnail, searchChannelImage) searchChannelName.text = item.name @@ -144,10 +130,7 @@ class SearchAdapter( } } - private fun bindPlaylist( - item: ContentItem, - binding: PlaylistsRowBinding - ) { + private fun bindPlaylist(item: ContentItem, binding: PlaylistsRowBinding) { binding.apply { ImageHelper.loadImage(item.thumbnail, playlistThumbnail) if (item.videos != -1L) videoCount.text = item.videos.toString() @@ -169,4 +152,14 @@ class SearchAdapter( } } } + + private object SearchCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean { + return oldItem.url == newItem.url + } + + override fun areContentsTheSame(oldItem: ContentItem, newItem: ContentItem): Boolean { + return true + } + } } diff --git a/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt index c0e2c37e1..e5ec8d60d 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt @@ -6,12 +6,12 @@ import androidx.recyclerview.widget.RecyclerView import com.github.libretube.api.obj.Subscription import com.github.libretube.databinding.ChannelSubscriptionRowBinding 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.extensions.setupSubscriptionButton import com.github.libretube.ui.sheets.ChannelOptionsBottomSheet import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper class SubscriptionChannelAdapter( private val subscriptions: MutableList diff --git a/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt index 326edefd5..cf2139337 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/VideosAdapter.kt @@ -20,14 +20,14 @@ import com.github.libretube.databinding.VideoRowBinding import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.formatShort 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.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.sheets.VideoOptionsBottomSheet 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 class VideosAdapter( diff --git a/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt index 3359efefe..bed0f0b77 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/WatchHistoryAdapter.kt @@ -8,13 +8,13 @@ import com.github.libretube.databinding.VideoRowBinding import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.obj.WatchHistoryItem 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.extensions.setFormattedDuration import com.github.libretube.ui.extensions.setWatchProgressLength import com.github.libretube.ui.sheets.VideoOptionsBottomSheet import com.github.libretube.ui.viewholders.WatchHistoryViewHolder -import com.github.libretube.util.ImageHelper -import com.github.libretube.util.NavigationHelper class WatchHistoryAdapter( private val watchHistory: MutableList diff --git a/app/src/main/java/com/github/libretube/ui/base/BaseActivity.kt b/app/src/main/java/com/github/libretube/ui/base/BaseActivity.kt index 189aad047..c56566e63 100644 --- a/app/src/main/java/com/github/libretube/ui/base/BaseActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/base/BaseActivity.kt @@ -2,8 +2,8 @@ package com.github.libretube.ui.base import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.github.libretube.util.LocaleHelper -import com.github.libretube.util.ThemeHelper +import com.github.libretube.helpers.LocaleHelper +import com.github.libretube.helpers.ThemeHelper /** * Activity that applies the LibreTube theme and the in-app language diff --git a/app/src/main/java/com/github/libretube/ui/base/BasePreferenceFragment.kt b/app/src/main/java/com/github/libretube/ui/base/BasePreferenceFragment.kt index 49879195d..8fcc90d42 100644 --- a/app/src/main/java/com/github/libretube/ui/base/BasePreferenceFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/base/BasePreferenceFragment.kt @@ -7,8 +7,8 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.github.libretube.R import com.github.libretube.databinding.DialogTextPreferenceBinding +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.activities.SettingsActivity -import com.github.libretube.util.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder /** diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/BackupDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/BackupDialog.kt index ae0ce72b5..34c50f58e 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/BackupDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/BackupDialog.kt @@ -7,9 +7,9 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import com.github.libretube.R 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.PreferenceItem -import com.github.libretube.util.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/CreatePlaylistDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/CreatePlaylistDialog.kt index 63e1e54d9..0f8000fc1 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/CreatePlaylistDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/CreatePlaylistDialog.kt @@ -7,6 +7,7 @@ import androidx.fragment.app.DialogFragment import com.github.libretube.R import com.github.libretube.api.PlaylistsHelper import com.github.libretube.databinding.DialogCreatePlaylistBinding +import com.github.libretube.extensions.toastFromMainThread import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -24,8 +25,19 @@ class CreatePlaylistDialog( binding.clonePlaylist.setOnClickListener { val playlistUrl = binding.playlistUrl.text.toString().toHttpUrlOrNull() + val appContext = context?.applicationContext + 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() } ?: run { Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/DeleteAccountDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/DeleteAccountDialog.kt index c08746e62..295a91bbd 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/DeleteAccountDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/DeleteAccountDialog.kt @@ -12,7 +12,7 @@ import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.DeleteUserRequest import com.github.libretube.databinding.DialogDeleteAccountBinding 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 class DeleteAccountDialog : DialogFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/DeletePlaylistDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/DeletePlaylistDialog.kt index 091966ad8..01bef8455 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/DeletePlaylistDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/DeletePlaylistDialog.kt @@ -4,16 +4,15 @@ import android.app.Dialog import android.os.Bundle import android.util.Log import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import com.github.libretube.R import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.PlaylistId import com.github.libretube.db.DatabaseHolder import com.github.libretube.enums.PlaylistType import com.github.libretube.extensions.TAG -import com.github.libretube.extensions.awaitQuery -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -28,44 +27,41 @@ class DeletePlaylistDialog( .setTitle(R.string.deletePlaylist) .setMessage(R.string.areYouSure) .setPositiveButton(R.string.yes) { _, _ -> - PreferenceHelper.getToken() - deletePlaylist() + lifecycleScope.launch(Dispatchers.IO) { + deletePlaylist() + } } .setNegativeButton(R.string.cancel, null) .show() } - private fun deletePlaylist() { + private suspend fun deletePlaylist() { if (playlistType == PlaylistType.LOCAL) { - awaitQuery { - DatabaseHolder.Database.localPlaylistsDao().deletePlaylistById(playlistId) - DatabaseHolder.Database.localPlaylistsDao().deletePlaylistItemsByPlaylistId( - playlistId - ) + DatabaseHolder.Database.localPlaylistsDao().deletePlaylistById(playlistId) + DatabaseHolder.Database.localPlaylistsDao().deletePlaylistItemsByPlaylistId(playlistId) + withContext(Dispatchers.Main) { + onSuccess() } - onSuccess.invoke() return } - CoroutineScope(Dispatchers.IO).launch { - val response = try { - RetrofitInstance.authApi.deletePlaylist( - PreferenceHelper.getToken(), - PlaylistId(playlistId) - ) - } catch (e: Exception) { - Log.e(TAG(), e.toString()) - return@launch - } - try { - if (response.message == "ok") { - withContext(Dispatchers.Main) { - onSuccess.invoke() - } + val response = try { + RetrofitInstance.authApi.deletePlaylist( + PreferenceHelper.getToken(), + PlaylistId(playlistId) + ) + } catch (e: Exception) { + Log.e(TAG(), e.toString()) + return + } + try { + if (response.message == "ok") { + withContext(Dispatchers.Main) { + onSuccess() } - } catch (e: Exception) { - Log.e(TAG(), e.toString()) } + } catch (e: Exception) { + Log.e(TAG(), e.toString()) } } } diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt index e165e1f47..7aa5a33e1 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt @@ -15,7 +15,7 @@ import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.Streams import com.github.libretube.databinding.DialogDownloadBinding 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.google.android.material.dialog.MaterialAlertDialogBuilder import java.io.IOException diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/ErrorDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/ErrorDialog.kt index 68a154997..6a71f24f9 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/ErrorDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/ErrorDialog.kt @@ -6,8 +6,8 @@ import android.os.Bundle import android.widget.Toast import androidx.fragment.app.DialogFragment import com.github.libretube.R -import com.github.libretube.util.ClipboardHelper -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.ClipboardHelper +import com.github.libretube.helpers.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class ErrorDialog : DialogFragment() { @@ -22,7 +22,7 @@ class ErrorDialog : DialogFragment() { .setMessage(errorLog) .setNegativeButton(R.string.okay, null) .setPositiveButton(R.string.copy) { _, _ -> - ClipboardHelper(requireContext()).save(errorLog) + ClipboardHelper.save(requireContext(), errorLog) Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() } .show() diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/LoginDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/LoginDialog.kt index 5281ed074..9a6afdeeb 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/LoginDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/LoginDialog.kt @@ -12,7 +12,7 @@ import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.obj.Login import com.github.libretube.databinding.DialogLoginBinding 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.google.android.material.dialog.MaterialAlertDialogBuilder diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/LogoutDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/LogoutDialog.kt index 72f7d730b..3d48aacf5 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/LogoutDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/LogoutDialog.kt @@ -8,7 +8,7 @@ import androidx.core.app.ActivityCompat import androidx.fragment.app.DialogFragment import com.github.libretube.R 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 class LogoutDialog : DialogFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/NavBarOptionsDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/NavBarOptionsDialog.kt index deb142297..181394b3a 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/NavBarOptionsDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/NavBarOptionsDialog.kt @@ -8,8 +8,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.github.libretube.R import com.github.libretube.databinding.DialogNavbarOptionsBinding +import com.github.libretube.helpers.NavBarHelper import com.github.libretube.ui.adapters.NavBarOptionsAdapter -import com.github.libretube.util.NavBarHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class NavBarOptionsDialog : DialogFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/RequireRestartDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/RequireRestartDialog.kt index 5011d2657..cf245f828 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/RequireRestartDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/RequireRestartDialog.kt @@ -5,7 +5,7 @@ import android.os.Bundle import androidx.core.app.ActivityCompat import androidx.fragment.app.DialogFragment import com.github.libretube.R -import com.github.libretube.util.NavigationHelper +import com.github.libretube.helpers.NavigationHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class RequireRestartDialog : DialogFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt index 67fbcf391..08550f499 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt @@ -13,8 +13,8 @@ import com.github.libretube.databinding.DialogShareBinding import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.enums.ShareObjectType import com.github.libretube.extensions.awaitQuery +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.obj.ShareData -import com.github.libretube.util.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class ShareDialog( diff --git a/app/src/main/java/com/github/libretube/ui/extensions/SetDrawables.kt b/app/src/main/java/com/github/libretube/ui/extensions/SetDrawables.kt new file mode 100644 index 000000000..5d9cc62c4 --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/extensions/SetDrawables.kt @@ -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) } + ) +} diff --git a/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt b/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt index d11f979ed..05bbede85 100644 --- a/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt +++ b/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt @@ -1,8 +1,7 @@ package com.github.libretube.ui.extensions import android.view.View -import android.view.ViewTreeObserver -import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams import com.github.libretube.db.DatabaseHolder.Companion.Database 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 * @return Whether the video is already watched more than 90% */ -fun View?.setWatchProgressLength(videoId: String, duration: Long): Boolean { - val view = this!! +fun View.setWatchProgressLength(videoId: String, duration: Long): Boolean { + updateLayoutParams { + matchConstraintPercentWidth = 0f + } + visibility = View.GONE val progress = try { awaitQuery { @@ -26,22 +28,13 @@ fun View?.setWatchProgressLength(videoId: String, duration: Long): Boolean { ?.toFloat()?.div(1000) if (progress == null || duration == 0L) { - view.visibility = View.GONE return false } - view.viewTreeObserver - .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { - 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 - } - }) + updateLayoutParams { + matchConstraintPercentWidth = progress / duration.toFloat() + } + visibility = View.VISIBLE return progress / duration.toFloat() > 0.9 } diff --git a/app/src/main/java/com/github/libretube/ui/extensions/SetupNotificationBell.kt b/app/src/main/java/com/github/libretube/ui/extensions/SetupNotificationBell.kt index 335192635..854569d98 100644 --- a/app/src/main/java/com/github/libretube/ui/extensions/SetupNotificationBell.kt +++ b/app/src/main/java/com/github/libretube/ui/extensions/SetupNotificationBell.kt @@ -1,7 +1,7 @@ package com.github.libretube.ui.extensions import com.github.libretube.R -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.PreferenceHelper import com.google.android.material.button.MaterialButton fun MaterialButton.setupNotificationBell(channelId: String) { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt index 40512ce0c..0a737e5dd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt @@ -1,5 +1,6 @@ package com.github.libretube.ui.fragments +import android.annotation.SuppressLint import android.content.ComponentName import android.content.Context import android.content.Intent @@ -17,22 +18,28 @@ import com.github.libretube.R import com.github.libretube.api.obj.StreamItem import com.github.libretube.databinding.FragmentAudioPlayerBinding import com.github.libretube.enums.ShareObjectType +import com.github.libretube.extensions.normalize 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.services.BackgroundMode import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.base.BaseFragment 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.PlayingQueueSheet 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 -class AudioPlayerFragment : BaseFragment() { +class AudioPlayerFragment : BaseFragment(), AudioPlayerOptions { private lateinit var binding: FragmentAudioPlayerBinding + private lateinit var audioHelper: AudioHelper + private val onTrackChangeListener: (StreamItem) -> Unit = { updateStreamInfo() } @@ -64,6 +71,7 @@ class AudioPlayerFragment : BaseFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + audioHelper = AudioHelper(requireContext()) Intent(activity, BackgroundMode::class.java).also { intent -> activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE) } @@ -78,6 +86,7 @@ class AudioPlayerFragment : BaseFragment() { return binding.root } + @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -129,13 +138,8 @@ class AudioPlayerFragment : BaseFragment() { ).show(childFragmentManager, null) } - binding.thumbnail.setOnClickListener { - val current = PlayingQueue.getCurrent() - current?.let { - VideoOptionsBottomSheet(it.url!!.toID(), it.title!!) - .show(childFragmentManager) - } - } + val listener = AudioPlayerThumbnailListener(requireContext(), this) + binding.thumbnail.setOnTouchListener(listener) // Listen for track changes due to autoplay or the notification PlayingQueue.addOnTrackChangedListener(onTrackChangeListener) @@ -146,6 +150,11 @@ class AudioPlayerFragment : BaseFragment() { // load the stream info into the UI 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() } + + 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)}" + } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt index 625f4eda9..b0cf372dd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt @@ -19,6 +19,7 @@ import com.github.libretube.enums.ShareObjectType import com.github.libretube.extensions.TAG import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.toID +import com.github.libretube.helpers.ImageHelper import com.github.libretube.obj.ChannelTabs import com.github.libretube.obj.ShareData 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.dialogs.ShareDialog import com.github.libretube.ui.extensions.setupSubscriptionButton -import com.github.libretube.util.ImageHelper import java.io.IOException -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import retrofit2.HttpException class ChannelFragment : BaseFragment() { @@ -45,8 +45,6 @@ class ChannelFragment : BaseFragment() { private var onScrollEnd: () -> Unit = {} - private val scope = CoroutineScope(Dispatchers.IO) - val possibleTabs = listOf( ChannelTabs.Channels, ChannelTabs.Playlists, @@ -227,18 +225,18 @@ class ChannelFragment : BaseFragment() { } private fun loadTab(tab: ChannelTab) { - scope.launch { + lifecycleScope.launch { val response = try { - RetrofitInstance.api.getChannelTab(tab.data) + withContext(Dispatchers.IO) { + RetrofitInstance.api.getChannelTab(tab.data) + } } catch (e: Exception) { return@launch } - val adapter = SearchAdapter(response.content.toMutableList()) - - runOnUiThread { - binding.channelRecView.adapter = adapter - } + val adapter = SearchAdapter() + binding.channelRecView.adapter = adapter + adapter.submitList(response.content) var tabNextPage = response.nextpage onScrollEnd = { @@ -284,18 +282,18 @@ class ChannelFragment : BaseFragment() { adapter: SearchAdapter, onNewNextPage: (String?) -> Unit ) { - scope.launch { + lifecycleScope.launch { val newContent = try { - RetrofitInstance.api.getChannelTab(tab.data, nextPage) + withContext(Dispatchers.IO) { + RetrofitInstance.api.getChannelTab(tab.data, nextPage) + } } catch (e: Exception) { Log.e(TAG(), "Exception: $e") null } - onNewNextPage.invoke(newContent?.nextpage) - runOnUiThread { - newContent?.content?.let { - adapter.updateItems(it) - } + onNewNextPage(newContent?.nextpage) + newContent?.content?.let { + adapter.submitList(adapter.currentList + it) } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt index bf8c5020d..2e9adacfe 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt @@ -58,7 +58,7 @@ class CommentsMainFragment : Fragment() { viewModel.commentsPage.observe(viewLifecycleOwner) { it ?: return@observe binding.progress.visibility = View.GONE - if (it.disabled == true) { + if (it.disabled) { binding.errorTV.visibility = View.VISIBLE return@observe } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt index 77bb48329..b23146823 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt @@ -19,13 +19,13 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.db.obj.DownloadWithItems import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.formatAsFileSize +import com.github.libretube.helpers.DownloadHelper import com.github.libretube.obj.DownloadStatus import com.github.libretube.receivers.DownloadReceiver import com.github.libretube.services.DownloadService import com.github.libretube.ui.adapters.DownloadsAdapter import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.viewholders.DownloadsViewHolder -import com.github.libretube.util.DownloadHelper import java.io.File import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest diff --git a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt index 97f75c4be..360a6cd7a 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt @@ -18,11 +18,11 @@ import com.github.libretube.databinding.FragmentHomeBinding import com.github.libretube.db.DatabaseHolder import com.github.libretube.extensions.awaitQuery 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.PlaylistsAdapter import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.base.BaseFragment -import com.github.libretube.util.LocaleHelper import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt index d61693c03..d868af09e 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt @@ -21,13 +21,13 @@ import com.github.libretube.db.DatabaseHolder import com.github.libretube.extensions.TAG import com.github.libretube.extensions.awaitQuery 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.PlaylistsAdapter import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.dialogs.CreatePlaylistDialog import com.github.libretube.ui.models.PlayerViewModel -import com.github.libretube.util.NavBarHelper -import com.github.libretube.util.PreferenceHelper class LibraryFragment : BaseFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index f9da8f9d8..e79c92976 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -35,6 +35,7 @@ import androidx.core.os.postDelayed import androidx.core.text.parseAsHtml import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels +import androidx.fragment.app.commit import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope 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.toStreamItem 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.VideoResolution 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.setupSubscriptionButton 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.PlayerViewModel import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.CommentsSheet 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.HtmlParser -import com.github.libretube.util.ImageHelper import com.github.libretube.util.LinkHandler -import com.github.libretube.util.NavigationHelper 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.PreferenceHelper -import com.github.libretube.util.SeekbarPreviewListener import com.github.libretube.util.TextUtils import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlayer @@ -1510,9 +1511,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { private fun killPlayerFragment() { viewModel.isFullscreen.value = false binding.playerMotionLayout.transitionToEnd() - mainActivity.supportFragmentManager.beginTransaction() - .remove(this) - .commit() + mainActivity.supportFragmentManager.commit { + remove(this@PlayerFragment) + } onDestroy() } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt index 63e4f9875..980f525fd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt @@ -26,13 +26,13 @@ import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.query import com.github.libretube.extensions.toID 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.base.BaseFragment import com.github.libretube.ui.extensions.serializable import com.github.libretube.ui.models.PlayerViewModel 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.TextUtils diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt index 54a84c9b8..860b019cd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt @@ -16,9 +16,11 @@ import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.obj.SearchHistoryItem import com.github.libretube.extensions.TAG import com.github.libretube.extensions.hideKeyboard +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.adapters.SearchAdapter 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 retrofit2.HttpException @@ -85,7 +87,9 @@ class SearchResultFragment : BaseFragment() { lifecycleScope.launchWhenCreated { view?.let { context?.hideKeyboard(it) } val response = try { - RetrofitInstance.api.getSearchResults(query, apiSearchFilter) + withContext(Dispatchers.IO) { + RetrofitInstance.api.getSearchResults(query, apiSearchFilter) + } } catch (e: IOException) { println(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") return@launchWhenCreated } - runOnUiThread { - searchAdapter = SearchAdapter(response.items.toMutableList()) - binding.searchRecycler.adapter = searchAdapter - binding.noSearchResult.isVisible = response.items.isEmpty() - } + searchAdapter = SearchAdapter() + binding.searchRecycler.adapter = searchAdapter + searchAdapter.submitList(response.items) + binding.noSearchResult.isVisible = response.items.isEmpty() nextPage = response.nextpage } } @@ -106,11 +109,13 @@ class SearchResultFragment : BaseFragment() { private fun fetchNextSearchItems() { lifecycleScope.launchWhenCreated { val response = try { - RetrofitInstance.api.getSearchResultsNextPage( - query, - apiSearchFilter, - nextPage!! - ) + withContext(Dispatchers.IO) { + RetrofitInstance.api.getSearchResultsNextPage( + query, + apiSearchFilter, + nextPage!! + ) + } } catch (e: IOException) { println(e) Log.e(TAG(), "IOException, you might not have internet connection") @@ -120,10 +125,8 @@ class SearchResultFragment : BaseFragment() { return@launchWhenCreated } nextPage = response.nextpage!! - kotlin.runCatching { - if (response.items.isNotEmpty()) { - searchAdapter.updateItems(response.items) - } + if (response.items.isNotEmpty()) { + searchAdapter.submitList(searchAdapter.currentList + response.items) } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt index 3cfecbb8a..526ec633e 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt @@ -13,13 +13,13 @@ import com.github.libretube.R import com.github.libretube.api.obj.StreamItem import com.github.libretube.constants.PreferenceKeys 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.SubscriptionChannelAdapter import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.sheets.BaseBottomSheet -import com.github.libretube.util.PreferenceHelper class SubscriptionsFragment : BaseFragment() { private lateinit var binding: FragmentSubscriptionsBinding diff --git a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt index 2a78b0496..ec2187af8 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt @@ -12,10 +12,10 @@ import com.github.libretube.R import com.github.libretube.api.RetrofitInstance import com.github.libretube.databinding.FragmentTrendsBinding import com.github.libretube.extensions.TAG +import com.github.libretube.helpers.LocaleHelper import com.github.libretube.ui.activities.SettingsActivity import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.base.BaseFragment -import com.github.libretube.util.LocaleHelper import com.google.android.material.snackbar.Snackbar import java.io.IOException import retrofit2.HttpException diff --git a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt index ec8293b70..f39eca543 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt @@ -19,12 +19,12 @@ import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.dpToPx 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.base.BaseFragment import com.github.libretube.ui.models.PlayerViewModel -import com.github.libretube.util.NavigationHelper import com.github.libretube.util.PlayingQueue -import com.github.libretube.util.ProxyHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class WatchHistoryFragment : BaseFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/interfaces/AudioPlayerOptions.kt b/app/src/main/java/com/github/libretube/ui/interfaces/AudioPlayerOptions.kt new file mode 100644 index 000000000..91a0198fb --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/interfaces/AudioPlayerOptions.kt @@ -0,0 +1,12 @@ +package com.github.libretube.ui.interfaces + +interface AudioPlayerOptions { + + fun onSingleTap() + + fun onLongTap() + + fun onSwipe(distanceY: Float) + + fun onSwipeEnd() +} diff --git a/app/src/main/java/com/github/libretube/ui/listeners/AudioPlayerThumbnailListener.kt b/app/src/main/java/com/github/libretube/ui/listeners/AudioPlayerThumbnailListener.kt new file mode 100644 index 000000000..05e747704 --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/listeners/AudioPlayerThumbnailListener.kt @@ -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" + } +} diff --git a/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt b/app/src/main/java/com/github/libretube/ui/listeners/PlayerGestureController.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/PlayerGestureController.kt rename to app/src/main/java/com/github/libretube/ui/listeners/PlayerGestureController.kt index 42150da11..f59d8aa3b 100644 --- a/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt +++ b/app/src/main/java/com/github/libretube/ui/listeners/PlayerGestureController.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.ui.listeners import android.annotation.SuppressLint import android.content.res.Configuration @@ -12,6 +12,7 @@ import android.view.ScaleGestureDetector import android.view.View import androidx.activity.viewModels import androidx.core.os.postDelayed +import com.github.libretube.helpers.PlayerHelper import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.interfaces.PlayerGestureOptions import com.github.libretube.ui.models.PlayerViewModel diff --git a/app/src/main/java/com/github/libretube/util/SeekbarPreviewListener.kt b/app/src/main/java/com/github/libretube/ui/listeners/SeekbarPreviewListener.kt similarity index 98% rename from app/src/main/java/com/github/libretube/util/SeekbarPreviewListener.kt rename to app/src/main/java/com/github/libretube/ui/listeners/SeekbarPreviewListener.kt index cad92c8a6..5740a9aa9 100644 --- a/app/src/main/java/com/github/libretube/util/SeekbarPreviewListener.kt +++ b/app/src/main/java/com/github/libretube/ui/listeners/SeekbarPreviewListener.kt @@ -1,4 +1,4 @@ -package com.github.libretube.util +package com.github.libretube.ui.listeners import android.graphics.Bitmap import android.view.View @@ -9,6 +9,7 @@ import androidx.core.math.MathUtils import androidx.core.view.updateLayoutParams import coil.request.ImageRequest import com.github.libretube.api.obj.PreviewFrames +import com.github.libretube.helpers.ImageHelper import com.github.libretube.obj.PreviewFrame import com.google.android.exoplayer2.ui.TimeBar diff --git a/app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt b/app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt index 03fdcfd1d..8dc4c846e 100644 --- a/app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt +++ b/app/src/main/java/com/github/libretube/ui/models/SubscriptionsViewModel.kt @@ -9,7 +9,7 @@ import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.Subscription import com.github.libretube.extensions.TAG 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.launch diff --git a/app/src/main/java/com/github/libretube/ui/preferences/AdvancedSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/AdvancedSettings.kt index 3c92363c8..61b3d3965 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/AdvancedSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/AdvancedSettings.kt @@ -6,9 +6,9 @@ import androidx.preference.ListPreference import androidx.preference.Preference import com.github.libretube.R 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.util.ImageHelper -import com.github.libretube.util.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder class AdvancedSettings : BasePreferenceFragment() { diff --git a/app/src/main/java/com/github/libretube/ui/preferences/AppearanceSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/AppearanceSettings.kt index 3e5aa8615..ecea86cc6 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/AppearanceSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/AppearanceSettings.kt @@ -7,12 +7,12 @@ import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.github.libretube.R import com.github.libretube.constants.PreferenceKeys +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.adapters.IconsSheetAdapter import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.dialogs.NavBarOptionsDialog import com.github.libretube.ui.dialogs.RequireRestartDialog import com.github.libretube.ui.sheets.IconsBottomSheet -import com.github.libretube.util.PreferenceHelper class AppearanceSettings : BasePreferenceFragment() { override val titleResourceId: Int = R.string.appearance diff --git a/app/src/main/java/com/github/libretube/ui/preferences/BackupRestoreSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/BackupRestoreSettings.kt index fc065ae99..9deee4061 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/BackupRestoreSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/BackupRestoreSettings.kt @@ -1,79 +1,79 @@ package com.github.libretube.ui.preferences -import android.net.Uri import android.os.Bundle -import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.CreateDocument +import androidx.lifecycle.lifecycleScope import androidx.preference.Preference 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.ui.base.BasePreferenceFragment import com.github.libretube.ui.dialogs.BackupDialog -import com.github.libretube.util.BackupHelper -import com.github.libretube.util.ImportHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.time.LocalDateTime import java.time.format.DateTimeFormatter class BackupRestoreSettings : BasePreferenceFragment() { private val backupDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH:mm:ss") + private var backupFile = BackupFile() override val titleResourceId: Int = R.string.backup_restore // backup and restore database - private lateinit var getBackupFile: ActivityResultLauncher - private lateinit var createBackupFile: ActivityResultLauncher - private var backupFile = BackupFile() + private val getBackupFile = registerForActivityResult(ActivityResultContracts.GetContent()) { + it?.let { + 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 */ - private lateinit var getSubscriptionsFile: ActivityResultLauncher - private lateinit var createSubscriptionsFile: ActivityResultLauncher + private val getSubscriptionsFile = registerForActivityResult( + 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 */ - private lateinit var getPlaylistsFile: ActivityResultLauncher - private lateinit var createPlaylistsFile: ActivityResultLauncher - - override fun onCreate(savedInstanceState: Bundle?) { - getSubscriptionsFile = - registerForActivityResult( - ActivityResultContracts.GetContent() - ) { uri -> - ImportHelper(requireActivity()).importSubscriptions(uri) + private val getPlaylistsFile = registerForActivityResult(ActivityResultContracts.GetContent()) { + it?.let { + lifecycleScope.launch(Dispatchers.IO) { + ImportHelper.importPlaylists(requireActivity(), it) } - createSubscriptionsFile = registerForActivityResult( - CreateDocument("application/json") - ) { uri -> - ImportHelper(requireActivity()).exportSubscriptions(uri) } - - getPlaylistsFile = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> - ImportHelper(requireActivity()).importPlaylists(uri) - } - - createPlaylistsFile = registerForActivityResult( - CreateDocument("application/json") - ) { uri -> - ImportHelper(requireActivity()).exportPlaylists(uri) - } - - getBackupFile = - registerForActivityResult( - ActivityResultContracts.GetContent() - ) { uri: Uri? -> - BackupHelper(requireContext()).restoreAdvancedBackup(uri) + } + private val createPlaylistsFile = registerForActivityResult(CreateDocument(JSON)) { + it?.let { + lifecycleScope.launch(Dispatchers.IO) { + ImportHelper.exportPlaylists(requireActivity(), it) } - - createBackupFile = registerForActivityResult( - CreateDocument("application/json") - ) { uri: Uri? -> - BackupHelper(requireContext()).createAdvancedBackup(uri, backupFile) } - - super.onCreate(savedInstanceState) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -116,8 +116,12 @@ class BackupRestoreSettings : BasePreferenceFragment() { val restoreAdvancedBackup = findPreference("restore") restoreAdvancedBackup?.setOnPreferenceClickListener { - getBackupFile.launch("application/json") + getBackupFile.launch(JSON) true } } + + companion object { + private const val JSON = "application/json" + } } diff --git a/app/src/main/java/com/github/libretube/ui/preferences/GeneralSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/GeneralSettings.kt index 5d28cf875..a698caef4 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/GeneralSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/GeneralSettings.kt @@ -7,10 +7,10 @@ import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.github.libretube.R 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.dialogs.RequireRestartDialog -import com.github.libretube.util.LocaleHelper -import com.github.libretube.util.PreferenceHelper class GeneralSettings : BasePreferenceFragment() { override val titleResourceId: Int = R.string.general diff --git a/app/src/main/java/com/github/libretube/ui/preferences/InstanceSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/InstanceSettings.kt index da14044ea..662e7034b 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/InstanceSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/InstanceSettings.kt @@ -15,12 +15,12 @@ import com.github.libretube.constants.PreferenceKeys import com.github.libretube.db.DatabaseHolder.Companion.Database import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.toastFromMainThread +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.dialogs.CustomInstanceDialog import com.github.libretube.ui.dialogs.DeleteAccountDialog import com.github.libretube.ui.dialogs.LoginDialog import com.github.libretube.ui.dialogs.LogoutDialog -import com.github.libretube.util.PreferenceHelper class InstanceSettings : BasePreferenceFragment() { override val titleResourceId: Int = R.string.instance diff --git a/app/src/main/java/com/github/libretube/ui/preferences/MainSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/MainSettings.kt index a9ff2410c..12bdfee16 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/MainSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/MainSettings.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.widget.Toast import androidx.annotation.StringRes import androidx.fragment.app.Fragment +import androidx.fragment.app.commitNow import androidx.preference.Preference import com.github.libretube.BuildConfig import com.github.libretube.R @@ -124,9 +125,9 @@ class MainSettings : BasePreferenceFragment() { } private fun navigateToSettingsFragment(newFragment: Fragment): Boolean { - parentFragmentManager.beginTransaction() - .replace(R.id.settings, newFragment) - .commitNow() + parentFragmentManager.commitNow { + replace(R.id.settings, newFragment) + } return true } } diff --git a/app/src/main/java/com/github/libretube/ui/preferences/NotificationSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/NotificationSettings.kt index 34807ebdf..3e23d33a4 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/NotificationSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/NotificationSettings.kt @@ -6,9 +6,9 @@ import androidx.preference.SwitchPreferenceCompat import androidx.work.ExistingPeriodicWorkPolicy import com.github.libretube.R import com.github.libretube.constants.PreferenceKeys +import com.github.libretube.helpers.NotificationHelper import com.github.libretube.ui.base.BasePreferenceFragment import com.github.libretube.ui.views.TimePickerPreference -import com.github.libretube.util.NotificationHelper class NotificationSettings : BasePreferenceFragment() { override val titleResourceId: Int = R.string.notifications diff --git a/app/src/main/java/com/github/libretube/ui/preferences/PlayerSettings.kt b/app/src/main/java/com/github/libretube/ui/preferences/PlayerSettings.kt index f77cb80a7..0587ca184 100644 --- a/app/src/main/java/com/github/libretube/ui/preferences/PlayerSettings.kt +++ b/app/src/main/java/com/github/libretube/ui/preferences/PlayerSettings.kt @@ -12,9 +12,9 @@ import androidx.preference.Preference import androidx.preference.SwitchPreferenceCompat import com.github.libretube.R 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.util.LocaleHelper -import com.github.libretube.util.PreferenceHelper class PlayerSettings : BasePreferenceFragment() { override val titleResourceId: Int = R.string.player diff --git a/app/src/main/java/com/github/libretube/ui/sheets/ChannelOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/ChannelOptionsBottomSheet.kt index f3edef523..eaf86e581 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/ChannelOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/ChannelOptionsBottomSheet.kt @@ -7,10 +7,10 @@ import com.github.libretube.api.RetrofitInstance import com.github.libretube.enums.ShareObjectType import com.github.libretube.extensions.TAG 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.ui.dialogs.ShareDialog -import com.github.libretube.util.BackgroundHelper -import com.github.libretube.util.NavigationHelper import kotlinx.coroutines.runBlocking /** diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt index caaa79898..01bc6ff15 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlaybackOptionsSheet.kt @@ -7,7 +7,7 @@ import android.view.ViewGroup import com.github.libretube.constants.PreferenceKeys import com.github.libretube.databinding.PlaybackBottomSheetBinding 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.PlaybackParameters diff --git a/app/src/main/java/com/github/libretube/ui/sheets/PlaylistOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/PlaylistOptionsBottomSheet.kt index f652a9246..835445bcd 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/PlaylistOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/PlaylistOptionsBottomSheet.kt @@ -11,11 +11,12 @@ import com.github.libretube.extensions.awaitQuery import com.github.libretube.extensions.query import com.github.libretube.extensions.toID import com.github.libretube.extensions.toPlaylistBookmark +import com.github.libretube.extensions.toastFromMainThread +import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.obj.ShareData import com.github.libretube.ui.dialogs.DeletePlaylistDialog import com.github.libretube.ui.dialogs.RenamePlaylistDialog import com.github.libretube.ui.dialogs.ShareDialog -import com.github.libretube.util.BackgroundHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -59,14 +60,23 @@ class PlaylistOptionsBottomSheet( val playlist = PlaylistsHelper.getPlaylist(playlistId) BackgroundHelper.playOnBackground( context = requireContext(), - videoId = playlist.relatedStreams!![0].url!!.toID(), + videoId = playlist.relatedStreams[0].url!!.toID(), playlistId = playlistId ) } } // Clone the playlist to the users Piped account getString(R.string.clonePlaylist) -> { - PlaylistsHelper.clonePlaylist(requireContext(), playlistId) + val appContext = context?.applicationContext + CoroutineScope(Dispatchers.IO).launch { + val playlistId = PlaylistsHelper.clonePlaylist( + requireContext().applicationContext, + playlistId + ) + appContext?.toastFromMainThread( + if (playlistId != null) R.string.playlistCloned else R.string.server_error + ) + } } // share the playlist getString(R.string.share) -> { diff --git a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt index fb75cf325..a6243de9c 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt @@ -5,11 +5,11 @@ import com.github.libretube.R import com.github.libretube.api.RetrofitInstance import com.github.libretube.enums.ShareObjectType import com.github.libretube.extensions.toStreamItem +import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.obj.ShareData import com.github.libretube.ui.dialogs.AddToPlaylistDialog import com.github.libretube.ui.dialogs.DownloadDialog import com.github.libretube.ui.dialogs.ShareDialog -import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.PlayingQueue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt b/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt index 54c6c1386..45b59a9ac 100644 --- a/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt +++ b/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt @@ -7,8 +7,8 @@ import android.os.Process import androidx.core.os.postDelayed import com.github.libretube.R import com.github.libretube.constants.PreferenceKeys +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.activities.MainActivity -import com.github.libretube.util.PreferenceHelper import com.google.android.material.snackbar.Snackbar object SleepTimer { diff --git a/app/src/main/java/com/github/libretube/ui/views/AppNameTextView.kt b/app/src/main/java/com/github/libretube/ui/views/AppNameTextView.kt index aad8a7f5a..848b62c42 100644 --- a/app/src/main/java/com/github/libretube/ui/views/AppNameTextView.kt +++ b/app/src/main/java/com/github/libretube/ui/views/AppNameTextView.kt @@ -4,7 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.util.TypedValue import androidx.appcompat.widget.AppCompatTextView -import com.github.libretube.util.ThemeHelper +import com.github.libretube.helpers.ThemeHelper class AppNameTextView : AppCompatTextView { constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet) diff --git a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt index 132654c0c..bc46f7f9c 100644 --- a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt +++ b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt @@ -23,19 +23,19 @@ import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding import com.github.libretube.databinding.PlayerGestureControlsViewBinding import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.normalize +import com.github.libretube.helpers.AudioHelper +import com.github.libretube.helpers.BrightnessHelper +import com.github.libretube.helpers.PlayerHelper import com.github.libretube.obj.BottomSheetItem import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.interfaces.OnlinePlayerOptions import com.github.libretube.ui.interfaces.PlayerGestureOptions import com.github.libretube.ui.interfaces.PlayerOptions +import com.github.libretube.ui.listeners.PlayerGestureController import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.PlaybackOptionsSheet -import com.github.libretube.util.AudioHelper -import com.github.libretube.util.BrightnessHelper -import com.github.libretube.util.PlayerGestureController -import com.github.libretube.util.PlayerHelper import com.github.libretube.util.PlayingQueue import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.Player @@ -196,7 +196,7 @@ internal class CustomExoPlayerView( } private fun cancelHideControllerTask() { - handler.removeCallbacksAndMessages(HIDE_CONTROLLER_TOKEN) + handler?.removeCallbacksAndMessages(HIDE_CONTROLLER_TOKEN) } override fun hideController() { @@ -618,10 +618,8 @@ internal class CustomExoPlayerView( player?.let { player -> if (player.isPlaying) { player.pause() - if (!isControllerFullyVisible) showController() } else { player.play() - if (isControllerFullyVisible) hideController() } } } diff --git a/app/src/main/java/com/github/libretube/ui/views/CustomSwipeToRefresh.kt b/app/src/main/java/com/github/libretube/ui/views/CustomSwipeToRefresh.kt index 733e7ea1e..8d2d7d1f2 100644 --- a/app/src/main/java/com/github/libretube/ui/views/CustomSwipeToRefresh.kt +++ b/app/src/main/java/com/github/libretube/ui/views/CustomSwipeToRefresh.kt @@ -8,7 +8,8 @@ import android.view.MotionEvent.ACTION_MOVE import android.view.ViewConfiguration import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.github.libretube.R -import com.github.libretube.util.ThemeHelper +import com.github.libretube.helpers.ThemeHelper +import com.google.android.material.elevation.SurfaceColors import kotlin.math.abs class CustomSwipeToRefresh(context: Context?, attrs: AttributeSet?) : @@ -19,7 +20,7 @@ class CustomSwipeToRefresh(context: Context?, attrs: AttributeSet?) : init { setColorSchemeColors(ThemeHelper.getThemeColor(this.context, R.attr.colorPrimary)) setProgressBackgroundColorSchemeColor( - ThemeHelper.getThemeColor(this.context, R.attr.colorSurface) + SurfaceColors.getColorForElevation(this.context, 20f) ) } diff --git a/app/src/main/java/com/github/libretube/ui/views/DrawableTextView.kt b/app/src/main/java/com/github/libretube/ui/views/DrawableTextView.kt new file mode 100644 index 000000000..0d5909310 --- /dev/null +++ b/app/src/main/java/com/github/libretube/ui/views/DrawableTextView.kt @@ -0,0 +1,75 @@ +package com.github.libretube.ui.views + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.Gravity +import androidx.appcompat.widget.AppCompatTextView +import com.github.libretube.R + +/** + * TextView with custom sizable drawable support. + * It may only be used for icons as it gives same width and height to the drawable. + */ +class DrawableTextView( + context: Context, + attrs: AttributeSet? = null +) : AppCompatTextView(context, attrs) { + + private var drawableStartDimen = 0F + private var drawableTopDimen = 0F + private var drawableEndDimen = 0F + private var drawableBottomDimen = 0F + + init { + val ta = getContext().obtainStyledAttributes(attrs, R.styleable.DrawableTextView, 0, 0) + try { + drawableStartDimen = getDimen(ta, R.styleable.DrawableTextView_drawableStartDimen) + drawableTopDimen = getDimen(ta, R.styleable.DrawableTextView_drawableTopDimen) + drawableEndDimen = getDimen(ta, R.styleable.DrawableTextView_drawableEndDimen) + drawableBottomDimen = getDimen(ta, R.styleable.DrawableTextView_drawableBottomDimen) + + gravity = ta.getInt( + R.styleable.DrawableTextView_android_gravity, + Gravity.CENTER_VERTICAL + ) + compoundDrawablePadding = ta.getDimensionPixelOffset( + R.styleable.DrawableTextView_android_drawablePadding, + 20 + ) + } finally { + ta.recycle() + } + } + + private fun getDimen(ta: TypedArray, index: Int): Float { + return ta.getDimensionPixelOffset(index, 0).toFloat() + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + val relDrawables = adjust(compoundDrawablesRelative) + setCompoundDrawablesRelative( + relDrawables[0], + relDrawables[1], + relDrawables[2], + relDrawables[3] + ) + } + + private fun adjust(drawables: Array): Array { + listOf(drawableStartDimen, drawableTopDimen, drawableEndDimen, drawableBottomDimen).forEachIndexed { index, dimen -> + drawables[index] = drawables[index].getAdjustedDrawable(dimen) + } + return drawables + } + + private fun Drawable?.getAdjustedDrawable(dimen: Float): Drawable? { + this ?: return null + dimen.toInt().let { + if (it > 0) setBounds(0, 0, it, it) + } + return this + } +} diff --git a/app/src/main/java/com/github/libretube/ui/views/MarkableTimeBar.kt b/app/src/main/java/com/github/libretube/ui/views/MarkableTimeBar.kt index ffd97eefb..36615a265 100644 --- a/app/src/main/java/com/github/libretube/ui/views/MarkableTimeBar.kt +++ b/app/src/main/java/com/github/libretube/ui/views/MarkableTimeBar.kt @@ -11,8 +11,8 @@ import com.github.libretube.R import com.github.libretube.api.obj.Segment import com.github.libretube.constants.PreferenceKeys import com.github.libretube.extensions.dpToPx -import com.github.libretube.util.PreferenceHelper -import com.github.libretube.util.ThemeHelper +import com.github.libretube.helpers.PreferenceHelper +import com.github.libretube.helpers.ThemeHelper import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.ui.DefaultTimeBar diff --git a/app/src/main/java/com/github/libretube/ui/views/SliderPreference.kt b/app/src/main/java/com/github/libretube/ui/views/SliderPreference.kt index 1f927013a..6aee66187 100644 --- a/app/src/main/java/com/github/libretube/ui/views/SliderPreference.kt +++ b/app/src/main/java/com/github/libretube/ui/views/SliderPreference.kt @@ -7,7 +7,7 @@ import androidx.preference.Preference import com.github.libretube.R import com.github.libretube.databinding.DialogSliderBinding import com.github.libretube.extensions.round -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.PreferenceHelper import com.google.android.material.dialog.MaterialAlertDialogBuilder /** diff --git a/app/src/main/java/com/github/libretube/ui/views/TimePickerPreference.kt b/app/src/main/java/com/github/libretube/ui/views/TimePickerPreference.kt index f319b2ac5..248f527a3 100644 --- a/app/src/main/java/com/github/libretube/ui/views/TimePickerPreference.kt +++ b/app/src/main/java/com/github/libretube/ui/views/TimePickerPreference.kt @@ -5,7 +5,7 @@ import android.text.format.DateFormat.is24HourFormat import android.util.AttributeSet import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference -import com.github.libretube.util.PreferenceHelper +import com.github.libretube.helpers.PreferenceHelper import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import java.time.LocalTime diff --git a/app/src/main/java/com/github/libretube/util/ClipboardHelper.kt b/app/src/main/java/com/github/libretube/util/ClipboardHelper.kt deleted file mode 100644 index 47ad931ff..000000000 --- a/app/src/main/java/com/github/libretube/util/ClipboardHelper.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.libretube.util - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import com.github.libretube.R - -class ClipboardHelper( - private val context: Context -) { - fun save(text: String) { - val clipboard: ClipboardManager = - context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText(context.getString(R.string.copied), text) - clipboard.setPrimaryClip(clip) - } -} diff --git a/app/src/main/java/com/github/libretube/util/DataSaverMode.kt b/app/src/main/java/com/github/libretube/util/DataSaverMode.kt index 619ce58d2..29cf85aff 100644 --- a/app/src/main/java/com/github/libretube/util/DataSaverMode.kt +++ b/app/src/main/java/com/github/libretube/util/DataSaverMode.kt @@ -2,6 +2,8 @@ package com.github.libretube.util import android.content.Context import com.github.libretube.constants.PreferenceKeys +import com.github.libretube.helpers.NetworkHelper +import com.github.libretube.helpers.PreferenceHelper object DataSaverMode { fun isEnabled(context: Context): Boolean { diff --git a/app/src/main/java/com/github/libretube/util/ExceptionHandler.kt b/app/src/main/java/com/github/libretube/util/ExceptionHandler.kt index 3d790d7c8..e6ab250df 100644 --- a/app/src/main/java/com/github/libretube/util/ExceptionHandler.kt +++ b/app/src/main/java/com/github/libretube/util/ExceptionHandler.kt @@ -1,5 +1,7 @@ package com.github.libretube.util +import com.github.libretube.helpers.PreferenceHelper + class ExceptionHandler( private val defaultExceptionHandler: Thread.UncaughtExceptionHandler? ) : Thread.UncaughtExceptionHandler { diff --git a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt index 234a1cbd2..761130a6f 100644 --- a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt +++ b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt @@ -24,6 +24,8 @@ import com.github.libretube.compat.PendingIntentCompat import com.github.libretube.constants.BACKGROUND_CHANNEL_ID import com.github.libretube.constants.IntentData import com.github.libretube.constants.PLAYER_NOTIFICATION_ID +import com.github.libretube.helpers.ImageHelper +import com.github.libretube.helpers.PlayerHelper import com.github.libretube.ui.activities.MainActivity import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.Player diff --git a/app/src/main/java/com/github/libretube/util/TextUtils.kt b/app/src/main/java/com/github/libretube/util/TextUtils.kt index 1da931d67..26d29d5f9 100644 --- a/app/src/main/java/com/github/libretube/util/TextUtils.kt +++ b/app/src/main/java/com/github/libretube/util/TextUtils.kt @@ -41,11 +41,7 @@ object TextUtils { * @return Time in seconds */ fun parseTimestamp(t: String): Long? { - if (t.all { c -> c.isDigit() }) { - return t.toLong() - } - - return Duration.parseOrNull(t)?.inWholeSeconds + return t.toLongOrNull() ?: Duration.parseOrNull(t)?.inWholeSeconds } /** diff --git a/app/src/main/java/com/github/libretube/workers/NotificationWorker.kt b/app/src/main/java/com/github/libretube/workers/NotificationWorker.kt index 2ea3e01bc..004f20e6d 100644 --- a/app/src/main/java/com/github/libretube/workers/NotificationWorker.kt +++ b/app/src/main/java/com/github/libretube/workers/NotificationWorker.kt @@ -8,7 +8,7 @@ import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.work.Worker +import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.github.libretube.R import com.github.libretube.api.SubscriptionHelper @@ -19,17 +19,18 @@ import com.github.libretube.constants.PUSH_CHANNEL_ID import com.github.libretube.constants.PreferenceKeys import com.github.libretube.extensions.TAG import com.github.libretube.extensions.toID +import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.views.TimePickerPreference -import com.github.libretube.util.PreferenceHelper import java.time.LocalTime -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext /** * The notification worker which checks for new streams in a certain frequency */ class NotificationWorker(appContext: Context, parameters: WorkerParameters) : - Worker(appContext, parameters) { + CoroutineWorker(appContext, parameters) { private val notificationManager = NotificationManagerCompat.from(appContext) @@ -41,7 +42,7 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) : DOWNLOAD_PROGRESS_NOTIFICATION_ID } - override fun doWork(): Result { + override suspend fun doWork(): Result { if (!checkTime()) Result.success() // check whether there are new streams and notify if there are some val result = checkForNewStreams() @@ -82,71 +83,68 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) : /** * check whether new streams are available in subscriptions */ - private fun checkForNewStreams(): Boolean { - var success = true - + private suspend fun checkForNewStreams(): Boolean { Log.d(TAG(), "Work manager started") - runBlocking { - // fetch the users feed - val videoFeed = try { + // fetch the users feed + val videoFeed = try { + withContext(Dispatchers.IO) { SubscriptionHelper.getFeed() - } catch (e: Exception) { - success = false - return@runBlocking } - - val lastSeenStreamId = PreferenceHelper.getLastSeenVideoId() - val latestFeedStreamId = videoFeed.firstOrNull()?.url?.toID() ?: return@runBlocking - - // first time notifications are enabled or no new video available - if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) { - PreferenceHelper.setLatestVideoId(lastSeenStreamId) - return@runBlocking - } - - // filter the new videos until the last seen video in the feed - val newStreams = videoFeed.takeWhile { it.url!!.toID() != lastSeenStreamId } - - // return if the previous video didn't get found - if (newStreams.isEmpty()) return@runBlocking - - // hide for notifications unsubscribed channels - val channelsToIgnore = PreferenceHelper.getIgnorableNotificationChannels() - val filteredVideos = newStreams.filter { - channelsToIgnore.none { channelId -> - channelId == it.uploaderUrl?.toID() - } - } - - // group the new streams by the uploader - val channelGroups = filteredVideos.groupBy { it.uploaderUrl } - - Log.d(TAG(), "Create notifications for new videos") - - // create a notification for each new stream - channelGroups.forEach { (_, streams) -> - createNotification( - group = streams.first().uploaderUrl!!.toID(), - title = streams.first().uploaderName.toString(), - urlPath = streams.first().uploaderUrl!!, - isGroupSummary = true - ) - - streams.forEach { streamItem -> - createNotification( - title = streamItem.title.toString(), - description = streamItem.uploaderName.toString(), - urlPath = streamItem.url!!, - group = streamItem.uploaderUrl!!.toID() - ) - } - } - // save the latest streams that got notified about - PreferenceHelper.setLatestVideoId(videoFeed.first().url!!.toID()) + } catch (e: Exception) { + return false } + + val lastSeenStreamId = PreferenceHelper.getLastSeenVideoId() + val latestFeedStreamId = videoFeed.firstOrNull()?.url?.toID() ?: return true + + // first time notifications are enabled or no new video available + if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) { + PreferenceHelper.setLatestVideoId(lastSeenStreamId) + return true + } + + // filter the new videos until the last seen video in the feed + val newStreams = videoFeed.takeWhile { it.url!!.toID() != lastSeenStreamId } + + // return if the previous video didn't get found + if (newStreams.isEmpty()) return true + + // hide for notifications unsubscribed channels + val channelsToIgnore = PreferenceHelper.getIgnorableNotificationChannels() + val filteredVideos = newStreams.filter { + channelsToIgnore.none { channelId -> + channelId == it.uploaderUrl?.toID() + } + } + + // group the new streams by the uploader + val channelGroups = filteredVideos.groupBy { it.uploaderUrl } + + Log.d(TAG(), "Create notifications for new videos") + + // create a notification for each new stream + channelGroups.forEach { (_, streams) -> + createNotification( + group = streams.first().uploaderUrl!!.toID(), + title = streams.first().uploaderName.toString(), + urlPath = streams.first().uploaderUrl!!, + isGroupSummary = true + ) + + streams.forEach { streamItem -> + createNotification( + title = streamItem.title.toString(), + description = streamItem.uploaderName.toString(), + urlPath = streamItem.url!!, + group = streamItem.uploaderUrl!!.toID() + ) + } + } + // save the latest streams that got notified about + PreferenceHelper.setLatestVideoId(videoFeed.first().url!!.toID()) // return whether the work succeeded - return success + return true } /** diff --git a/app/src/main/res/drawable/ic_dislike.xml b/app/src/main/res/drawable/ic_dislike.xml new file mode 100644 index 000000000..a9d9224ac --- /dev/null +++ b/app/src/main/res/drawable/ic_dislike.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_mastodon.xml b/app/src/main/res/drawable/ic_mastodon.xml new file mode 100644 index 000000000..87be939df --- /dev/null +++ b/app/src/main/res/drawable/ic_mastodon.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ic_twitter.xml b/app/src/main/res/drawable/ic_twitter.xml deleted file mode 100644 index b27dbedcf..000000000 --- a/app/src/main/res/drawable/ic_twitter.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index e06e93f56..ece2000c5 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -1,6 +1,7 @@ @@ -16,14 +17,15 @@ app:navigationIcon="?homeAsUpIndicator" app:title="@string/about" /> - + android:src="@drawable/ic_launcher_lockscreen" + app:tint="?attr/colorSecondary" + tools:ignore="ContentDescription" /> - - - - - - - + @@ -57,17 +52,10 @@ android:id="@+id/github" style="@style/AboutCard"> - - - - - - - + @@ -75,17 +63,10 @@ android:id="@+id/piped" style="@style/AboutCard"> - - - - - - - + @@ -93,17 +74,10 @@ android:id="@+id/translate" style="@style/AboutCard"> - - - - - - - + @@ -111,17 +85,10 @@ android:id="@+id/license" style="@style/AboutCard"> - - - - - - - + @@ -129,17 +96,10 @@ android:id="@+id/device" style="@style/AboutCard"> - - - - - - - + diff --git a/app/src/main/res/layout/activity_help.xml b/app/src/main/res/layout/activity_help.xml index e7fea8bd7..729a33e2d 100644 --- a/app/src/main/res/layout/activity_help.xml +++ b/app/src/main/res/layout/activity_help.xml @@ -1,6 +1,7 @@ @@ -14,32 +15,26 @@ android:layout_width="match_parent" android:layout_height="wrap_content" app:navigationIcon="?homeAsUpIndicator" - app:title="@string/community" /> + app:title="@string/help" /> - + android:src="@drawable/ic_launcher_lockscreen" + app:tint="?attr/colorSecondary" + tools:ignore="ContentDescription" /> - - - - - - - + @@ -47,17 +42,24 @@ android:id="@+id/matrix" style="@style/AboutCard"> - + - + - - + + + @@ -65,17 +67,11 @@ android:id="@+id/telegram" style="@style/AboutCard"> - - - - - - - + @@ -83,17 +79,11 @@ android:id="@+id/discord" style="@style/AboutCard"> - - - - - - - + @@ -101,35 +91,11 @@ android:id="@+id/reddit" style="@style/AboutCard"> - - - - - - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/bottom_sheet_item.xml b/app/src/main/res/layout/bottom_sheet_item.xml index dbcd835d2..76231da51 100644 --- a/app/src/main/res/layout/bottom_sheet_item.xml +++ b/app/src/main/res/layout/bottom_sheet_item.xml @@ -1,27 +1,13 @@ - - - - - - - \ No newline at end of file + android:paddingVertical="12dp" + android:gravity="center_vertical" + android:drawablePadding="25dp" + android:textSize="16sp" + tools:drawableStart="@drawable/ic_download" + tools:text="Option" /> \ No newline at end of file diff --git a/app/src/main/res/layout/comments_row.xml b/app/src/main/res/layout/comments_row.xml index 1c619b9f7..6f27c1706 100644 --- a/app/src/main/res/layout/comments_row.xml +++ b/app/src/main/res/layout/comments_row.xml @@ -11,126 +11,118 @@ android:layout_height="wrap_content" android:layout_marginTop="10dp" android:animateLayoutChanges="true" - android:orientation="vertical" + android:layout_marginBottom="16dp" + android:orientation="horizontal" android:paddingStart="15dp" android:paddingEnd="15dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"> + + - - + android:orientation="vertical"> - - - - - - - - - + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal"> + android:ellipsize="end" + android:maxLines="2" + android:textAlignment="viewStart" + android:textSize="14sp" + android:textColor="?android:attr/textColorSecondary" + tools:text="Octacat • 10h" /> - + - + + - + - + - + - + - + + diff --git a/app/src/main/res/layout/comments_sheet.xml b/app/src/main/res/layout/comments_sheet.xml index 197927b86..c79509a14 100644 --- a/app/src/main/res/layout/comments_sheet.xml +++ b/app/src/main/res/layout/comments_sheet.xml @@ -49,6 +49,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginHorizontal="10dp" + android:textAlignment="viewStart" tools:text="Title" /> + + + + + + + + + + + android:orientation="vertical"> @@ -82,6 +84,7 @@ android:text="@string/playlists" android:padding="8dp" android:textSize="18sp" + android:textAlignment="viewStart" android:textStyle="bold" /> + android:nestedScrollingEnabled="false" /> @@ -147,7 +150,7 @@ android:id="@+id/bookmarks_recView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:nestedScrollingEnabled="false"/> + android:nestedScrollingEnabled="false" /> diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 73c3f7e2f..01fb7a38f 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -29,6 +29,8 @@ android:id="@+id/player_title_layout" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?selectableItemBackground" + android:layout_gravity="center_vertical" android:orientation="vertical"> - + android:layout_marginHorizontal="5dp" + android:drawablePadding="5dp" + app:drawableStartCompat="@drawable/ic_like" + app:drawableStartDimen="12dp" + tools:text="4.2K" /> - - - - - - - - - + @@ -210,26 +197,25 @@ - - - - - - - + android:paddingHorizontal="8dp" + android:text="@string/comments" + android:textAlignment="viewStart" + app:drawableEndCompat="@drawable/ic_arrow_up_down" /> @@ -356,29 +326,19 @@ app:strokeWidth="1dp" tools:ignore="RtlSymmetry"> - - - - - - - + android:visibility="gone" + android:text="@string/skip_segment" + android:textColor="@android:color/white" + android:textSize="18sp" + android:drawablePadding="20dp" + app:drawableEndCompat="@drawable/ic_next" + app:drawableEndDimen="20dp" + app:drawableTint="@android:color/white" /> diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml index 94ed78fbd..8ecf17855 100644 --- a/app/src/main/res/layout/fragment_subscriptions.xml +++ b/app/src/main/res/layout/fragment_subscriptions.xml @@ -69,27 +69,14 @@ android:layout_marginBottom="12dp" app:cardCornerRadius="18dp"> - - - - - - - + android:layout_margin="8dp" + android:paddingHorizontal="8dp" + android:text="@string/subscriptions" + android:textAlignment="viewStart" + app:drawableEndCompat="@drawable/ic_arrow_up_down" /> diff --git a/app/src/main/res/layout/searchhistory_row.xml b/app/src/main/res/layout/searchhistory_row.xml index e8cb42e26..2e4de6833 100644 --- a/app/src/main/res/layout/searchhistory_row.xml +++ b/app/src/main/res/layout/searchhistory_row.xml @@ -1,40 +1,34 @@ - + android:gravity="center_vertical" + android:paddingVertical="8dp" + android:layout_height="wrap_content"> - - - + android:textAlignment="viewStart" + android:drawablePadding="15dp" + android:layout_marginEnd="10dp" + app:drawableStartCompat="@drawable/ic_history" + tools:text="Suggestion item" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/searchsuggestion_row.xml b/app/src/main/res/layout/searchsuggestion_row.xml index 589932d99..2ec8814db 100644 --- a/app/src/main/res/layout/searchsuggestion_row.xml +++ b/app/src/main/res/layout/searchsuggestion_row.xml @@ -1,35 +1,29 @@ - + android:gravity="center_vertical" + android:paddingVertical="8dp" + android:layout_height="wrap_content"> - - - + android:textAlignment="viewStart" + android:drawablePadding="15dp" + android:layout_marginEnd="10dp" + app:drawableStartCompat="@drawable/ic_search" + tools:text="Suggestion item" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/trending_row.xml b/app/src/main/res/layout/trending_row.xml index 7ebb58466..4a3580733 100644 --- a/app/src/main/res/layout/trending_row.xml +++ b/app/src/main/res/layout/trending_row.xml @@ -20,16 +20,15 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - + android:layout_height="match_parent"> + + + app:cardElevation="0dp" + app:layout_constraintBottom_toTopOf="@id/watch_progress" + app:layout_constraintEnd_toEndOf="parent"> - - + android:layout_height="5dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + @@ -73,6 +75,7 @@ android:maxLines="2" android:textSize="15sp" android:textStyle="bold" + android:textAlignment="viewStart" app:layout_constraintEnd_toEndOf="@+id/thumbnailcard" app:layout_constraintStart_toEndOf="@+id/channel_image" app:layout_constraintTop_toBottomOf="@+id/thumbnailcard" @@ -85,6 +88,7 @@ android:ellipsize="end" android:maxLines="2" android:paddingBottom="16dp" + android:textAlignment="viewStart" app:layout_constraintEnd_toEndOf="@+id/textView_title" app:layout_constraintStart_toStartOf="@+id/textView_title" app:layout_constraintTop_toBottomOf="@+id/textView_title" diff --git a/app/src/main/res/layout/video_row.xml b/app/src/main/res/layout/video_row.xml index 165098b27..ba752d7c6 100644 --- a/app/src/main/res/layout/video_row.xml +++ b/app/src/main/res/layout/video_row.xml @@ -25,17 +25,15 @@ app:layout_constraintVertical_bias="0.0" app:strokeWidth="0dp"> - + android:layout_height="match_parent"> - + + app:cardElevation="0dp" + app:layout_constraintBottom_toTopOf="@id/watch_progress" + app:layout_constraintEnd_toEndOf="parent"> - - + style="@style/WatchProgress" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + @@ -73,6 +74,7 @@ android:layout_height="wrap_content" android:layout_marginStart="8dp" android:ellipsize="end" + android:textAlignment="viewStart" android:maxLines="2" app:layout_constraintEnd_toStartOf="@id/delete_video" app:layout_constraintStart_toEndOf="@id/thumbnail_card" @@ -83,6 +85,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" + android:textAlignment="viewStart" android:maxLines="1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/thumbnail_card" @@ -97,7 +100,8 @@ android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline" - app:layout_constraintTop_toBottomOf="@id/video_info"> + app:layout_constraintTop_toBottomOf="@id/video_info" + android:layout_gravity="start|center_vertical"> diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index af132d9f1..3a8fd743c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -216,11 +216,6 @@ افقي عمودي المجتمع - ديسكورد - المصفوفة - تيليجرام - ريديت - تويتر يرجى تشغيل Wi-Fi أو بيانات الجوال للاتصال بالإنترنت. فتح … الفصول @@ -450,4 +445,5 @@ تخطي الصمت التعليمات الأسئلة المتداولة + الردود \ No newline at end of file diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index d9356e230..164be0eea 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -5,7 +5,7 @@ Axtarış Videolar Abunə Ol - Abunəlikdən çıx + Abunə olma Paylaş Saxla Yüklə @@ -15,13 +15,13 @@ Qeydiyyatdan keç Çıx Ləğv et - Daxil olundu. + Giriş edildi. Çıxıldı. Qeydiyyatdan keçilib. İndi kanallara abunə ola bilərsiniz. Artıq daxil olunub.İstəsəniz hesabınızdan çıxa bilərsiniz. Zəhmət olmasa, daxil ol və təkrar cəhd et. Ev - Abunəliklər + Abunələr Kitabxana Seç… Fərdi @@ -38,7 +38,7 @@ YT Musiqi Mahnıları YT Musiqi Videoları YT Musiqi Pleylistləri - Defolt Panel + Standart Panel SponsorBlok https://sponsor.ajay.app API\'sini istifadə edir Həqiqi məzmunu olmayan aralıq. Fasilə, statik çərçivə, təkrarlanan animasiya ola bilər. Məlumat ehtiva edən keçidlər üçün istifadə edilməməlidir. @@ -54,33 +54,33 @@ Bitdi. Şərhlər Əvvəlcə İnternetə Qoşul. - Yenidən Cəhd Et + Təkrar Cəhd Et Seqment ötürüldü Ödənişli reklam, tövsiyələr və birbaşa reklamlar.Öz reklamı və ya səbəblərə, yaradıcılara, veb saytlara və məhsullara pulsuz orijinal elanlar üçün deyil. İnteraksiya xatırladıcı(bəyən və abunə ol) Fasilə/Giriş Animasiyası Daxil ol/qeydiyyatdan keç - Zəhmət olmasa, əvvəlcə tənzimləmələrdə daxil ol və ya qeydiyyatdan keç. + Zəhmət olmasa, əvvəlcə tənzimləmələrdə giriş et və ya qeydiyyatdan keç. Abunə olundu Əvvəlcə bəzi kanallara abunə ol. Bu yayımı endirmək mümkün deyil. Endirmə tamamlandı. - Başqa endirmə artıq davam edir, zəhmət olmasa, bitənə qədər gözlə. + Artıq digər endirmə davam edir, zəhmət olmasa, bitənə qədər gözlə. Endirmə alınmadı. VLC\'də Aç VLC-də açmaq mümkün olmadı. O, quraşdırılmamış ola bilər. Abunəlikləri idxal et YouTube və ya NewPipe\'dan Tema - Serverlə problem yaşanır. Başqa nümunə cəhd edilsinmi\? + Serverlə problem yaşanır. Başqa nümunə sınansın\? Şəbəkə xətası. Nəsə xəta baş verdi. İstifadəçi adı və şifrə daxil etməlisiniz. Bu Piped hesabı üçündür. Video görüntü imkanı Şəbəkə sütunları - Burada heç nə yoxdur. + Bura boşdur. Pleylisti sil Pleylist silinsinmi\? Pleylist yarat @@ -119,7 +119,7 @@ %1$s Versiyası Mövcuddur Onu endirmək üçün GitHub\'da buraxılışlara keçilsinmi\? Xarici Görünüş - Davranma + Davranış Yüklənənlər Video formatı Səs və video birlikdə endirildisə, faylların dönüşdürülməsi. @@ -132,9 +132,9 @@ Oynatma sürəti Qabaqcıl Oynadıcı - Tətbiqi zövqünüzə uyğunlaşdırın. + Tətbiqi zövqünüzə görə uyğunlaşdırın. Yüklənənlər və sıfırla - Yeniləmə üçün yoxla + Yeniləmək üçün yoxla Ən son versiyanı işlədirsiniz. Canlı Müəlliflər @@ -148,7 +148,7 @@ Daxili yaddaş Endirilmiş medianın saxlanıldığı qovluğun adı. %1$s baxış - Defolt + Standart İtmiş miras Dəbdəbəli yanğın Pərgar meyli @@ -188,14 +188,14 @@ \"Trenddə olan\" səhifəsini gizlət Keyfiyyət Davranış - Defolt tənzimləmələr və davranış + Standartlar və davranış Axtarış artımı Nümunə üçün URL - Cari videodan sonra növbəti videonu avtomatik oynat. + Hazırkı videodan sonra növbəti videonu avtomatik oynat. Avtomatik fasilə Ekran söndürüldükdə oynatmanı dayandır. Pleylisti klonla - İlkin tənzimləmələri qaytar + Standartları qaytar Bütün tənzimləmələr sıfırlansın və sistemdən çıxılsın\? Hesab Bərpa Et @@ -204,7 +204,7 @@ Baxış Tarixçəsi Mövqe xatırlat Son oynatma mövqeyindən davam etdir - Təsdiqləyici nümunəni seç + Təsdiqləyici nümunə seç Təsdiqlənmiş dəvətlər üçün fərqli nümunə istifadə et. Təsdiqləyici nümunə GitHub @@ -216,13 +216,8 @@ Portret Landşaft Cəmiyyət - Matrix - Telegram - Reddit - Twitter - Discord Aç… - Lütfən, İnternetə qoşulmaq üçün Wi-Fi və ya mobil məlumatı açın. + Zəhmət olmasa, İnternetə qoşulmaq üçün Wi-Fi və ya mobil məlumatı açın. Bölmələr Oynatma sürəti Yeni dəyişiklikləri işlətmək üçün tətbiqi yenidən başlat. @@ -237,10 +232,10 @@ Təmiz tema Təmiz ağ/qara tema Xarici oynadıcı tapılmadı. Zəhmət olmasa, birinin quraşdırıldığından əmin olun. - Məlumata qənaət rejimi + Məlumat qənaəti rejimi Cihaz fırladıldıqda tam ekran oynat. Axtarışları saxla - Baxılan videoları yerli olaraq saxla + Baxılan videoların yerli olaraq saxlanılması Sıfırla Sistem altyazı üsulu Altyazılar @@ -302,7 +297,7 @@ Yaxınlaşdır Uyğunlaşdır Heç biri - Cari + Hazırkı Nüsxələ & bərpa et Nüsxələ Şəkil içində Şəkil @@ -337,10 +332,10 @@ Səs oynadıcını idarə etmək üçün düymələrlə bildiriş göstərir. Sonrakında oynat Naviqasiya çubuğu - Lütfən, ən az bir element seç - Trend\'in cari ölkə üçün əlçatan olmadığı görünür.Lütfən, tənzimləmələrdə başqasını seç. + Xahiş edirik, ən az bir element seç + Trend\'in hazırkı ölkə üçün əlçatan olmadığı görünür.Xahiş edirik, tənzimləmələrdə başqasını seç. HLS\'ni 1080p ilə məhdudlaşdır - Defolt + Standart Qabaqcıl, yükləmə intervalı ölçüsü Daha aşağı dəyər ilkin video yükləməni sürətləndirə bilər. baxış @@ -360,7 +355,7 @@ Vaxt çubuğunda seqmentləri nişanla. Canlı yayımlar Alternativ video tərtibatı - Defolt işıqlı + Standart işıqlı Pleylist klonlandı %1$s abunəliyini ləğv etmək istədiyinizə əminsiniz\? Abunəlik ləğvini təsdiqlə @@ -370,18 +365,18 @@ Başlama vaxtı Bitmə vaxtı Bildiriş vaxtı - Bildirişlərin göstərilməsinə icazə verilən vaxt aralığı. + Bildirişləri göstərmək üçün icazə verilən vaxt aralığı. Alternativ trend tərtibatı Ardıcıllıq Tərtibat Alternativ oynadıcı tərtibatı Əlaqəli videoları aşağıda deyil, şərhlərin üstündə cərgə kimi göstər. Səs treki - Defolt + Standart Dəstəklənməyən fayl formatı! Avtomatik HLS istifadə et - DASH əvəzinə HLS istifadə et (daha yavaş olacaq, tövsiyə edilmir) + DASH əvəzinə HLS istifadə et (daha yavaş olacaq, tövsiyə edilmədi) İş vaxtı məhdudiyyət Bildirişdən növbə aç Trendlər @@ -393,21 +388,21 @@ Hələ əlfəcinlər yoxdur! Əlaqəli videoları yerləşdir Yerli pleylistlər - Lütfən, əvvəla başqa başlanğıc paneli seç! + Xahiş edirik, əvvəla başqa başlatma paneli seç! Menyu elementi aktiv deyil! Parlaqlıq Avtomatik Parlaqlığı və səs səviyyəsini nizamlamaq üçün sürüşdürmə jesti istifadə et. Səs səviyyəsi Sürüşdürmə nəzarətləri - Defolt + Standartlar Ani pəncərə Bu videoda şərhlər əlçatan deyil. Şərhlər yükləyici tərəfindən deaktiv edilib. Axtarmaq üçün iki dəfə kliklə Oynadıcı mövqeyini geri və ya irəli çəkmək üçün sola yaxud sağa iki dəfə kliklə. Hamısını yuxarıda tutmusunuz - Bütün yeni videolara baxmısınız + Bütün yeni videoları gördünüz Altyazı ölçüsü Pleylistləri idxal et Pleylistləri ixrac et @@ -422,7 +417,7 @@ Minimalist Tək Rəngli Ən son videoları oynat Heç nə seçilməyib! - Hərtərəfli Bənövşəyi + Bütöv Bənövşəyi Mövcud nümunələri əldə etmək mümkün olmadı. Baxılan videoları axından gizlət Abunəliklər panelində 90% daha çox baxılan videoları göstərmə. @@ -432,15 +427,15 @@ Əlfəcini sil Əlfəcinlərə əlavə et İrəli - Fasilə ver + Dayandır Alternativ PiP nəzarətləri - Yalnız səsi göstər və irəli və geri çevirmək əvəzində PiP-də idarəetmələri ötür + PiP\'də, irəli və geri çevirmək əvəzinə \"yalnız səs\" düyməsin göstər və idarəetmələri ötür Geri sar Səs oynadıcı Yalnız səs rejimi LibreTube-u musiqi oynadıcıya dəyiş. Altyazı yoxdur - Endirməyə fasilə verildi + Endirmə dayandırıldı Endirmə tamamlandı Maksimal paralel endirmələr Maksimal paralel endirmə limitinə çatıldı. @@ -450,4 +445,5 @@ Səssizliyi ötür Kömək T-TSS + Cavablar \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index cf4a019da..590bdcf20 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -3,8 +3,6 @@ Клониране на плейлист Изтриване на регистрацията Общност - Discord - Matrix Нулиране Начало Абонаменти @@ -83,9 +81,6 @@ GitHub Звук и видео Портрет - Telegram - Reddit - Twitter Избрано Никога Библиотека diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index cd62a3ec9..e3d5eb41d 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -204,24 +204,19 @@ দয়া করেন ওয়াই-ফাই অথবা মোবাইল ডেটা অন করুন ইন্টারনেটের সাথে যুক্ত হতে। খুলুন… অথেনটিকেশনের জন্য ভিন্ন ইন্সটেন্স ব্যবহার করুন। - রেডিট সবসময় কখনই না - টেলিগ্রাম - ডিস্কর্ড দেখার ইতিহাস পজিশন মনে রাখুন HLS স্বয়ংক্রিয়-রোটেশন ল্যান্ডস্কেপ পোর্টেইট - টুইটার শেষ প্লেব্যাক পজিশন থেকে শুরু করুন অথেনটিকেশনের জন্য ইন্সটেন্স বাছাই করুন গিটহাব অডিও এবং ভিডিও কমিউনিটি - ম্যাট্রিক্স প্লে-ব্যাকের গতি বাছাইকৃত এই পরিবর্তনটি কার্যকর করতে হলে রিস্টার্ট করুন। @@ -395,4 +390,5 @@ বুকমার্ক বুকমার্ক মুছে দিন কোনো বুকমার্ক নেই! + পপ-আপ \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 460d249ca..1c19a3fab 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -188,12 +188,7 @@ Automatické otáčení Na šířku Na výšku - Twitter Komunita - Discord - Matrix - Telegram - Reddit Je dostupná verze %1$s Přejít na vydání na GitHubu a stáhnout ji\? Vzhled @@ -450,4 +445,5 @@ Přeskočit ticho Nápověda Často kladené dotazy + Odpovědi \ No newline at end of file diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 63ef5c22f..ff94d90c4 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -208,12 +208,7 @@ Automatisk rotation Landskab Portræt - Twitter Fællesskab - Discord - Matrix - Telegram - Reddit Downloader… Ingen lyd Ingen video diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 16fd501e5..c5f4cb67a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -211,11 +211,6 @@ Landschaft Porträt Community - Discord - Matrix - Telegram - Reddit - Twitter Verlorenes Vermächtnis Füllungstangente/Witze Keine @@ -441,4 +436,14 @@ Herunterladen abgeschlossen Unbekannt Fortsetzen + \"Nur Audio\"- und Skip-Steuerung anzeigen im Bild-in-Bild-Modus anstelle von Vor- und Zurückspulen + Antworten + Maximale Anzahl gleichzeitiger Downloads erreicht. + LibreTube in einen Musikplayer umwandeln. + Häufig gestellte Fragen + Audio-Player + Hilfe + Maximale Anzahl gleichzeitiger Downloads + Einschlaf-Timer + Stille überspringen \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ce134a83b..d195dd7c3 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -215,12 +215,7 @@ Αυτόματη περιστροφή Τοπίο Πορτρέτο - Telegram - Reddit Κοινότητα - Discord - Matrix - Twitter Στυλ λεζάντας συστήματος Πάντα Επιλεγμένα diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 44fa77ba2..c11f44e49 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -215,12 +215,7 @@ Vertical Relación de aspecto del vídeo Rotación automática - Matrix - Telegram - Reddit Comunidad - Discord - Twitter Por favor, active la WiFi o los datos móviles para conectarse a Internet. Abrir… Capítulos @@ -450,4 +445,5 @@ Saltar silencio Ayuda Preguntas frecuentes + Respuestas \ No newline at end of file diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index ca7908225..b53dac537 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -216,11 +216,6 @@ Paisaia Erretratua Komunitatea - Discord - Matrix - Reddit - Telegram - Twitter Ireki… Kapituluak Erreprodukzio abiadura diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4a3d1e218..2e66d44a6 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -215,12 +215,7 @@ Paysage Portrait Rapport d\'aspect vidéo - Matrix - Reddit - Twitter - Telegram Communauté - Discord Ouvrir … Veuillez activer le Wi-Fi ou les données mobiles pour vous connecter à internet. Chapitres diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 19e0198a6..6a93ea602 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -112,11 +112,6 @@ ઓડિયો અને વિડિયો સંપૂર્ણસ્ક્રીન દિશા વિડિયો એસ્પેક્ટ રેશિયો - ઝઘડો - મેટ્રિક્સ - ટેલિગ્રામ - રેડિટ - Twitter પ્રકરણો એપ્લિકેશન પુનઃપ્રારંભ જરૂરી લેબલ દૃશ્યતા diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index fe1568c71..69e94ad48 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -345,11 +345,6 @@ गिटहब फुलस्क्रीन ओरिएंटेशन समुदाय - डिसकौरड - मैट्रिक्स - टेलीग्राम - रैडिट - ट्विटर खोलें… अध्याय प्लेबैक गति @@ -450,4 +445,5 @@ चुप्पी छोड़ें सहायता आम सवाल + जवाब \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index ed777a3b9..d7930ebc6 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -200,11 +200,6 @@ HLS GitHub Közösség - Discord - Matrix - Telegram - Reddit - Twitter Fejezetek Lejátszási sebesség Alkalmazás újraindítása szükséges @@ -450,4 +445,5 @@ Csend átugrása Súgó GYIK + Válaszok \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 875acfd73..073341bc4 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -224,11 +224,6 @@ Rasio aspek video Putar secara otomatis Komunitas - Discord - Matrix - Telegram - Reddit - Twitter Lainnya Pengingat istirahat Waktunya istirahat @@ -450,4 +445,5 @@ Lewati keheningan Bantuan SSD + Balasan \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e39dee016..11b099329 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -214,13 +214,8 @@ Rotazione automatica Orizzontale Comunità - Discord - Twitter Rapporto d\'aspetto del video Verticale - Matrix - Telegram - Reddit Connettiti a Internet attivando Wi-Fi o dati mobili. Apri… Capitoli @@ -450,4 +445,5 @@ Salta il silenzio Aiuto Domande frequenti + Risposte \ No newline at end of file diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index da38021d7..891837fa8 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -213,14 +213,9 @@ כיוון מסך מלא לרוחב קהילה - דיסקורד - Matrix - טלגרם - Reddit הטיה אוטומטית לאורך יחס רוחב-גובה של סרטון - טוויטר נא להפעיל את הרשת האלחוטית או החיבור הסלולרי כדי להתחבר לאינטרנט. פתיחה… פרקים @@ -450,4 +445,5 @@ דילוג על שקט עזרה שו״ת + תגובות \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index c994ddc6b..11883d473 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -198,11 +198,6 @@ コミュニティ - Discord - Matrix - Telegram - Reddit - Twitter ダウンロード 動画の形式 音声と動画の両方をダウンロードした場合のファイル形式の変換。 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2c27ea60a..a30460c17 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -273,7 +273,6 @@ 언어 및 지역 품질 및 형식 대기열 - 트위터 없음 열다 기기 정보 @@ -318,8 +317,6 @@ 컨트롤 변경 스와이프 제스처를 사용하여 밝기와 음량을 조정하십시오. 세그먼트 건너뛰기 - 텔레그램 - 레딧 선택된 클립보드에 복사됨 음량 @@ -385,7 +382,6 @@ 채우기 항상 미터링됨 - 매트릭스 전혀 아닌 없음 확대 @@ -422,7 +418,6 @@ 시청한 영상 피드에서 숨기기 구독 탭에 90% 이상 시청한 비디오를 보여주지 않습니다. 마커 - 디스코드 핀치 컨트롤 기타 재생목록 URL @@ -430,4 +425,16 @@ 셔플 북마크에 추가 북마크 제거 + 답글 + 알 수 없음 + FAQ + 최대 동시 다운로드 수 + 무음 건너뛰기 + 최대 동시 다운로드 수에 도달하였습니다. + 다운로드 완료 + 일시정지 + 취침 타이머 + 오디오 플레이어 + 오디오 모드 + LibreTube 를 음악 플에어이로 바꿉니다. \ No newline at end of file diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index bf893a96f..7a104696a 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -114,11 +114,6 @@ Garsas ir vaizdas Portretas Bendruomenė - Discord - Matrix - Telegram - Reddit - Twitter Norėdami prisijungti prie interneto, įjunkite Wi-Fi arba mobiliuosius duomenis. Skirsniai Būtina iš naujo paleisti programėlę diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index edc33b93e..3725d9661 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -222,11 +222,6 @@ Horizontāla Vertikāla Kopiena - Discord - Matrix - Telegram - Reddit - Twitter Rādīt tekstu navigācijas panelī Vienmēr Nekad diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 391877bec..d501e63d4 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -222,11 +222,6 @@ Liggende Stående Gemenskap - Discord - Matrix - Telegram - Reddit - Twitter Ingen Alltid Ren svarthvitt-drakt diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index c6f831238..9dc057f86 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -199,10 +199,6 @@ ଅଡିଓ ଏବଂ ଭିଡିଓ ଭିଡିଓ ଦିଗ ଅନୁପାତ ଆନୁଲମ୍ବିକ - ଦିସ୍କର୍ଡ - ମେଟ୍ରିକ୍ - ଟେଲିଗ୍ରାମ - ରେଡିଟ ସର୍ବଦା ମନୋନୀତ କେବେ ନୁହ @@ -300,7 +296,6 @@ ସମ୍ପ୍ରଦାୟ ପ୍ଲେବେକ୍ ବେଗ ଆପ୍ ପୁନଆରମ୍ଭ ଆବଶ୍ୟକ - ଟୁଇଟର ଖୋଲନ୍ତୁ… ପୁରୁଣା ସଦସ୍ୟତା ଦୃଶ୍ୟ ଅଧ୍ୟାୟ ଗୁଡ଼ିକ @@ -450,4 +445,5 @@ ନୀରବତା ଛାଡିଦିଅ ସାହାଯ୍ୟ FAQ + ଉତ୍ତର ଗୁଡିକ \ No newline at end of file diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 140528f64..cc1dbacac 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -113,7 +113,7 @@ ਅਨੰਦਮਈ ਨੀਲਾ ਜ਼ੋਰਦਾਰ ਪੀਲਾ ਆਕਰਸ਼ਕ ਹਰਾ - ਅਨੰਦਦਾਇਕ ਜਾਮਨੀ + ਅਨੰਦਦਾਇਕ ਜਾਮਣੀ ਕਾਲ੍ਹਾ ਮਟੀਰੀਅਲ ਯੂ ਨੋਟੀਫਿਕੇਸ਼ਨ @@ -220,11 +220,6 @@ ਲੈਂਡਸਕੇਪ ਪੋਰਟਰੇਟ ਭਾਈਚਾਰਾ - ਡਿਸਕੌਰਡ - ਮੈਟਰਿਕਸ - ਟੈਲੀਗ੍ਰਾਮ - ਟਵਿੱਟਰ - ਰੈਡਿੱਟ ਕਿਰਪਾ ਕਰਕੇ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਲਈ ਵਾਈ-ਫਾਈ ਜਾਂ ਮੋਬਾਈਲ ਡਾਟਾ ਚਾਲੂ ਕਰੋ। ਖੋਲ੍ਹੋ… ਚੈਪਟਰ @@ -450,4 +445,5 @@ ਸਹਾਇਤਾ ਆਮ ਸਵਾਲ ਸਲੀਪ ਟਾਈਮਰ + ਜਵਾਬ \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 3bc4ba29c..cfb42fef4 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -238,8 +238,6 @@ Wyświetlanie pełnoekranowe Wymuś tryb pionowy Społeczność - Discord - Matrix Rozdziały Prędkość odtwarzania Czysty biały/czarny motyw @@ -303,12 +301,9 @@ Najgorsza Modna lampka Użyj proporcji obrazu wideo - Reddit Zatrzymaj odtwarzanie, gdy ekran jest wyłączony. Bez limitu Automatyczne obracanie - Telegram - Twitter Brak Maksymalny rozmiar historii Zainstalować teraz nową wersję LibreTube\? diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8536098cd..23e0fe0fe 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -233,11 +233,6 @@ Paisagem Retrato Comunidade - Discord - Matrix - Telegram - Reddit - Twitter Ao vivo Sem vídeo Áudio @@ -450,4 +445,5 @@ Pular silêncio Ajuda FAQ + Respostas \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 345f5de1e..784240da3 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -195,11 +195,6 @@ Horizontal Vertical Comunidade - Discord - Matrix - Telegram - Reddit - Twitter Sem áudio Sem vídeo Áudio @@ -450,4 +445,5 @@ FAQ Temporizador de sono Ignorar o silêncio + Respostas \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 610d80c7d..c7eea1359 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -214,13 +214,8 @@ Выбрать сервер Автоповорот Альбомная - Reddit Портретная Сообщество - Discord - Matrix - Telegram - Twitter Пожалуйста, включите Wi-Fi или мобильный интернет для подключения. Открыть… Эпизоды @@ -450,4 +445,5 @@ Пропускать тишину Помощь FAQ + Ответы \ No newline at end of file diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 06ad6fef3..7a6071814 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -163,11 +163,6 @@ සම්පූර්ණ තිර නැඹුරුව භූ දර්ශනය ප්‍රජාව - Discord - Matrix - Telegram - Reddit - Twitter කරුණාකර අන්තර්ජාලයට සම්බන්ධ වීමට Wi-Fi හෝ ජංගම දත්ත ක්‍රියාත්මක කරන්න. විවෘත කරන්න… පරිච්ඡේද @@ -446,4 +441,9 @@ ගෙවුම් ප්‍රවර්ධනය, ගෙවුම් යොමු කිරීම් සහ සෘජු දැන්වීම්. ස්වයං ප්‍රවර්ධනය හෝ නොමිලේ නිර්මාණකරුවන්, වෙබ් අඩවි සහ නිෂ්පාදන සඳහා සත්‍ය ප්‍රචාරක කටයුතු, සඳහා නොවේ. මෙම හෝ එහි මාලාවේ අනාගත වීඩියෝවල ඉදිරියට එන අන්තර්ගතය විස්තර කරන කොටස් සඳහා, නමුත් අමතර තොරතුරු ලබා නොදේ. මෙහි පමණක් පෙනෙන ක්ලිප් ඇතුළත් නම්, මෙය බොහෝ දුරට වැරදි කාණ්ඩයකි. ඉදිරියට සහ පසුපසට යැවීම වෙනුවට PiP හි ශබ්ද පමණක් සහ මඟහරින පාලක පෙන්වන්න + පිළිතුරු + නිහඬ කොටස් මඟ හරින්න + උදව් + නිතර අසන ප්‍රශ්න + නින්ද ටයිමරය \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 88097e645..159120f19 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -22,7 +22,7 @@ Iné sťahovanie práve prebieha, počkajte kým bude ukončené. Sťahovanie zlyhalo. Otvoriť vo VLC - Nemožno otvoriť vo VLC. Možno nie je nainštalovaný. + Nepodarilo sa otvoriť vo VLC. Možno nie je nainštalovaný. Import odberov Z YouTube alebo NewPipe Na serveri sa vyskytol problém. Skúsiť inú inštanciu\? @@ -175,7 +175,7 @@ Skryť kapitoly Ak sa uprostred obsahu nachádza krátka pripomienka, aby ste sa prihlásili k odberu alebo sledovaniu. Ak je dlhý alebo o niečom konkrétnom, mal by byť namiesto toho sebapropagáciou. Cieľ vyrovnávacej pamäte - Ide o vedľajšie scény pridané len ako výplň alebo humor, ktoré nie sú potrebné na pochopenie hlavného obsahu videa. + Pre vedľajšie scény pridané len ako výplň alebo humor, ktoré nie sú potrebné na pochopenie hlavného obsahu videa. Len na použitie v hudobných videoklipoch. Mal by pokrývať časti videoklipu, ktoré nie sú súčasťou oficiálnych mixov. Nakoniec by sa video malo čo najviac podobať verzii mixu na Spotify alebo akejkoľvek inej verzii mixu, alebo by malo obmedziť hovorenie alebo iné rušivé prvky. Informácie po skončení. Nie pre závery s informáciami. Prosím zapnite si wifi alebo mobilné dáta aby ste sa pripojili na internet. @@ -236,7 +236,6 @@ Audio a video Orientácia na celú obrazovku Nastavenie zobrazenia - Telegram Pridať do radu Rôzne Pripomienka prestávky @@ -250,8 +249,6 @@ Aktuálne Použi inú inštanciu pre overené hovory. Komunita - Matrix - Twitter Video formát pre prehrávač Bez zvuku bez videa @@ -292,4 +289,5 @@ Najlepšia Audio formát pre prehrávač Kvalita zvuku + Odpovede \ No newline at end of file diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 480d40c4d..51474c2d5 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -164,9 +164,6 @@ Хоризонтално Вертикално Заједница - Telegram - Reddit - Twitter Укључите WiFi или мобилне податке да би сте се повезали на интернет. Отвори… Поглавља @@ -242,8 +239,6 @@ Аутоплеј Ресетовати сва подешавања и одјавити се\? Аудио и видео - Дискорд - Matrix Брзина репродукције Прескочи сличице и друге слике. Историја прегледа и претрага diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index a3fd9cadb..9087a2023 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -180,10 +180,5 @@ Maks. antal sekunder videor att buffra. Videoformat for spelaren GitHub - Discord - Matrix - Telegram Bild i bild - Reddit - Twitter \ No newline at end of file diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index 853bf4e49..5c02da79f 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -167,7 +167,6 @@ Landşaft Portret Jemgyýet - Discord Wi-Fi ýa-da ykjam maglumatlary açyp, internete birikmegiňizi haýyş edýäris. Aç … Baplar diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 813bce80b..043e2ceb2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -215,12 +215,7 @@ Dikey Yatay Ses ve video - Matrix - Telegram - Reddit - Twitter Topluluk - Discord Açın… İnternete bağlanmak için lütfen Wi-Fi veya mobil verileri açın. Bölümler @@ -450,4 +445,5 @@ Sessizliği atla Yardım SSS + Cevaplar \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e176e1148..05b4880af 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -207,11 +207,6 @@ Альбомна Портретна Спільнота - Discord - Matrix - Telegram - Reddit - Twitter Ім\'я екземпляра Формат відео для програвача Сервер аутентифікації @@ -450,4 +445,5 @@ ЧаПи Таймер сну Пропускати тишу + Відповіді \ No newline at end of file diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml new file mode 100644 index 000000000..fff4197b3 --- /dev/null +++ b/app/src/main/res/values-ur/strings.xml @@ -0,0 +1,143 @@ + + + شیئر کریں + ڈاؤن لوڈ کریں + لاگ ان کریں + محفوظ کریں + رجسٹر کریں + لاگ آؤٹ کریں + لاگ ان ہو گیا۔ + رجسٹر ہو گیا. اب آپ چینل سبسکرائب کر سکتے ہیں۔ + براہ کرم لاگ ان کریں اور دوبارہ کوشش کریں۔ + منتخب کریں… + پہلے سے لاگ ان ہے۔ آپ اپنے اکاؤنٹ سے لاگ آؤٹ کر سکتے ہیں۔ + ویڈیوز + سبسکرائب + لاگ آؤٹ ہو گیا۔ + تلاش کریں + گھر + سبسکرپشنز + معیار + ہاں + پاس ورڈ + منسوخ کریں + سسٹم + روشن + اندھیرا + %1$s سبسکرائبر + ترتیبات + تبصرے + جوابات + براہ کرم پہلے سیٹنگز میں لاگ ان یا رجسٹر ہوں۔ + علاقہ + لاگ ان/رجسٹر + پہلے کچھ چینل سبسکرائب کریں۔ + اس سلسلہ کو ڈاؤن لوڈ نہیں کیا جا سکتا۔ + ڈاؤن لوڈ مکمل ہو گیا۔ + ایک اور ڈاؤن لوڈ پہلے سے جاری ہے، براہ کرم اس کے مکمل ہونے تک انتظار کریں۔ + VLC میں کھولیں + تھیم + نیٹ ورک کی خرابی۔ + کچھ غلط ہو گیا. + ڈاؤن لوڈ ناکام ہو گیا۔ + سبسکرپشن درآمد کریں + سرور میں ایک مسئلہ ہے۔ دوسری انسٹنش آزمائیں؟ + VLC میں نہیں کھل سکا۔ شاید یہ انسٹال نہیں ہے۔ + یوٹیوب یا نیو پائپ سے + یہ پائپڈ اکاؤنٹ کے لیے ہے۔ + ویڈیو ریزولوشن + پلے لسٹ ڈیلیٹ کریں + صارف نام + آپ کو صارف نام اور پاس ورڈ درج کرنا ہوگا۔ + پلے لسٹ کا نام + گرڈ کالم + پلے لسٹ ڈیلیٹ کریں؟ + پلے لسٹ بنائیں + پلے لسٹ بنائی گئی۔ + پلے لسٹ کا نام خالی نہیں ہو سکتا + پلے لسٹ میں شامل کریں + مکمل۔ + ناکام :( + یہاں کچھ بھی نہیں ہے. + انسٹنش + مقام + ویب سائٹ + زبان + ہمارے بارے میں + سسٹم + موافقت + دوبارہ کوشش کریںدوبارہ کوشش کریں + %1$s ویڈیو + پہلے انٹرنیٹ سے جڑیں۔ + تلاش کی سرگزشت + ٹھیک ہے + تلاش کا فلٹر منتخب کریں + چینلز + تمام + پلے لسٹس + سرگزشت + سرگزشت صاف کریں + YT میوزک البمز + YT میوزک گانے + YT میوزک ویڈیوز + YT میوزک پلے لسٹس + ڈیفالٹ ٹیب + لائبریری + ان سبسکرائب + حسب ضرورت + سبسکرائب کیا + اسپانسر بلاک + https://sponsor.ajay.app API استعمال کرتا ہے + چھوڑا ہوا طبقہ + آن + طبقات + سپانسر + بامعاوضہ پروموشن، بامعاوضہ حوالہ جات اور براہ راست اشتہارات۔ خود کو فروغ دینے یا اسباب، تخلیق کاروں، ویب سائٹس اور پروڈکٹس کے لیے مفت حقیقی شور مچانے کے لیے نہیں۔ + تعامل کی یاد دہانی (پسند کریں اور سبسکرائب کریں) + انٹرمیشن/انٹرو اینیمیشن + اصل مواد کے بغیر وقفہ۔ ایک توقف، جامد فریم، دہرانے والی حرکت پذیری ہو سکتی ہے۔ معلومات پر مشتمل ٹرانزیشن کے لیے استعمال نہیں کیا جانا چاہیے۔ + اختتامی کارڈ اور کریڈٹ + اختتام کے بعد معلومات۔ معلومات کے ساتھ نتیجہ اخذ کرنے کے لیے نہیں۔ + فلر ٹینجنٹ/مذاق + موسیقی: غیر موسیق سیکشن + لائسنس + لہجے + آرام دہ سرخ + خوشنما نیلا + گرووی سبز + ییٹنگ پیلا + خوشگوار جامنی + سیاہ + میٹریل یو + اطلاعات + آئیکن + چالو + بند + پائپڈ + یوٹیوب + پس منظر میں چلائیں + ورژن %1$s دستیاب ہے + اسے ڈاؤن لوڈ کرنے کے لیے GitHub پر ریلیز پر جائیں؟ + ہئیت + شروع کریں + آٹو پلے + انسٹانس فرنٹ اینڈ کا URL + معیار + رویہ + ڈیفالٹس اور رویہ + سیک اضافہ + اسکرین آف ہونے پر پلے بیک کو روک دیں۔ + موجودہ ویڈیو کے بعد اگلی ویڈیو خود بخود چلائیں۔ + پلے لسٹ کلون کریں + ڈیفالٹس بحال کریں + بلا معاوضہ/سیلف پروموشن + بلا معاوضہ یا خود پروموشن کے علاوہ \"اسپانسر\" کی طرح۔ اس میں تجارتی مال، عطیات، یا اس بارے میں معلومات کے حصے شامل ہیں کہ انہوں نے کس کے ساتھ تعاون کیا۔ + جب مواد کے بیچ میں پسند کرنے، سبسکرائب کرنے یا پیروی کرنے کے لیے ایک مختصر یاد دہانی ہو۔ اگر طویل ہو یا کسی خاص چیز کے بارے میں، تو اس کی بجائے خود کو فروغ دینا چاہیے۔ + صرف میوزک ویڈیوز میں استعمال کے لیے۔ اسے ویڈیو کے کچھ حصوں کا احاطہ کرنا چاہیے نہ کہ آفیشل مکسز کا۔ آخر میں، ویڈیو کو Spotify یا کسی دوسرے مخلوط ورژن سے زیادہ سے زیادہ قریب سے مشابہ ہونا چاہیے، یا بات کرنے یا دیگر خلفشار کو کم کرنا چاہیے۔ + پیش نظار/تذکر + ٹینجینٹل مناظر کے لیے صرف فلر یا مزاح کے لیے شامل کیے گئے ویڈیو کے مرکزی مواد کو سمجھنے کی ضرورت نہیں ہے۔ + اس سیریز میں آنے والے مواد یا مستقبل کے ویڈیوز کی تفصیل دینے والے حصوں کے لیے، لیکن اضافی معلومات فراہم نہ کریں۔ اگر اس میں ایسے کلپس شامل ہیں جو صرف یہاں ظاہر ہوتے ہیں، تو یہ غلط زمرہ ہے۔ + توقف کریں + رجحان ساز صفحہ چھپائیں + خودکار توقف + \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 49a678b2e..88ac8179f 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -227,12 +227,7 @@ Yêu cầu kết nối mạng Tất cả Ngang - Telegram - Reddit - Twitter Cộng đồng - Discord - Matrix Lịch sử xem Nhớ vị trí Instance được xác minh diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index d5989365e..5662a6787 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -215,12 +215,7 @@ 自动旋转 横屏 竖屏 - Reddit 社区 - Twitter - Discord - Matrix - Telegram 请打开 Wi-Fi 或移动数据连接到互联网。 打开… 章节 @@ -450,4 +445,5 @@ 跳过静音 帮助 常见问题 + 回复 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9fefb45da..d532b1405 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -266,9 +266,6 @@ 繼續 自動暫停 肖像 - Discord - Matrix - Telegram 請開啟Wifi或行動數據連上網路。 永不 播放速度 @@ -388,8 +385,6 @@ 影片比例 自動旋轉 風景 - Reddit - Twitter 調整音高 播放最新影片 音訊曲目 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 58159c406..a5ed73221 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -8,4 +8,13 @@ + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 53d5cef34..90285cd73 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -226,11 +226,6 @@ Landscape Portrait Community - Discord - Matrix - Telegram - Reddit - Twitter Please turn on Wi-Fi or mobile data to connect to the Internet. Open… Chapters diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml index 043068972..487c1f216 100644 --- a/app/src/main/res/values/style.xml +++ b/app/src/main/res/values/style.xml @@ -17,8 +17,8 @@ - - - -