diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a54eda8c8..3fcab124b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,10 +33,10 @@ jobs: python checkrun.py cd .. - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: "temurin" cache: "gradle" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 55aed5992..d63d60009 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,9 +37,17 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: + - name: Checkout repository uses: actions/checkout@v3 + - name: Setup Java 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: "temurin" + cache: "gradle" + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/app/build.gradle b/app/build.gradle index 3abdb8277..8be27ab13 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,11 +53,11 @@ android { } compileOptions { coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_17 } splits { abi { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c9ea8f734..a168aa319 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -52,3 +52,57 @@ } -keepattributes RuntimeVisibleAnnotations,AnnotationDefault + +# -- Retrofit keep rules, obtained from https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro + +# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and +# EnclosingMethod is required to use InnerClasses. +-keepattributes Signature, InnerClasses, EnclosingMethod + +# Retrofit does reflection on method and parameter annotations. +-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations + +# Keep annotation default values (e.g., retrofit2.http.Field.encoded). +-keepattributes AnnotationDefault + +# Retain service method parameters when optimizing. +-keepclassmembers,allowshrinking,allowobfuscation interface * { + @retrofit2.http.* ; +} + +# Ignore annotation used for build tooling. +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement + +# Ignore JSR 305 annotations for embedding nullability information. +-dontwarn javax.annotation.** + +# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. +-dontwarn kotlin.Unit + +# Top-level functions that can only be used by Kotlin. +-dontwarn retrofit2.KotlinExtensions +-dontwarn retrofit2.KotlinExtensions$* + +# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy +# and replaces all potential values with null. Explicitly keeping the interfaces prevents this. +-if interface * { @retrofit2.http.* ; } +-keep,allowobfuscation interface <1> + +# Keep inherited services. +-if interface * { @retrofit2.http.* ; } +-keep,allowobfuscation interface * extends <1> + +# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items). +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response + +# With R8 full mode generic signatures are stripped for classes that are not +# kept. Suspend functions are wrapped in continuations where the type argument +# is used. +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +# -- End of Retrofit keep rules + +-dontwarn org.conscrypt.** +-dontwarn org.bouncycastle.** +-dontwarn org.openjsse.** diff --git a/app/src/main/java/com/github/libretube/api/obj/Streams.kt b/app/src/main/java/com/github/libretube/api/obj/Streams.kt index 79eb584ac..9306507a4 100644 --- a/app/src/main/java/com/github/libretube/api/obj/Streams.kt +++ b/app/src/main/java/com/github/libretube/api/obj/Streams.kt @@ -35,7 +35,6 @@ data class Streams( val uploaderSubscriberCount: Long = 0, val previewFrames: List = emptyList(), ) { - @Suppress("NewApi") // The Paths class is desugared. fun toDownloadItems( videoId: String, fileName: String, diff --git a/app/src/main/java/com/github/libretube/extensions/Path.kt b/app/src/main/java/com/github/libretube/extensions/Path.kt index ecdccfb73..47cef7df9 100644 --- a/app/src/main/java/com/github/libretube/extensions/Path.kt +++ b/app/src/main/java/com/github/libretube/extensions/Path.kt @@ -10,6 +10,5 @@ fun Path.toAndroidUriOrNull(): Uri? { } fun Path.toAndroidUri(): Uri { - @Suppress("NewApi") // The Path class is desugared. return toFile().toUri() } diff --git a/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt b/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt index a25983706..519bf3d17 100644 --- a/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/DownloadHelper.kt @@ -9,7 +9,6 @@ import com.github.libretube.constants.PreferenceKeys import com.github.libretube.db.obj.DownloadItem import com.github.libretube.services.DownloadService import java.nio.file.Path -import kotlin.io.path.createDirectories object DownloadHelper { const val VIDEO_DIR = "video" @@ -35,8 +34,11 @@ object DownloadHelper { } fun getDownloadDir(context: Context, path: String): Path { - @Suppress("NewApi") // The Path class is desugared. - return getOfflineStorageDir(context).resolve(path).createDirectories() + // TODO: Use createDirectories() when https://issuetracker.google.com/issues/279034662 is + // fixed. + return getOfflineStorageDir(context).resolve(path).apply { + toFile().mkdirs() + } } fun getMaxConcurrentDownloads(): Int { diff --git a/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt index 79d588ded..332471f7b 100644 --- a/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ImportHelper.kt @@ -44,6 +44,7 @@ object ImportHelper { /** * Get a list of channel IDs from a file [Uri] */ + @OptIn(ExperimentalSerializationApi::class) private fun getChannelsFromUri(activity: Activity, uri: Uri): List { return when (val fileType = activity.contentResolver.getType(uri)) { "application/json", "application/*", "application/octet-stream" -> { @@ -72,6 +73,7 @@ object ImportHelper { /** * Write the text to the document */ + @OptIn(ExperimentalSerializationApi::class) suspend fun exportSubscriptions(activity: Activity, uri: Uri) { val token = PreferenceHelper.getToken() val subs = if (token.isNotEmpty()) { @@ -149,6 +151,7 @@ object ImportHelper { /** * Export Playlists */ + @OptIn(ExperimentalSerializationApi::class) suspend fun exportPlaylists(activity: Activity, uri: Uri) { val playlists = PlaylistsHelper.exportPlaylists() val playlistFile = ImportPlaylistFile("Piped", 1, playlists) diff --git a/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt b/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt index e14562e1f..97295f7af 100644 --- a/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NetworkHelper.kt @@ -10,6 +10,7 @@ object NetworkHelper { /** * Detect whether network is available */ + @Suppress("DEPRECATION") fun isNetworkAvailable(context: Context): Boolean { // In case we are using a VPN, we return true since we might be using reverse tethering val connectivityManager = context.getSystemService() ?: return false @@ -36,6 +37,7 @@ object NetworkHelper { * @param context Context of the application * @return whether the network is metered or not */ + @Suppress("DEPRECATION") fun isNetworkMetered(context: Context): Boolean { val connectivityManager = context.getSystemService()!! val activeNetworkInfo = connectivityManager.activeNetworkInfo diff --git a/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt b/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt index 8a3c2ffe2..9bcc52b82 100644 --- a/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/ThemeHelper.kt @@ -132,7 +132,7 @@ object ThemeHelper { * Get the styled app name */ fun getStyledAppName(context: Context): Spanned { - val colorPrimary = getThemeColor(context, R.attr.colorPrimaryDark) + val colorPrimary = getThemeColor(context, androidx.appcompat.R.attr.colorPrimaryDark) val hexColor = String.format("#%06X", (0xFFFFFF and colorPrimary)) return "LibreTube" .parseAsHtml(HtmlCompat.FROM_HTML_MODE_COMPACT) 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 f79f8c100..1a7345e5d 100644 --- a/app/src/main/java/com/github/libretube/services/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/services/DownloadService.kt @@ -57,6 +57,7 @@ import java.nio.file.StandardOpenOption import java.util.concurrent.Executors import kotlin.io.path.absolute import kotlin.io.path.createFile +import kotlin.io.path.deleteIfExists import kotlin.io.path.fileSize /** @@ -103,7 +104,6 @@ class DownloadService : LifecycleService() { } val thumbnailTargetPath = getDownloadPath(DownloadHelper.THUMBNAIL_DIR, fileName) - .absolute() val download = Download( videoId, @@ -147,7 +147,7 @@ class DownloadService : LifecycleService() { FileType.AUDIO -> getDownloadPath(DownloadHelper.AUDIO_DIR, item.fileName) FileType.VIDEO -> getDownloadPath(DownloadHelper.VIDEO_DIR, item.fileName) FileType.SUBTITLE -> getDownloadPath(DownloadHelper.SUBTITLE_DIR, item.fileName) - }.createFile().absolute() + }.apply { deleteIfExists() }.createFile() lifecycleScope.launch(coroutineContext) { item.id = Database.downloadDao().insertDownloadItem(item).toInt() @@ -440,8 +440,7 @@ class DownloadService : LifecycleService() { * Get a [File] from the corresponding download directory and the file name */ private fun getDownloadPath(directory: String, fileName: String): Path { - @Suppress("NewApi") // The Path class is desugared. - return DownloadHelper.getDownloadDir(this, directory).resolve(fileName) + return DownloadHelper.getDownloadDir(this, directory).resolve(fileName).absolute() } override fun onDestroy() { 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 77e890072..963fe0136 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 @@ -238,8 +238,14 @@ class MainActivity : BaseActivity() { if (lastSeenVideoIndex < 1) return@observe binding.bottomNav.getOrCreateBadge(R.id.subscriptionsFragment).apply { number = lastSeenVideoIndex - backgroundColor = ThemeHelper.getThemeColor(this@MainActivity, R.attr.colorPrimary) - badgeTextColor = ThemeHelper.getThemeColor(this@MainActivity, R.attr.colorOnPrimary) + backgroundColor = ThemeHelper.getThemeColor( + this@MainActivity, + androidx.appcompat.R.attr.colorPrimary, + ) + badgeTextColor = ThemeHelper.getThemeColor( + this@MainActivity, + com.google.android.material.R.attr.colorOnPrimary, + ) } } } 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 395910de9..3689fe7b4 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 @@ -103,8 +103,9 @@ class CommentsAdapter( // highlight the comment that is being replied to if (comment == comments.firstOrNull()) { root.setBackgroundColor( - ThemeHelper.getThemeColor(root.context, R.attr.colorSurface), + ThemeHelper.getThemeColor(root.context, com.google.android.material.R.attr.colorSurface), ) + root.updatePadding(top = 20) root.updateLayoutParams { bottomMargin = 20 } } else { 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 14b188f5d..0b5c95686 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 @@ -35,12 +35,12 @@ class LegacySubscriptionAdapter( root.setOnClickListener { NavigationHelper.navigateChannel( root.context, - subscription.url!!.toID(), + subscription.url.toID(), ) } root.setOnLongClickListener { - ChannelOptionsBottomSheet(subscription.url!!.toID(), subscription.name) + ChannelOptionsBottomSheet(subscription.url.toID(), subscription.name) .show((root.context as BaseActivity).supportFragmentManager) true } 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 6d30a1153..79b03f5da 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 @@ -40,7 +40,8 @@ class PlaylistsAdapter( // set imageview drawable as empty playlist if imageview empty if (playlist.thumbnail.orEmpty().split("/").size <= 4) { playlistThumbnail.setImageResource(R.drawable.ic_empty_playlist) - playlistThumbnail.setBackgroundColor(R.attr.colorSurface) + playlistThumbnail + .setBackgroundColor(com.google.android.material.R.attr.colorSurface) } else { ImageHelper.loadImage(playlist.thumbnail, playlistThumbnail) } 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 dc2976cb1..12e58ff7d 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 @@ -38,13 +38,13 @@ class SubscriptionChannelAdapter( NavigationHelper.navigateChannel(root.context, subscription.url) } root.setOnLongClickListener { - ChannelOptionsBottomSheet(subscription.url!!.toID(), subscription.name) + ChannelOptionsBottomSheet(subscription.url.toID(), subscription.name) .show((root.context as BaseActivity).supportFragmentManager) true } subscriptionSubscribe.setupSubscriptionButton( - subscription.url?.toID(), + subscription.url.toID(), subscription.name, notificationBell, true, 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 6a71f24f9..76e718f50 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 @@ -21,7 +21,7 @@ class ErrorDialog : DialogFragment() { .setTitle(R.string.error_occurred) .setMessage(errorLog) .setNegativeButton(R.string.okay, null) - .setPositiveButton(R.string.copy) { _, _ -> + .setPositiveButton(androidx.preference.R.string.copy) { _, _ -> ClipboardHelper.save(requireContext(), errorLog) Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() } 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 494e017b6..bbd28d713 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 @@ -41,7 +41,7 @@ class NotificationSettings : BasePreferenceFragment() { NotificationHelper .enqueueWork( context = requireContext(), - existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE, + existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.UPDATE, ) } } diff --git a/app/src/main/java/com/github/libretube/ui/sheets/CommentsSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/CommentsSheet.kt index a25ca80d3..13b32335b 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/CommentsSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/CommentsSheet.kt @@ -102,7 +102,7 @@ class CommentsSheet : ExpandedBottomSheet() { // BottomSheetDialogFragment passthrough user outside touch event dialog.setOnShowListener { - dialog.findViewById(R.id.touch_outside)?.apply { + dialog.findViewById(com.google.android.material.R.id.touch_outside)?.apply { setOnTouchListener { v, event -> event.setLocation(event.rawX - v.x, event.rawY - v.y) activity?.dispatchTouchEvent(event) 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 5296cb4ea..1fe184aeb 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 @@ -7,7 +7,6 @@ import android.view.MotionEvent import android.view.MotionEvent.ACTION_MOVE import android.view.ViewConfiguration import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.github.libretube.R import com.github.libretube.helpers.ThemeHelper import com.google.android.material.elevation.SurfaceColors import kotlin.math.abs @@ -18,7 +17,9 @@ class CustomSwipeToRefresh(context: Context?, attrs: AttributeSet?) : private var mPrevX = 0f init { - setColorSchemeColors(ThemeHelper.getThemeColor(this.context, R.attr.colorPrimary)) + setColorSchemeColors( + ThemeHelper.getThemeColor(this.context, androidx.appcompat.R.attr.colorPrimary), + ) setProgressBackgroundColorSchemeColor( SurfaceColors.getColorForElevation(this.context, 20f), ) 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 234fc751e..09d26e6e2 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 @@ -7,7 +7,6 @@ import android.graphics.Rect import android.util.AttributeSet import android.view.View import androidx.core.view.marginLeft -import com.github.libretube.R import com.github.libretube.api.obj.Segment import com.github.libretube.constants.PreferenceKeys import com.github.libretube.extensions.dpToPx @@ -56,7 +55,10 @@ class MarkableTimeBar( canvas.height - marginY, ), Paint().apply { - color = ThemeHelper.getThemeColor(context, R.attr.colorOnSecondary) + color = ThemeHelper.getThemeColor( + context, + com.google.android.material.R.attr.colorOnSecondary, + ) }, ) } diff --git a/build.gradle b/build.gradle index dee15d07e..7708abf3d 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" diff --git a/gradle.properties b/gradle.properties index dcdc79c66..c57869e52 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=true +android.nonFinalResIds=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f398c33c4..42defcc94 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists