mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-05-24 16:35:49 -04:00
Support for SOCKS proxy
adds a new setting and exposes the proxy in all those places where we do network requests
This commit is contained in:
@@ -5,6 +5,7 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.fdroid.BuildConfig
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@@ -15,13 +16,13 @@ object DownloadModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHttpManager(): HttpManager {
|
||||
return HttpManager(userAgent = USER_AGENT)
|
||||
fun provideHttpManager(settingsManager: SettingsManager): HttpManager {
|
||||
return HttpManager(userAgent = USER_AGENT, proxyConfig = settingsManager.proxyConfig)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDownloaderFactory(httpManager: HttpManager): DownloaderFactory {
|
||||
return DownloaderFactoryImpl(httpManager)
|
||||
}
|
||||
fun provideDownloaderFactory(
|
||||
downloaderFactoryImpl: DownloaderFactoryImpl,
|
||||
): DownloaderFactory = downloaderFactoryImpl
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ import android.net.Uri
|
||||
import org.fdroid.IndexFile
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.index.IndexFormatVersion
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class DownloaderFactoryImpl @Inject constructor(
|
||||
private val httpManager: HttpManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
) : DownloaderFactory() {
|
||||
override fun create(
|
||||
repo: Repository,
|
||||
@@ -31,7 +35,7 @@ class DownloaderFactoryImpl @Inject constructor(
|
||||
val request = DownloadRequest(
|
||||
indexFile = indexFile,
|
||||
mirrors = mirrors,
|
||||
proxy = null,
|
||||
proxy = settingsManager.proxyConfig,
|
||||
username = repo.username,
|
||||
password = repo.password,
|
||||
tryFirstMirror = tryFirst,
|
||||
|
||||
@@ -2,10 +2,11 @@ package org.fdroid.download
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.IndexFile
|
||||
import org.fdroid.database.Repository
|
||||
|
||||
fun IndexFile.getImageModel(repository: Repository?): Any? {
|
||||
fun IndexFile.getImageModel(repository: Repository?, proxyConfig: ProxyConfig?): Any? {
|
||||
if (repository == null) return null
|
||||
val address = repository.address
|
||||
if (address.startsWith("content://") || address.startsWith("file://")) {
|
||||
@@ -14,7 +15,7 @@ fun IndexFile.getImageModel(repository: Repository?): Any? {
|
||||
return DownloadRequest(
|
||||
indexFile = this,
|
||||
mirrors = repository.getMirrors(),
|
||||
proxy = null, // TODO proxy support
|
||||
proxy = proxyConfig,
|
||||
username = repository.username,
|
||||
password = repository.password,
|
||||
)
|
||||
|
||||
@@ -13,6 +13,9 @@ object SettingsConstants {
|
||||
const val PREF_KEY_AUTO_UPDATES = "autoUpdates"
|
||||
const val PREF_DEFAULT_AUTO_UPDATES = true
|
||||
|
||||
const val PREF_KEY_PROXY = "proxy"
|
||||
const val PREF_DEFAULT_PROXY = ""
|
||||
|
||||
const val PREF_KEY_SHOW_INCOMPATIBLE = "incompatibleVersions"
|
||||
const val PREF_DEFAULT_SHOW_INCOMPATIBLE = true
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import androidx.core.content.edit
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.ktor.client.engine.ProxyBuilder
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -14,11 +16,13 @@ import org.fdroid.database.AppListSortOrder
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_APP_LIST_SORT_ORDER
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_LAST_UPDATE_CHECK
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_SHOW_INCOMPATIBLE
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_THEME
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_APP_LIST_SORT_ORDER
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_LAST_UPDATE_CHECK
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_SHOW_INCOMPATIBLE
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_THEME
|
||||
import org.fdroid.settings.SettingsConstants.getAppListSortOrder
|
||||
@@ -61,6 +65,16 @@ class SettingsManager @Inject constructor(
|
||||
private val _lastRepoUpdateFlow = MutableStateFlow(lastRepoUpdate)
|
||||
val lastRepoUpdateFlow = _lastRepoUpdateFlow.asStateFlow()
|
||||
|
||||
val proxyConfig: ProxyConfig?
|
||||
get() {
|
||||
val proxyStr = prefs.getString(PREF_KEY_PROXY, PREF_DEFAULT_PROXY)
|
||||
return if (proxyStr.isNullOrBlank()) null
|
||||
else {
|
||||
val (host, port) = proxyStr.split(':')
|
||||
ProxyBuilder.socks(host, port.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
val filterIncompatible: Boolean
|
||||
get() = !prefs.getBoolean(PREF_KEY_SHOW_INCOMPATIBLE, PREF_DEFAULT_SHOW_INCOMPATIBLE)
|
||||
val appListSortOrder: AppListSortOrder
|
||||
|
||||
@@ -250,6 +250,7 @@ fun Main(onListeningForIntent: () -> Unit = {}) {
|
||||
}
|
||||
AddRepo(
|
||||
state = viewModel.state.collectAsStateWithLifecycle().value,
|
||||
proxyConfig = viewModel.proxyConfig,
|
||||
onFetchRepo = viewModel::onFetchRepo,
|
||||
onAddRepo = viewModel::addFetchedRepository,
|
||||
onExistingRepo = { repoId ->
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.fdroid.download.getImageModel
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.install.InstallState
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.updates.UpdatesManager
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
import javax.inject.Inject
|
||||
@@ -34,6 +35,7 @@ class MyAppsViewModel @Inject constructor(
|
||||
@param:IoDispatcher private val scope: CoroutineScope,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val db: FDroidDatabase,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val appInstallManager: AppInstallManager,
|
||||
private val updatesManager: UpdatesManager,
|
||||
private val repoManager: RepoManager,
|
||||
@@ -49,6 +51,7 @@ class MyAppsViewModel @Inject constructor(
|
||||
private var installedAppsLiveData =
|
||||
db.getAppDao().getInstalledAppListItems(application.packageManager)
|
||||
private val installedAppsObserver = Observer<List<AppListItem>> { list ->
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
installedApps.value = list.map { app ->
|
||||
InstalledAppItem(
|
||||
packageName = app.packageName,
|
||||
@@ -56,7 +59,7 @@ class MyAppsViewModel @Inject constructor(
|
||||
installedVersionName = app.installedVersionName ?: "???",
|
||||
lastUpdated = app.lastUpdated,
|
||||
iconModel = repoManager.getRepository(app.repoId)?.let { repo ->
|
||||
app.getIcon(localeList)?.getImageModel(repo)
|
||||
app.getIcon(localeList)?.getImageModel(repo, proxyConfig)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ fun AppDetailsHeader(
|
||||
repos = item.repositories,
|
||||
currentRepoId = item.app.repoId,
|
||||
preferredRepoId = item.preferredRepoId,
|
||||
proxy = item.proxy,
|
||||
onRepoChanged = item.actions.onRepoChanged,
|
||||
onPreferredRepoChanged = item.actions.onPreferredRepoChanged,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.database.App
|
||||
import org.fdroid.database.AppMetadata
|
||||
import org.fdroid.database.AppPrefs
|
||||
@@ -63,6 +64,7 @@ data class AppDetailsItem(
|
||||
*/
|
||||
val noUpdatesBecauseDifferentSigner: Boolean = false,
|
||||
val authorHasMoreThanOneApp: Boolean = false,
|
||||
val proxy: ProxyConfig?,
|
||||
) {
|
||||
constructor(
|
||||
repository: Repository,
|
||||
@@ -80,6 +82,7 @@ data class AppDetailsItem(
|
||||
noUpdatesBecauseDifferentSigner: Boolean,
|
||||
authorHasMoreThanOneApp: Boolean,
|
||||
localeList: LocaleListCompat,
|
||||
proxy: ProxyConfig?,
|
||||
) : this(
|
||||
app = dbApp.metadata,
|
||||
actions = actions,
|
||||
@@ -89,10 +92,10 @@ data class AppDetailsItem(
|
||||
name = dbApp.name ?: "Unknown App",
|
||||
summary = dbApp.summary,
|
||||
description = getHtmlDescription(dbApp.getDescription(localeList)),
|
||||
icon = dbApp.getIcon(localeList)?.getImageModel(repository),
|
||||
featureGraphic = dbApp.getFeatureGraphic(localeList)?.getImageModel(repository),
|
||||
icon = dbApp.getIcon(localeList)?.getImageModel(repository, proxy),
|
||||
featureGraphic = dbApp.getFeatureGraphic(localeList)?.getImageModel(repository, proxy),
|
||||
phoneScreenshots = dbApp.getPhoneScreenshots(localeList).mapNotNull {
|
||||
it.getImageModel(repository)
|
||||
it.getImageModel(repository, proxy)
|
||||
},
|
||||
categories = dbApp.metadata.categories?.mapNotNull { categoryId ->
|
||||
val category = repository.getCategories()[categoryId] ?: return@mapNotNull null
|
||||
@@ -108,11 +111,16 @@ data class AppDetailsItem(
|
||||
possibleUpdate = possibleUpdate,
|
||||
appPrefs = appPrefs,
|
||||
whatsNew = installedVersion?.getWhatsNew(localeList),
|
||||
antiFeatures = installedVersion?.getAntiFeatures(repository, localeList)
|
||||
?: suggestedVersion?.getAntiFeatures(repository, localeList)
|
||||
?: (versions?.first()?.version as? AppVersion).getAntiFeatures(repository, localeList),
|
||||
antiFeatures = installedVersion?.getAntiFeatures(repository, localeList, proxy)
|
||||
?: suggestedVersion?.getAntiFeatures(repository, localeList, proxy)
|
||||
?: (versions?.first()?.version as? AppVersion).getAntiFeatures(
|
||||
repository = repository,
|
||||
localeList = localeList,
|
||||
proxy = proxy,
|
||||
),
|
||||
noUpdatesBecauseDifferentSigner = noUpdatesBecauseDifferentSigner,
|
||||
authorHasMoreThanOneApp = authorHasMoreThanOneApp,
|
||||
proxy = proxy,
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -242,12 +250,13 @@ data class AntiFeature(
|
||||
private fun AppVersion?.getAntiFeatures(
|
||||
repository: Repository,
|
||||
localeList: LocaleListCompat,
|
||||
proxy: ProxyConfig?,
|
||||
): List<AntiFeature>? {
|
||||
return this?.antiFeatureKeys?.mapNotNull { key ->
|
||||
val antiFeature = repository.getAntiFeatures()[key] ?: return@mapNotNull null
|
||||
AntiFeature(
|
||||
id = key,
|
||||
icon = antiFeature.getIcon(localeList)?.getImageModel(repository),
|
||||
icon = antiFeature.getIcon(localeList)?.getImageModel(repository, proxy),
|
||||
name = antiFeature.getName(localeList) ?: key,
|
||||
reason = getAntiFeatureReason(key, localeList),
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.fdroid.index.RELEASE_CHANNEL_BETA
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.install.InstallState
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.updates.UpdatesManager
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
import javax.inject.Inject
|
||||
@@ -45,6 +46,7 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val repoManager: RepoManager,
|
||||
private val updateChecker: UpdateChecker,
|
||||
private val updatesManager: UpdatesManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val appInstallManager: AppInstallManager,
|
||||
) : AndroidViewModel(app) {
|
||||
private val log = KotlinLogging.logger { }
|
||||
@@ -62,6 +64,7 @@ class AppDetailsViewModel @Inject constructor(
|
||||
viewModel = this,
|
||||
packageInfoFlow = packageInfoFlow,
|
||||
currentRepoIdFlow = currentRepoIdFlow,
|
||||
settingsManager = settingsManager,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.fdroid.database.Repository
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.install.InstallState
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.utils.sha256
|
||||
|
||||
private const val TAG = "DetailsPresenter"
|
||||
@@ -28,6 +29,7 @@ fun DetailsPresenter(
|
||||
db: FDroidDatabase,
|
||||
repoManager: RepoManager,
|
||||
updateChecker: UpdateChecker,
|
||||
settingsManager: SettingsManager,
|
||||
appInstallManager: AppInstallManager,
|
||||
viewModel: AppDetailsViewModel,
|
||||
packageInfoFlow: StateFlow<AppInfo?>,
|
||||
@@ -190,6 +192,7 @@ fun DetailsPresenter(
|
||||
noUpdatesBecauseDifferentSigner = noUpdatesBecauseDifferentSigner,
|
||||
authorHasMoreThanOneApp = authorHasMoreThanOneApp,
|
||||
localeList = locales,
|
||||
proxy = settingsManager.proxyConfig,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
@@ -43,6 +44,7 @@ fun RepoChooser(
|
||||
repos: List<Repository>,
|
||||
currentRepoId: Long,
|
||||
preferredRepoId: Long,
|
||||
proxy: ProxyConfig?,
|
||||
onRepoChanged: (Long) -> Unit,
|
||||
onPreferredRepoChanged: (Long) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -79,7 +81,7 @@ fun RepoChooser(
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
RepoIcon(repo = currentRepo, modifier = Modifier.size(24.dp))
|
||||
RepoIcon(repo = currentRepo, proxy = proxy, modifier = Modifier.size(24.dp))
|
||||
},
|
||||
trailingIcon = {
|
||||
if (repos.size > 1) Icon(
|
||||
@@ -115,6 +117,7 @@ fun RepoChooser(
|
||||
RepoMenuItem(
|
||||
repo = repo,
|
||||
isPreferred = repo.repoId == preferredRepoId,
|
||||
proxy = proxy,
|
||||
onClick = {
|
||||
onRepoChanged(repo.repoId)
|
||||
expanded = false
|
||||
@@ -140,6 +143,7 @@ fun RepoChooser(
|
||||
private fun RepoMenuItem(
|
||||
repo: Repository,
|
||||
isPreferred: Boolean,
|
||||
proxy: ProxyConfig?,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
@@ -152,7 +156,7 @@ private fun RepoMenuItem(
|
||||
},
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
leadingIcon = { RepoIcon(repo, Modifier.size(24.dp)) }
|
||||
leadingIcon = { RepoIcon(repo, proxy, Modifier.size(24.dp)) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,7 +176,7 @@ private fun getRepoString(repo: Repository, isPreferred: Boolean) = buildAnnotat
|
||||
fun RepoChooserSingleRepoPreview() {
|
||||
val repo1 = Repository(1L, "1", 1L, TWO, "null", 1L, 1, 1L)
|
||||
FDroidContent(pureBlack = true) {
|
||||
RepoChooser(listOf(repo1), 1L, 1L, {}, {})
|
||||
RepoChooser(listOf(repo1), 1L, 1L, null, {}, {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +187,7 @@ fun RepoChooserPreview() {
|
||||
val repo2 = Repository(2L, "2", 2L, TWO, "null", 2L, 2, 2L)
|
||||
val repo3 = Repository(3L, "2", 3L, TWO, "null", 3L, 3, 3L)
|
||||
FDroidContent(pureBlack = true) {
|
||||
RepoChooser(listOf(repo1, repo2, repo3), 1L, 1L, {}, {})
|
||||
RepoChooser(listOf(repo1, repo2, repo3), 1L, 1L, null, {}, {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +198,6 @@ fun RepoChooserNightPreview() {
|
||||
val repo2 = Repository(2L, "2", 2L, TWO, "null", 2L, 2, 2L)
|
||||
val repo3 = Repository(3L, "2", 3L, TWO, "null", 3L, 3, 3L)
|
||||
FDroidContent(pureBlack = true) {
|
||||
RepoChooser(listOf(repo1, repo2, repo3), 1L, 2L, {}, {})
|
||||
RepoChooser(listOf(repo1, repo2, repo3), 1L, 2L, null, {}, {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import app.cash.molecule.AndroidUiDispatcher
|
||||
import app.cash.molecule.RecompositionMode.ContextClock
|
||||
import app.cash.molecule.launchMolecule
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -41,7 +42,7 @@ class DiscoverViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val db: FDroidDatabase,
|
||||
updatesManager: UpdatesManager,
|
||||
settingsManager: SettingsManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val repoManager: RepoManager,
|
||||
@param:IoDispatcher private val ioScope: CoroutineScope,
|
||||
) : AndroidViewModel(app) {
|
||||
@@ -52,15 +53,17 @@ class DiscoverViewModel @Inject constructor(
|
||||
|
||||
val numUpdates = updatesManager.numUpdates
|
||||
val newApps = db.getAppDao().getNewAppsFlow().map { list ->
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
list.mapNotNull {
|
||||
val repository = repoManager.getRepository(it.repoId) ?: return@mapNotNull null
|
||||
it.toAppDiscoverItem(repository)
|
||||
it.toAppDiscoverItem(repository, proxyConfig)
|
||||
}
|
||||
}
|
||||
val recentlyUpdatedApps = db.getAppDao().getRecentlyUpdatedAppsFlow().map { list ->
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
list.mapNotNull {
|
||||
val repository = repoManager.getRepository(it.repoId) ?: return@mapNotNull null
|
||||
it.toAppDiscoverItem(repository)
|
||||
it.toAppDiscoverItem(repository, proxyConfig)
|
||||
}
|
||||
}
|
||||
private val categories = db.getRepositoryDao().getLiveCategories().asFlow().map { categories ->
|
||||
@@ -108,6 +111,7 @@ class DiscoverViewModel @Inject constructor(
|
||||
log.info { "Searching for: $query" }
|
||||
val timedApps = measureTimedValue {
|
||||
try {
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
db.getAppDao().getAppSearchItems(query).sortedDescending().mapNotNull {
|
||||
val repository = repoManager.getRepository(it.repoId) ?: return@mapNotNull null
|
||||
AppListItem(
|
||||
@@ -117,7 +121,7 @@ class DiscoverViewModel @Inject constructor(
|
||||
summary = it.summary.getBestLocale(localeList) ?: "",
|
||||
lastUpdated = it.lastUpdated,
|
||||
isCompatible = true, // doesn't matter here, as we don't filter
|
||||
iconModel = it.getIcon(localeList)?.getImageModel(repository),
|
||||
iconModel = it.getIcon(localeList)?.getImageModel(repository, proxyConfig),
|
||||
categoryIds = it.categories?.toSet(),
|
||||
)
|
||||
}
|
||||
@@ -144,10 +148,13 @@ class DiscoverViewModel @Inject constructor(
|
||||
searchResults.value = null
|
||||
}
|
||||
|
||||
private fun AppOverviewItem.toAppDiscoverItem(repository: Repository) = AppDiscoverItem(
|
||||
private fun AppOverviewItem.toAppDiscoverItem(
|
||||
repository: Repository,
|
||||
proxyConfig: ProxyConfig?,
|
||||
) = AppDiscoverItem(
|
||||
packageName = packageName,
|
||||
name = getName(localeList) ?: "Unknown App",
|
||||
lastUpdated = lastUpdated,
|
||||
imageModel = getIcon(localeList)?.getImageModel(repository),
|
||||
imageModel = getIcon(localeList)?.getImageModel(repository, proxyConfig),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ class AppListViewModel @Inject constructor(
|
||||
}.sortedWith { c1, c2 -> collator.compare(c1.name, c2.name) }
|
||||
}
|
||||
private val repositories = repoManager.repositoriesState.map { repositories ->
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
repositories.mapNotNull {
|
||||
if (it.enabled) RepositoryItem(it, localeList)
|
||||
if (it.enabled) RepositoryItem(it, localeList, proxyConfig)
|
||||
else null
|
||||
}.sortedBy { it.weight }
|
||||
}
|
||||
@@ -102,6 +103,7 @@ class AppListViewModel @Inject constructor(
|
||||
@WorkerThread
|
||||
private suspend fun loadApps(type: AppListType): List<AppListItem> {
|
||||
val appDao = db.getAppDao()
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
return when (type) {
|
||||
is AppListType.Author -> appDao.getAppsByAuthor(type.authorName)
|
||||
is AppListType.Category -> appDao.getAppsByCategory(type.categoryId)
|
||||
@@ -119,7 +121,7 @@ class AppListViewModel @Inject constructor(
|
||||
summary = it.getSummary(localeList) ?: "Unknown",
|
||||
lastUpdated = it.lastUpdated,
|
||||
isCompatible = it.isCompatible,
|
||||
iconModel = it.getIcon(localeList)?.getImageModel(repository),
|
||||
iconModel = it.getIcon(localeList)?.getImageModel(repository, proxyConfig),
|
||||
categoryIds = it.categories?.toSet(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.download.getImageModel
|
||||
import org.fdroid.ui.utils.AsyncShimmerImage
|
||||
|
||||
@Composable
|
||||
fun RepoIcon(repo: Repository, modifier: Modifier = Modifier) {
|
||||
fun RepoIcon(repo: Repository, proxy: ProxyConfig?, modifier: Modifier = Modifier) {
|
||||
AsyncShimmerImage(
|
||||
model = repo.getIcon(LocaleListCompat.getDefault())?.getImageModel(repo),
|
||||
model = repo.getIcon(LocaleListCompat.getDefault())?.getImageModel(repo, proxy),
|
||||
contentDescription = null,
|
||||
error = painterResource(R.drawable.ic_repo_app_default),
|
||||
modifier = modifier,
|
||||
|
||||
@@ -66,7 +66,7 @@ class RepositoriesViewModel @Inject constructor(
|
||||
repos.update {
|
||||
repositories.mapNotNull {
|
||||
if (it.isArchiveRepo) null
|
||||
else RepositoryItem(it, localeList)
|
||||
else RepositoryItem(it, localeList, settingsManager.proxyConfig)
|
||||
}
|
||||
}
|
||||
repoSortingMap.update {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.fdroid.ui.repositories
|
||||
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.download.getImageModel
|
||||
|
||||
@@ -14,11 +15,11 @@ data class RepositoryItem(
|
||||
val weight: Int,
|
||||
val enabled: Boolean,
|
||||
) {
|
||||
constructor(repo: Repository, localeList: LocaleListCompat) : this(
|
||||
constructor(repo: Repository, localeList: LocaleListCompat, proxy: ProxyConfig?) : this(
|
||||
repoId = repo.repoId,
|
||||
address = repo.address,
|
||||
name = repo.getName(localeList) ?: "Unknown Repo",
|
||||
icon = repo.getIcon(localeList)?.getImageModel(repo),
|
||||
icon = repo.getIcon(localeList)?.getImageModel(repo, proxy),
|
||||
timestamp = repo.timestamp,
|
||||
lastUpdated = repo.lastUpdated,
|
||||
weight = repo.weight,
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.index.IndexUpdateResult
|
||||
import org.fdroid.repo.AddRepoError
|
||||
@@ -30,6 +31,7 @@ import org.fdroid.repo.RepoUpdateWorker
|
||||
@Composable
|
||||
fun AddRepo(
|
||||
state: AddRepoState,
|
||||
proxyConfig: ProxyConfig?,
|
||||
onFetchRepo: (String) -> Unit,
|
||||
onAddRepo: () -> Unit,
|
||||
onExistingRepo: (Long) -> Unit,
|
||||
@@ -73,6 +75,7 @@ fun AddRepo(
|
||||
} else {
|
||||
AddRepoPreviewScreen(
|
||||
state = state,
|
||||
proxyConfig = proxyConfig,
|
||||
onAddRepo = onAddRepo,
|
||||
onExistingRepo = onExistingRepo,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
|
||||
@@ -228,7 +228,7 @@ fun AddRepoIntroContent(onFetchRepo: (String) -> Unit, modifier: Modifier = Modi
|
||||
@Preview
|
||||
private fun Preview() {
|
||||
FDroidContent {
|
||||
AddRepo(None, {}, {}, {}, { _, _ -> }) {}
|
||||
AddRepo(None, null, {}, {}, {}, { _, _ -> }) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +236,6 @@ private fun Preview() {
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES, widthDp = 720, heightDp = 360)
|
||||
private fun PreviewNight() {
|
||||
FDroidContent {
|
||||
AddRepo(None, {}, {}, {}, { _, _ -> }) {}
|
||||
AddRepo(None, null, {}, {}, {}, { _, _ -> }) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.database.MinimalApp
|
||||
import org.fdroid.download.getImageModel
|
||||
@@ -34,6 +35,7 @@ import org.fdroid.ui.utils.getRepository
|
||||
@Composable
|
||||
fun AddRepoPreviewScreen(
|
||||
state: Fetching,
|
||||
proxyConfig: ProxyConfig?,
|
||||
modifier: Modifier = Modifier,
|
||||
onAddRepo: () -> Unit,
|
||||
onExistingRepo: (Long) -> Unit,
|
||||
@@ -48,6 +50,7 @@ fun AddRepoPreviewScreen(
|
||||
item {
|
||||
RepoPreviewHeader(
|
||||
state = state,
|
||||
proxyConfig = proxyConfig,
|
||||
onAddRepo = onAddRepo,
|
||||
onExistingRepo = onExistingRepo,
|
||||
modifier = Modifier
|
||||
@@ -88,7 +91,7 @@ fun AddRepoPreviewScreen(
|
||||
packageName = app.packageName,
|
||||
name = app.name ?: "Unknown app",
|
||||
summary = app.summary ?: "",
|
||||
iconModel = app.getIcon(localeList)?.getImageModel(repo),
|
||||
iconModel = app.getIcon(localeList)?.getImageModel(repo, proxyConfig),
|
||||
lastUpdated = 1L,
|
||||
isCompatible = true,
|
||||
)
|
||||
@@ -137,6 +140,7 @@ private fun Preview() {
|
||||
FDroidContent(pureBlack = true) {
|
||||
AddRepoPreviewScreen(
|
||||
Fetching(address, repo, listOf(app1, app2, app3), IsNewRepository),
|
||||
proxyConfig = null,
|
||||
onAddRepo = { },
|
||||
onExistingRepo = {},
|
||||
)
|
||||
|
||||
@@ -8,17 +8,21 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import mu.KotlinLogging
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.repo.AddRepoState
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AddRepoViewModel @Inject constructor(
|
||||
app: Application,
|
||||
private val repoManager: RepoManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
) : AndroidViewModel(app) {
|
||||
|
||||
private val log = KotlinLogging.logger { }
|
||||
val state: StateFlow<AddRepoState> = repoManager.addRepoState
|
||||
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
|
||||
override fun onCleared() {
|
||||
log.info { "onCleared() abort adding repository" }
|
||||
repoManager.abortAddingRepository()
|
||||
@@ -30,8 +34,7 @@ class AddRepoViewModel @Inject constructor(
|
||||
// TODO full only
|
||||
} else {
|
||||
repoManager.abortAddingRepository()
|
||||
// TODO support proxy
|
||||
repoManager.fetchRepositoryPreview(uri.toString(), proxy = null)
|
||||
repoManager.fetchRepositoryPreview(uri.toString(), settingsManager.proxyConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
import org.fdroid.repo.FetchResult.IsExistingMirror
|
||||
@@ -40,6 +41,7 @@ import org.fdroid.ui.utils.getRepository
|
||||
@Composable
|
||||
fun RepoPreviewHeader(
|
||||
state: Fetching,
|
||||
proxyConfig: ProxyConfig?,
|
||||
onAddRepo: () -> Unit,
|
||||
onExistingRepo: (Long) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -86,7 +88,7 @@ fun RepoPreviewHeader(
|
||||
horizontalArrangement = spacedBy(16.dp),
|
||||
verticalAlignment = CenterVertically,
|
||||
) {
|
||||
RepoIcon(repo, Modifier.size(48.dp))
|
||||
RepoIcon(repo, proxyConfig, Modifier.size(48.dp))
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(
|
||||
text = repo.getName(localeList) ?: "Unknown Repository",
|
||||
@@ -151,6 +153,7 @@ fun RepoPreviewScreenNewMirrorPreview() {
|
||||
onAddRepo = { },
|
||||
onExistingRepo = {},
|
||||
localeList = LocaleListCompat.getDefault(),
|
||||
proxyConfig = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -170,6 +173,7 @@ fun RepoPreviewScreenNewRepoAndNewMirrorPreview() {
|
||||
onAddRepo = { },
|
||||
onExistingRepo = {},
|
||||
localeList = LocaleListCompat.getDefault(),
|
||||
proxyConfig = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -185,6 +189,7 @@ fun RepoPreviewScreenExistingRepoPreview() {
|
||||
onAddRepo = { },
|
||||
onExistingRepo = {},
|
||||
localeList = LocaleListCompat.getDefault(),
|
||||
proxyConfig = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -199,6 +204,7 @@ fun RepoPreviewScreenExistingMirrorPreview() {
|
||||
onAddRepo = { },
|
||||
onExistingRepo = {},
|
||||
localeList = LocaleListCompat.getDefault(),
|
||||
proxyConfig = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,12 @@ fun RepoDetailsContent(
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
RepoDetailsHeader(repo, info.model.numberApps, onShowAppsClicked)
|
||||
RepoDetailsHeader(
|
||||
repo = repo,
|
||||
numberOfApps = info.model.numberApps,
|
||||
proxy = info.model.proxy,
|
||||
onShowAppsClicked = onShowAppsClicked,
|
||||
)
|
||||
if (info.model.showOfficialMirrors) {
|
||||
OfficialMirrors(
|
||||
mirrors = info.model.officialMirrors,
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
@@ -33,6 +34,7 @@ import org.fdroid.ui.utils.getRepository
|
||||
fun RepoDetailsHeader(
|
||||
repo: Repository,
|
||||
numberOfApps: Int?,
|
||||
proxy: ProxyConfig?,
|
||||
onShowAppsClicked: (String, Long) -> Unit,
|
||||
) {
|
||||
val localeList = LocaleListCompat.getDefault()
|
||||
@@ -59,7 +61,7 @@ fun RepoDetailsHeader(
|
||||
Row(
|
||||
horizontalArrangement = spacedBy(8.dp),
|
||||
) {
|
||||
RepoIcon(repo, Modifier.size(64.dp))
|
||||
RepoIcon(repo, proxy, Modifier.size(64.dp))
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(
|
||||
text = name,
|
||||
@@ -110,6 +112,6 @@ fun RepoDetailsHeader(
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
FDroidContent {
|
||||
RepoDetailsHeader(getRepository(), 45) { _, _ -> }
|
||||
RepoDetailsHeader(getRepository(), 45, null) { _, _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
||||
import android.content.Intent.ACTION_SEND
|
||||
import android.content.Intent.EXTRA_TEXT
|
||||
import android.graphics.Bitmap
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import org.fdroid.R
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.download.Mirror
|
||||
@@ -39,6 +40,7 @@ data class RepoDetailsModel(
|
||||
val userMirrors: List<UserMirrorItem>,
|
||||
val archiveState: ArchiveState,
|
||||
val showOnboarding: Boolean,
|
||||
val proxy: ProxyConfig?,
|
||||
) {
|
||||
/**
|
||||
* The repo's address is currently also an official mirror.
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.fdroid.ui.repositories.details
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import io.ktor.client.engine.ProxyConfig
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.fdroid.database.Repository
|
||||
@@ -12,6 +13,7 @@ fun RepoDetailsPresenter(
|
||||
numAppsFlow: Flow<Int?>,
|
||||
archiveStateFlow: StateFlow<ArchiveState>,
|
||||
showOnboardingFlow: StateFlow<Boolean>,
|
||||
proxyConfig: ProxyConfig?,
|
||||
): RepoDetailsModel {
|
||||
val repo = repoFlow.collectAsState(null).value
|
||||
return RepoDetailsModel(
|
||||
@@ -30,5 +32,6 @@ fun RepoDetailsPresenter(
|
||||
} ?: emptyList(),
|
||||
archiveState = archiveStateFlow.collectAsState().value,
|
||||
showOnboarding = showOnboardingFlow.collectAsState().value,
|
||||
proxy = proxyConfig,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.fdroid.download.Mirror
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.repo.RepoUpdateWorker
|
||||
import org.fdroid.settings.OnboardingManager
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.ui.repositories.details.ArchiveState.UNKNOWN
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
import javax.inject.Inject
|
||||
@@ -36,6 +37,7 @@ class RepoDetailsViewModel @Inject constructor(
|
||||
app: Application,
|
||||
private val db: FDroidDatabase,
|
||||
private val repoManager: RepoManager,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val onboardingManager: OnboardingManager,
|
||||
@param:IoDispatcher private val ioScope: CoroutineScope,
|
||||
) : AndroidViewModel(app), RepoDetailsActions {
|
||||
@@ -60,6 +62,7 @@ class RepoDetailsViewModel @Inject constructor(
|
||||
numAppsFlow = numAppsFlow,
|
||||
archiveStateFlow = archiveStateFlow,
|
||||
showOnboardingFlow = showOnboarding,
|
||||
proxyConfig = settingsManager.proxyConfig,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
128
next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt
Normal file
128
next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt
Normal file
@@ -0,0 +1,128 @@
|
||||
package org.fdroid.ui.settings
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.VpnLock
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import me.zhanghai.compose.preference.ProvidePreferenceLocals
|
||||
import me.zhanghai.compose.preference.textFieldPreference
|
||||
import org.fdroid.R
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_PROXY
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
fun LazyListScope.preferenceProxy(
|
||||
proxyState: MutableState<String>,
|
||||
showError: MutableState<Boolean>,
|
||||
) {
|
||||
textFieldPreference(
|
||||
key = PREF_KEY_PROXY,
|
||||
defaultValue = PREF_DEFAULT_PROXY,
|
||||
rememberState = { proxyState },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VpnLock,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(stringResource(R.string.pref_proxy_title))
|
||||
},
|
||||
summary = {
|
||||
val value = proxyState.value
|
||||
val s = if (value.isBlank()) {
|
||||
stringResource(R.string.pref_proxy_disabled)
|
||||
} else {
|
||||
stringResource(R.string.pref_proxy_enabled, value)
|
||||
}
|
||||
Text(s)
|
||||
},
|
||||
textToValue = {
|
||||
if (it.isBlank() || isProxyValid(it)) {
|
||||
showError.value = false
|
||||
it
|
||||
} else {
|
||||
showError.value = true
|
||||
// null is currently treated as an error and won't cause an update
|
||||
null
|
||||
}
|
||||
},
|
||||
textField = { value, onValueChange, onOk ->
|
||||
OutlinedTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardActions = KeyboardActions { onOk() },
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
if (value.text.isNotBlank()) {
|
||||
IconButton(onClick = { onValueChange(TextFieldValue("")) }) {
|
||||
Icon(Icons.Default.Clear, stringResource(R.string.clear))
|
||||
}
|
||||
}
|
||||
},
|
||||
isError = showError.value,
|
||||
supportingText = {
|
||||
val s = if (showError.value) {
|
||||
stringResource(R.string.pref_proxy_error)
|
||||
} else {
|
||||
stringResource(R.string.pref_proxy_hint)
|
||||
}
|
||||
Text(s)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun isProxyValid(proxyStr: String): Boolean = try {
|
||||
val (host, port) = proxyStr.split(':')
|
||||
InetSocketAddress.createUnresolved(host, port.toInt())
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewDefault() {
|
||||
FDroidContent {
|
||||
ProvidePreferenceLocals {
|
||||
val showProxyError = remember { mutableStateOf(false) }
|
||||
val proxyState = remember { mutableStateOf(PREF_DEFAULT_PROXY) }
|
||||
LazyColumn {
|
||||
preferenceProxy(proxyState, showProxyError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewProxySet() {
|
||||
FDroidContent {
|
||||
ProvidePreferenceLocals {
|
||||
val showProxyError = remember { mutableStateOf(false) }
|
||||
val proxyState = remember { mutableStateOf("127.0.0.1:8000") }
|
||||
LazyColumn {
|
||||
preferenceProxy(proxyState, showProxyError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
@@ -45,12 +47,15 @@ import me.zhanghai.compose.preference.ProvidePreferenceLocals
|
||||
import me.zhanghai.compose.preference.listPreference
|
||||
import me.zhanghai.compose.preference.preference
|
||||
import me.zhanghai.compose.preference.preferenceCategory
|
||||
import me.zhanghai.compose.preference.rememberPreferenceState
|
||||
import me.zhanghai.compose.preference.switchPreference
|
||||
import org.fdroid.R
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_DEFAULT_THEME
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_AUTO_UPDATES
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_PROXY
|
||||
import org.fdroid.settings.SettingsConstants.PREF_KEY_THEME
|
||||
import org.fdroid.ui.utils.asRelativeTimeString
|
||||
import org.fdroid.utils.getLogName
|
||||
@@ -87,6 +92,8 @@ fun Settings(
|
||||
val context = LocalContext.current
|
||||
val res = LocalResources.current
|
||||
ProvidePreferenceLocals(prefsFlow) {
|
||||
val showProxyError = remember { mutableStateOf(false) }
|
||||
val proxyState = rememberPreferenceState(PREF_KEY_PROXY, PREF_DEFAULT_PROXY)
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
@@ -196,6 +203,11 @@ fun Settings(
|
||||
Text(s)
|
||||
},
|
||||
)
|
||||
preferenceCategory(
|
||||
key = "pref_category_network",
|
||||
title = { Text(stringResource(R.string.pref_category_network)) },
|
||||
)
|
||||
preferenceProxy(proxyState, showProxyError)
|
||||
item {
|
||||
OutlinedButton(
|
||||
onClick = { launcher.launch("${getLogName(context)}.txt") },
|
||||
|
||||
@@ -214,6 +214,7 @@ val testApp = AppDetailsItem(
|
||||
installedVersion = testVersion2,
|
||||
suggestedVersion = null,
|
||||
possibleUpdate = testVersion1,
|
||||
proxy = null,
|
||||
)
|
||||
|
||||
fun getPreviewVersion(versionName: String, size: Long? = null) = object : PackageVersion {
|
||||
@@ -302,6 +303,7 @@ fun getRepoDetailsInfo(
|
||||
),
|
||||
archiveState = ArchiveState.LOADING,
|
||||
showOnboarding = false,
|
||||
proxy = null,
|
||||
),
|
||||
) = object : RepoDetailsInfo {
|
||||
override val model = model
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.fdroid.database.FDroidDatabase
|
||||
import org.fdroid.download.getImageModel
|
||||
import org.fdroid.index.RepoManager
|
||||
import org.fdroid.install.AppInstallManager
|
||||
import org.fdroid.settings.SettingsManager
|
||||
import org.fdroid.ui.apps.AppUpdateItem
|
||||
import org.fdroid.utils.IoDispatcher
|
||||
import javax.inject.Inject
|
||||
@@ -30,6 +31,7 @@ class UpdatesManager @Inject constructor(
|
||||
@ApplicationContext context: Context,
|
||||
private val db: FDroidDatabase,
|
||||
private val dbUpdateChecker: DbUpdateChecker,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val repoManager: RepoManager,
|
||||
private val appInstallManager: AppInstallManager,
|
||||
@param:IoDispatcher private val coroutineScope: CoroutineScope,
|
||||
@@ -70,6 +72,7 @@ class UpdatesManager @Inject constructor(
|
||||
val localeList = LocaleListCompat.getDefault()
|
||||
val updates = try {
|
||||
log.info { "Checking for updates..." }
|
||||
val proxyConfig = settingsManager.proxyConfig
|
||||
dbUpdateChecker.getUpdatableApps(onlyFromPreferredRepo = true).map { update ->
|
||||
AppUpdateItem(
|
||||
repoId = update.repoId,
|
||||
@@ -79,7 +82,7 @@ class UpdatesManager @Inject constructor(
|
||||
update = update.update,
|
||||
whatsNew = update.update.getWhatsNew(localeList),
|
||||
iconModel = repoManager.getRepository(update.repoId)?.let { repo ->
|
||||
update.getIcon(localeList)?.getImageModel(repo)
|
||||
update.getIcon(localeList)?.getImageModel(repo, proxyConfig)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
<string name="onboarding_app_list_filter_title">Filter</string>
|
||||
<string name="onboarding_app_list_filter_message">Here you can apply filters to the list of apps, e.g. showing only apps within a certain category or repository. Changing the sort order is also possible.</string>
|
||||
<string name="got_it">Got it</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="permission_camera_denied">Scanning the QR code can only work if you grant camera permission. Tap to grant it in settings.</string>
|
||||
<string name="repo_last_update_upstream">Last update: %s</string>
|
||||
<string name="repo_basicauth_username">Username</string>
|
||||
@@ -102,6 +103,12 @@
|
||||
|
||||
<string name="pref_language_summary">Opens system language settings</string>
|
||||
<string name="pref_auto_updates_summary">Download and update apps daily when the device isn\'t being used</string>
|
||||
<string name="pref_category_network">Network</string>
|
||||
<string name="pref_proxy_title">Connect via SOCKS proxy</string>
|
||||
<string name="pref_proxy_disabled">Connect to the internet without proxy</string>
|
||||
<string name="pref_proxy_enabled">Uses proxy %s to connect</string>
|
||||
<string name="pref_proxy_hint">Proxy is expected in host:port format.</string>
|
||||
<string name="pref_proxy_error">Proxy format invalid</string>
|
||||
|
||||
<string name="crash_report_text">An unexpected error occurred.
|
||||
This is not your fault.
|
||||
|
||||
Reference in New Issue
Block a user