diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 390977147..609c6e0aa 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -199,6 +199,9 @@ sonar {
}
dependencies {
+ // Compose UI
+ implementation(project(":composeApp"))
+
/** Desugaring **/
coreLibraryDesugaring(libs.android.desugar)
@@ -228,7 +231,6 @@ dependencies {
implementation(libs.androidx.room.rxjava3)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.swiperefreshlayout)
- implementation(libs.androidx.viewpager2)
implementation(libs.androidx.work.runtime)
implementation(libs.androidx.work.rxjava3)
implementation(libs.google.android.material)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 67a33742b..3ff502277 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -91,11 +91,6 @@
android:exported="false"
android:label="@string/settings" />
-
-
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
deleted file mode 100644
index ed5951f04..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
+++ /dev/null
@@ -1,260 +0,0 @@
-package org.schabi.newpipe.about
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Button
-import androidx.annotation.StringRes
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import androidx.viewpager2.adapter.FragmentStateAdapter
-import com.google.android.material.tabs.TabLayoutMediator
-import org.schabi.newpipe.BuildConfig
-import org.schabi.newpipe.R
-import org.schabi.newpipe.databinding.ActivityAboutBinding
-import org.schabi.newpipe.databinding.FragmentAboutBinding
-import org.schabi.newpipe.util.ThemeHelper
-import org.schabi.newpipe.util.external_communication.ShareUtils
-
-class AboutActivity : AppCompatActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- ThemeHelper.setTheme(this)
- title = getString(R.string.title_activity_about)
-
- val aboutBinding = ActivityAboutBinding.inflate(layoutInflater)
- setContentView(aboutBinding.root)
- setSupportActionBar(aboutBinding.aboutToolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
-
- // Create the adapter that will return a fragment for each of the three
- // primary sections of the activity.
- val mAboutStateAdapter = AboutStateAdapter(this)
- // Set up the ViewPager with the sections adapter.
- aboutBinding.aboutViewPager2.adapter = mAboutStateAdapter
- TabLayoutMediator(
- aboutBinding.aboutTabLayout,
- aboutBinding.aboutViewPager2
- ) { tab, position ->
- tab.setText(mAboutStateAdapter.getPageTitle(position))
- }.attach()
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == android.R.id.home) {
- finish()
- return true
- }
- return super.onOptionsItemSelected(item)
- }
-
- /**
- * A placeholder fragment containing a simple view.
- */
- class AboutFragment : Fragment() {
- private fun Button.openLink(@StringRes url: Int) {
- setOnClickListener {
- ShareUtils.openUrlInApp(context, requireContext().getString(url))
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- FragmentAboutBinding.inflate(inflater, container, false).apply {
- aboutAppVersion.text = BuildConfig.VERSION_NAME
- aboutGithubLink.openLink(R.string.github_url)
- aboutDonationLink.openLink(R.string.donation_url)
- aboutWebsiteLink.openLink(R.string.website_url)
- aboutPrivacyPolicyLink.openLink(R.string.privacy_policy_url)
- faqLink.openLink(R.string.faq_url)
- return root
- }
- }
- }
-
- /**
- * A [FragmentStateAdapter] that returns a fragment corresponding to
- * one of the sections/tabs/pages.
- */
- private class AboutStateAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
- private val posAbout = 0
- private val posLicense = 1
- private val totalCount = 2
-
- override fun createFragment(position: Int): Fragment {
- return when (position) {
- posAbout -> AboutFragment()
- posLicense -> LicenseFragment.newInstance(SOFTWARE_COMPONENTS)
- else -> error("Unknown position for ViewPager2")
- }
- }
-
- override fun getItemCount(): Int {
- // Show 2 total pages.
- return totalCount
- }
-
- fun getPageTitle(position: Int): Int {
- return when (position) {
- posAbout -> R.string.tab_about
- posLicense -> R.string.tab_licenses
- else -> error("Unknown position for ViewPager2")
- }
- }
- }
-
- companion object {
- /**
- * List of all software components.
- */
- private val SOFTWARE_COMPONENTS = arrayListOf(
- SoftwareComponent(
- "ACRA",
- "2013",
- "Kevin Gaudin",
- "https://github.com/ACRA/acra",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "AndroidX",
- "2005 - 2011",
- "The Android Open Source Project",
- "https://developer.android.com/jetpack",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "ExoPlayer",
- "2014 - 2020",
- "Google, Inc.",
- "https://github.com/google/ExoPlayer",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "GigaGet",
- "2014 - 2015",
- "Peter Cai",
- "https://github.com/PaperAirplane-Dev-Team/GigaGet",
- StandardLicenses.GPL3
- ),
- SoftwareComponent(
- "Groupie",
- "2016",
- "Lisa Wray",
- "https://github.com/lisawray/groupie",
- StandardLicenses.MIT
- ),
- SoftwareComponent(
- "Android-State",
- "2018",
- "Evernote",
- "https://github.com/Evernote/android-state",
- StandardLicenses.EPL1
- ),
- SoftwareComponent(
- "Bridge",
- "2021",
- "Livefront",
- "https://github.com/livefront/bridge",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "Jsoup",
- "2009 - 2020",
- "Jonathan Hedley",
- "https://github.com/jhy/jsoup",
- StandardLicenses.MIT
- ),
- SoftwareComponent(
- "Markwon",
- "2019",
- "Dimitry Ivanov",
- "https://github.com/noties/Markwon",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "Material Components for Android",
- "2016 - 2020",
- "Google, Inc.",
- "https://github.com/material-components/material-components-android",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "NewPipe Extractor",
- "2017 - 2020",
- "Christian Schabesberger",
- "https://github.com/TeamNewPipe/NewPipeExtractor",
- StandardLicenses.GPL3
- ),
- SoftwareComponent(
- "NoNonsense-FilePicker",
- "2016",
- "Jonas Kalderstam",
- "https://github.com/spacecowboy/NoNonsense-FilePicker",
- StandardLicenses.MPL2
- ),
- SoftwareComponent(
- "OkHttp",
- "2019",
- "Square, Inc.",
- "https://square.github.io/okhttp/",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "Coil",
- "2023",
- "Coil Contributors",
- "https://coil-kt.github.io/coil/",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "PrettyTime",
- "2012 - 2020",
- "Lincoln Baxter, III",
- "https://github.com/ocpsoft/prettytime",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "ProcessPhoenix",
- "2015",
- "Jake Wharton",
- "https://github.com/JakeWharton/ProcessPhoenix",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "RxAndroid",
- "2015",
- "The RxAndroid authors",
- "https://github.com/ReactiveX/RxAndroid",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "RxBinding",
- "2015",
- "Jake Wharton",
- "https://github.com/JakeWharton/RxBinding",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "RxJava",
- "2016 - 2020",
- "RxJava Contributors",
- "https://github.com/ReactiveX/RxJava",
- StandardLicenses.APACHE2
- ),
- SoftwareComponent(
- "SearchPreference",
- "2018",
- "ByteHamster",
- "https://github.com/ByteHamster/SearchPreference",
- StandardLicenses.MIT
- )
- )
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/about/License.kt b/app/src/main/java/org/schabi/newpipe/about/License.kt
deleted file mode 100644
index fc50c646d..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/License.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.schabi.newpipe.about
-
-import android.os.Parcelable
-import java.io.Serializable
-import kotlinx.parcelize.Parcelize
-
-/**
- * Class for storing information about a software license.
- */
-@Parcelize
-class License(val name: String, val abbreviation: String, val filename: String) : Parcelable, Serializable
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt
deleted file mode 100644
index bd0632c13..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.kt
+++ /dev/null
@@ -1,142 +0,0 @@
-package org.schabi.newpipe.about
-
-import android.os.Bundle
-import android.util.Base64
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.webkit.WebView
-import androidx.appcompat.app.AlertDialog
-import androidx.core.os.BundleCompat
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
-import io.reactivex.rxjava3.core.Observable
-import io.reactivex.rxjava3.disposables.CompositeDisposable
-import io.reactivex.rxjava3.disposables.Disposable
-import io.reactivex.rxjava3.schedulers.Schedulers
-import org.schabi.newpipe.BuildConfig
-import org.schabi.newpipe.R
-import org.schabi.newpipe.databinding.FragmentLicensesBinding
-import org.schabi.newpipe.databinding.ItemSoftwareComponentBinding
-import org.schabi.newpipe.ktx.parcelableArrayList
-import org.schabi.newpipe.util.external_communication.ShareUtils
-
-/**
- * Fragment containing the software licenses.
- */
-class LicenseFragment : Fragment() {
- private lateinit var softwareComponents: List
- private var activeSoftwareComponent: SoftwareComponent? = null
- private val compositeDisposable = CompositeDisposable()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- softwareComponents = arguments?.parcelableArrayList(ARG_COMPONENTS)!!
- .sortedBy { it.name } // Sort components by name
- activeSoftwareComponent = savedInstanceState?.let {
- BundleCompat.getSerializable(it, SOFTWARE_COMPONENT_KEY, SoftwareComponent::class.java)
- }
- }
-
- override fun onDestroy() {
- compositeDisposable.dispose()
- super.onDestroy()
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- val binding = FragmentLicensesBinding.inflate(inflater, container, false)
- binding.licensesAppReadLicense.setOnClickListener {
- compositeDisposable.add(
- showLicense(NEWPIPE_SOFTWARE_COMPONENT)
- )
- }
- for (component in softwareComponents) {
- val componentBinding = ItemSoftwareComponentBinding
- .inflate(inflater, container, false)
- componentBinding.name.text = component.name
- componentBinding.copyright.text = getString(
- R.string.copyright,
- component.years,
- component.copyrightOwner,
- component.license.abbreviation
- )
- val root: View = componentBinding.root
- root.tag = component
- root.setOnClickListener {
- compositeDisposable.add(
- showLicense(component)
- )
- }
- binding.licensesSoftwareComponents.addView(root)
- registerForContextMenu(root)
- }
- activeSoftwareComponent?.let { compositeDisposable.add(showLicense(it)) }
- return binding.root
- }
-
- override fun onSaveInstanceState(savedInstanceState: Bundle) {
- super.onSaveInstanceState(savedInstanceState)
- activeSoftwareComponent?.let { savedInstanceState.putSerializable(SOFTWARE_COMPONENT_KEY, it) }
- }
-
- private fun showLicense(
- softwareComponent: SoftwareComponent
- ): Disposable {
- return if (context == null) {
- Disposable.empty()
- } else {
- val context = requireContext()
- activeSoftwareComponent = softwareComponent
- Observable.fromCallable { getFormattedLicense(context, softwareComponent.license) }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe { formattedLicense ->
- val webViewData = Base64.encodeToString(
- formattedLicense.toByteArray(),
- Base64.NO_PADDING
- )
- val webView = WebView(context)
- webView.loadData(webViewData, "text/html; charset=UTF-8", "base64")
-
- val builder = AlertDialog.Builder(requireContext())
- .setTitle(softwareComponent.name)
- .setView(webView)
- .setOnCancelListener { activeSoftwareComponent = null }
- .setOnDismissListener { activeSoftwareComponent = null }
- .setPositiveButton(R.string.done) { dialog, _ -> dialog.dismiss() }
-
- if (softwareComponent != NEWPIPE_SOFTWARE_COMPONENT) {
- builder.setNeutralButton(R.string.open_website_license) { _, _ ->
- ShareUtils.openUrlInApp(requireContext(), softwareComponent.link)
- }
- }
-
- builder.show()
- }
- }
- }
-
- companion object {
- private const val ARG_COMPONENTS = "components"
- private const val SOFTWARE_COMPONENT_KEY = "ACTIVE_SOFTWARE_COMPONENT"
- private val NEWPIPE_SOFTWARE_COMPONENT = SoftwareComponent(
- "NewPipe",
- "2014-2023",
- "Team NewPipe",
- "https://newpipe.net/",
- StandardLicenses.GPL3,
- BuildConfig.VERSION_NAME
- )
-
- fun newInstance(softwareComponents: ArrayList): LicenseFragment {
- val fragment = LicenseFragment()
- fragment.arguments = bundleOf(ARG_COMPONENTS to softwareComponents)
- return fragment
- }
- }
-}
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.kt b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.kt
deleted file mode 100644
index 32e4f812f..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.schabi.newpipe.about
-
-import android.content.Context
-import java.io.IOException
-import org.schabi.newpipe.R
-import org.schabi.newpipe.util.ThemeHelper
-
-/**
- * @param context the context to use
- * @param license the license
- * @return String which contains a HTML formatted license page
- * styled according to the context's theme
- */
-fun getFormattedLicense(context: Context, license: License): String {
- try {
- return context.assets.open(license.filename).bufferedReader().use { it.readText() }
- // split the HTML file and insert the stylesheet into the HEAD of the file
- .replace("", "")
- } catch (e: IOException) {
- throw IllegalArgumentException("Could not get license file: ${license.filename}", e)
- }
-}
-
-/**
- * @param context the Android context
- * @return String which is a CSS stylesheet according to the context's theme
- */
-fun getLicenseStylesheet(context: Context): String {
- val isLightTheme = ThemeHelper.isLightThemeSelected(context)
- val licenseBackgroundColor = getHexRGBColor(
- context,
- if (isLightTheme) R.color.light_license_background_color else R.color.dark_license_background_color
- )
- val licenseTextColor = getHexRGBColor(
- context,
- if (isLightTheme) R.color.light_license_text_color else R.color.dark_license_text_color
- )
- val youtubePrimaryColor = getHexRGBColor(
- context,
- if (isLightTheme) R.color.light_youtube_primary_color else R.color.dark_youtube_primary_color
- )
- return "body{padding:12px 15px;margin:0;background:#$licenseBackgroundColor;color:#$licenseTextColor}" +
- "a[href]{color:#$youtubePrimaryColor}pre{white-space:pre-wrap}"
-}
-
-/**
- * Cast R.color to a hexadecimal color value.
- *
- * @param context the context to use
- * @param color the color number from R.color
- * @return a six characters long String with hexadecimal RGB values
- */
-fun getHexRGBColor(context: Context, color: Int): String {
- return context.getString(color).substring(3)
-}
diff --git a/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt b/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt
deleted file mode 100644
index a43ddfd5e..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.schabi.newpipe.about
-
-import android.os.Parcelable
-import java.io.Serializable
-import kotlinx.parcelize.Parcelize
-
-@Parcelize
-class SoftwareComponent
-@JvmOverloads
-constructor(
- val name: String,
- val years: String,
- val copyrightOwner: String,
- val link: String,
- val license: License,
- val version: String? = null
-) : Parcelable, Serializable
diff --git a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.kt b/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.kt
deleted file mode 100644
index c5b9618fe..000000000
--- a/app/src/main/java/org/schabi/newpipe/about/StandardLicenses.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.schabi.newpipe.about
-
-/**
- * Class containing information about standard software licenses.
- */
-object StandardLicenses {
- @JvmField
- val GPL3 = License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html")
-
- @JvmField
- val APACHE2 = License("Apache License, Version 2.0", "ALv2", "apache2.html")
-
- @JvmField
- val MPL2 = License("Mozilla Public License, Version 2.0", "MPL 2.0", "mpl2.html")
-
- @JvmField
- val MIT = License("MIT License", "MIT", "mit.html")
-
- @JvmField
- val EPL1 = License("Eclipse Public License, Version 1.0", "EPL 1.0", "epl1.html")
-}
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
index 9632b9bee..59cd71d02 100644
--- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java
@@ -25,11 +25,13 @@ import androidx.fragment.app.FragmentTransaction;
import com.jakewharton.processphoenix.ProcessPhoenix;
+import net.newpipe.app.extensions.ContextKt;
+import net.newpipe.app.navigation.Screen;
+
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.RouterActivity;
-import org.schabi.newpipe.about.AboutActivity;
import org.schabi.newpipe.database.feed.model.FeedGroupEntity;
import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.error.ErrorUtil;
@@ -682,8 +684,7 @@ public final class NavigationHelper {
}
public static void openAbout(final Context context) {
- final Intent intent = new Intent(context, AboutActivity.class);
- context.startActivity(intent);
+ ContextKt.navigateTo(context, Screen.About.INSTANCE);
}
public static void openSettings(final Context context) {
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
deleted file mode 100644
index 661c4affc..000000000
--- a/app/src/main/res/layout/activity_about.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_licenses.xml b/app/src/main/res/layout/fragment_licenses.xml
deleted file mode 100644
index c46d888ba..000000000
--- a/app/src/main/res/layout/fragment_licenses.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 120daffa1..3cce13570 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -33,7 +33,7 @@ kotlin {
namespace = "net.newpipe.app"
compileSdk {
version = release(36) {
- minorApiLevel = 1
+ minorApiLevel = 0
}
}
minSdk = 23
@@ -65,7 +65,9 @@ kotlin {
implementation(libs.jetbrains.lifecycle.viewmodel)
- implementation(libs.jetbrains.navigation3.ui)
+ // Use API as java compiler cannot see NavKey for some reason
+ api(libs.jetbrains.navigation3.ui)
+
implementation(libs.jetbrains.lifecycle.navigation3)
implementation(libs.kotlinx.serialization.json)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index bdc919418..cd0c962b5 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -70,7 +70,6 @@ teamnewpipe-nanojson = "e9d656ddb49a412a5a0a5d5ef20ca7ef09549996"
# If there’s already a git hash, just add more of it to the end (or remove a letter)
# to cause jitpack to regenerate the artifact.
teamnewpipe-newpipe-extractor = "1512cf3222b0c5d87a249e6ac231b98090c42623"
-viewpager2 = "1.1.0"
webkit = "1.15.0"
work = "2.11.2"
@@ -98,7 +97,6 @@ androidx-room-rxjava3 = { module = "androidx.room:room-rxjava3", version.ref = "
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "room" }
androidx-runner = { module = "androidx.test:runner", version.ref = "runner" }
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
-androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "work" }
androidx-work-rxjava3 = { module = "androidx.work:work-rxjava3", version.ref = "work" }