Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package org.schabi.newpipe.settings

import android.content.Context
import coil3.SingletonImageLoader
import net.newpipe.app.platform.ContentHost
import org.schabi.newpipe.DownloaderImpl
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.player.helper.PlayerHelper
import org.schabi.newpipe.util.Localization
import org.schabi.newpipe.util.image.ImageStrategy
import org.schabi.newpipe.util.image.PreferredImageQuality

class ComposeContentHost(private val context: Context) : ContentHost {

override fun applyImageQuality(quality: String) {
ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(context, quality))
val loader = SingletonImageLoader.get(context)
loader.memoryCache?.clear()
loader.diskCache?.clear()
}

override fun applyYoutubeRestrictedMode(enabled: Boolean) = DownloaderImpl.getInstance().updateYoutubeRestrictedModeCookies(enabled)

override fun applyContentLocalization() {
NewPipe.setupLocalization(
Localization.getPreferredLocalization(context),
Localization.getPreferredContentCountry(context)
)
PlayerHelper.resetFormat()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package net.newpipe.app.platform

import android.app.LocaleManager
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings as AndroidSettings
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import co.touchlab.kermit.Logger
import net.newpipe.app.preferences.ContentPreferences
import org.koin.core.annotation.Singleton

@Singleton(binds = [ContentActions::class])
class AndroidContentActions(private val context: Context) : ContentActions {

private val host: ContentHost? = loadHost(context)

// Android 13 moved per-app languages into the system settings
override val usesSystemAppLanguageSettings: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU

override fun systemAppLanguageName(): String? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return null
}
val locales = context.getSystemService(LocaleManager::class.java).applicationLocales
return if (locales.isEmpty) null else locales[0].displayName
}

override fun openSystemAppLanguageSettings() {
try {
context.startActivity(
Intent(
AndroidSettings.ACTION_APP_LOCALE_SETTINGS,
Uri.fromParts(SCHEME_PACKAGE, context.packageName, null)
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} catch (exception: ActivityNotFoundException) {
Logger.e(messageString = "No activity found for app language settings", throwable = exception)
}
}

override fun applyAppLanguage(languageCode: String) = AppCompatDelegate.setApplicationLocales(
when (languageCode) {
ContentPreferences.DEFAULT_LOCALIZATION -> LocaleListCompat.getEmptyLocaleList()
else -> LocaleListCompat.forLanguageTags(languageCode)
}
)

override fun applyImageQuality(quality: String) {
host?.applyImageQuality(quality)
}

override fun applyYoutubeRestrictedMode(enabled: Boolean) {
host?.applyYoutubeRestrictedMode(enabled)
}

override fun applyContentLocalization() {
host?.applyContentLocalization()
}

private fun loadHost(context: Context): ContentHost? = runCatching {
Class.forName(HOST_CLASS)
.getDeclaredConstructor(Context::class.java)
.newInstance(context) as ContentHost
}.onFailure { error ->
Logger.w(messageString = "No host content actions found, skipping host-side changes", throwable = error)
}.getOrNull()

private companion object {
const val HOST_CLASS = "org.schabi.newpipe.settings.ComposeContentHost"
const val SCHEME_PACKAGE = "package"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package net.newpipe.app.platform

import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.koin.core.annotation.Singleton

@Singleton(binds = [StringSetStore::class])
class AndroidStringSetStore(context: Context) : StringSetStore {

private val preferences = PreferenceManager.getDefaultSharedPreferences(context)

override fun get(key: String, defaultValue: Set<String>): Set<String> = preferences
.getStringSet(key, null)?.toSet() ?: defaultValue

override fun put(key: String, value: Set<String>) = preferences.edit {
putStringSet(key, value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package net.newpipe.app.platform

/**
* Hooks into the image, network and extractor helpers that still live in the current Android app
*/
interface ContentHost {

fun applyImageQuality(quality: String)

fun applyYoutubeRestrictedMode(enabled: Boolean)

fun applyContentLocalization()
}
Loading
Loading