Add support for proxies - 2/3

This commit is contained in:
Rahul Patel
2023-10-22 14:27:13 +05:30
parent 687c14c394
commit f19631ad6a
36 changed files with 226 additions and 44 deletions

View File

@@ -43,6 +43,7 @@ object Constants {
const val URL_DISPENSER = "https://auroraoss.com/api/auth" const val URL_DISPENSER = "https://auroraoss.com/api/auth"
const val PLAY_QUERY_URL = "https://play.google.com/store/search?q=" const val PLAY_QUERY_URL = "https://play.google.com/store/search?q="
const val ANDROID_CONNECTIVITY_URL = "http://connectivitycheck.android.com/generate_204"
//ACCOUNTS //ACCOUNTS
const val ACCOUNT_SIGNED_IN = "ACCOUNT_SIGNED_IN" const val ACCOUNT_SIGNED_IN = "ACCOUNT_SIGNED_IN"

View File

@@ -23,6 +23,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
fun runAsync(action: () -> Unit) = Thread(Runnable(action)).start() fun runAsync(action: () -> Unit) = Thread(Runnable(action)).start()
fun runAsyncResult(action: () -> Boolean) = Thread { action() }.apply { start() }
fun runOnUiThread(action: () -> Unit) { fun runOnUiThread(action: () -> Unit) {
when { when {

View File

@@ -0,0 +1,9 @@
package com.aurora.store.data.model
data class ProxyInfo(
var protocol: String,
var host: String,
var port: Int,
var proxyUser: String?,
var proxyPassword: String?
)

View File

@@ -19,11 +19,32 @@
package com.aurora.store.data.network package com.aurora.store.data.network
import com.aurora.gplayapi.network.IHttpClient import android.content.Context
import com.aurora.store.data.model.ProxyInfo
import com.aurora.store.util.Log
import com.aurora.store.util.Preferences
import com.google.gson.Gson
object HttpClient { object HttpClient {
fun getPreferredClient(context: Context): IProxyHttpClient {
val proxyEnabled = Preferences.getBoolean(
context,
Preferences.PREFERENCE_PROXY_ENABLED
)
fun getPreferredClient(): IHttpClient { return if (proxyEnabled) {
return OkHttpClient val proxyInfoString = Preferences.getString(context, Preferences.PREFERENCE_PROXY_INFO)
val proxyInfo = Gson().fromJson(proxyInfoString, ProxyInfo::class.java)
if (proxyInfo != null) {
OkHttpClient.setProxy(proxyInfo)
} else {
Log.e("Proxy info is unavailable, using default client")
OkHttpClient
}
} else {
Log.i("Proxy is disabled")
OkHttpClient
}
} }
} }

View File

@@ -1,9 +1,10 @@
package com.aurora.store.data.network package com.aurora.store.data.network
import com.aurora.gplayapi.network.IHttpClient import com.aurora.gplayapi.network.IHttpClient
import com.aurora.store.data.model.ProxyInfo
import java.net.Proxy import java.net.Proxy
interface IProxyHttpClient : IHttpClient { interface IProxyHttpClient : IHttpClient {
@Throws(UnsupportedOperationException::class) @Throws(UnsupportedOperationException::class)
fun setProxy(proxy: Proxy, proxyUser: String?, proxyPassword: String?): IHttpClient fun setProxy(proxyInfo: ProxyInfo): IHttpClient
} }

View File

@@ -21,6 +21,7 @@ package com.aurora.store.data.network
import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.store.BuildConfig import com.aurora.store.BuildConfig
import com.aurora.store.data.model.ProxyInfo
import com.aurora.store.util.Log import com.aurora.store.util.Log
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -31,6 +32,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException import java.io.IOException
import java.net.InetSocketAddress
import java.net.Proxy import java.net.Proxy
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -52,13 +54,29 @@ object OkHttpClient : IProxyHttpClient {
.followRedirects(true) .followRedirects(true)
.followSslRedirects(true) .followSslRedirects(true)
override fun setProxy( override fun setProxy(proxyInfo: ProxyInfo): OkHttpClient {
proxy: Proxy, proxyUser: String?, proxyPassword: String? val proxy = Proxy(
): IProxyHttpClient { if (proxyInfo.protocol == "socks") Proxy.Type.SOCKS else Proxy.Type.HTTP,
if (proxyUser != null) { InetSocketAddress.createUnresolved(
okHttpClientBuilder.proxyAuthenticator { route, response -> proxyInfo.host,
val credential = Credentials.basic(proxyUser, proxyPassword.orEmpty()) proxyInfo.port
response.request.newBuilder().header("Proxy-Authorization", credential).build() )
)
val proxyUser = proxyInfo.proxyUser
val proxyPassword = proxyInfo.proxyPassword
if (proxyUser != null && proxyPassword != null) {
okHttpClientBuilder.proxyAuthenticator { _, response ->
if (response.request.header("Proxy-Authorization") != null) {
return@proxyAuthenticator null
}
val credential = Credentials.basic(proxyUser, proxyPassword)
response.request
.newBuilder()
.header("Proxy-Authorization", credential)
.build()
} }
} }

View File

@@ -220,7 +220,7 @@ class UpdateService: LifecycleService() {
} }
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
authData = AuthProvider.with(this).getAuthData() authData = AuthProvider.with(this).getAuthData()
purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient()) purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient(this))
downloadManager = DownloadManager.with(this) downloadManager = DownloadManager.with(this)
fetch = downloadManager.fetch fetch = downloadManager.fetch
fetchListener = object : FetchGroupListener { fetchListener = object : FetchGroupListener {

View File

@@ -104,7 +104,7 @@ class UpdateWorker(private val appContext: Context, workerParams: WorkerParamete
Log.i("Checking for app updates") Log.i("Checking for app updates")
val appDetailsHelper = AppDetailsHelper(authData) val appDetailsHelper = AppDetailsHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(appContext))
val isGoogleFilterEnabled = Preferences.getBoolean( val isGoogleFilterEnabled = Preferences.getBoolean(
appContext, appContext,
@@ -153,7 +153,7 @@ class UpdateWorker(private val appContext: Context, workerParams: WorkerParamete
private fun isValid(authData: AuthData): Boolean { private fun isValid(authData: AuthData): Boolean {
return try { return try {
AuthValidator(authData) AuthValidator(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(appContext))
.isValid() .isValid()
} catch (e: Exception) { } catch (e: Exception) {
false false

View File

@@ -22,6 +22,7 @@ package com.aurora.store.util
import android.content.Context import android.content.Context
import com.aurora.extensions.isSAndAbove import com.aurora.extensions.isSAndAbove
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.model.ProxyInfo
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.Locale import java.util.Locale
import kotlin.math.ln import kotlin.math.ln
@@ -82,9 +83,11 @@ object CommonUtil {
hours > 0 -> { hours > 0 -> {
context.getString(R.string.download_eta_hrs, hours, minutes, seconds) context.getString(R.string.download_eta_hrs, hours, minutes, seconds)
} }
minutes > 0 -> { minutes > 0 -> {
context.getString(R.string.download_eta_min, minutes, seconds) context.getString(R.string.download_eta_min, minutes, seconds)
} }
else -> { else -> {
context.getString(R.string.download_eta_sec, seconds) context.getString(R.string.download_eta_sec, seconds)
} }
@@ -126,9 +129,11 @@ object CommonUtil {
mb >= 1 -> { mb >= 1 -> {
context.getString(R.string.download_speed_mb, decimalFormat.format(mb)) context.getString(R.string.download_speed_mb, decimalFormat.format(mb))
} }
kb >= 1 -> { kb >= 1 -> {
context.getString(R.string.download_speed_kb, decimalFormat.format(kb)) context.getString(R.string.download_speed_kb, decimalFormat.format(kb))
} }
else -> { else -> {
context.getString(R.string.download_speed_bytes, downloadedBytesPerSecond) context.getString(R.string.download_speed_bytes, downloadedBytesPerSecond)
} }
@@ -179,4 +184,29 @@ object CommonUtil {
else -> if (isSAndAbove()) R.style.Accent00 else R.style.Accent01 else -> if (isSAndAbove()) R.style.Accent00 else R.style.Accent01
} }
} }
fun parseProxyUrl(proxyUrl: String): ProxyInfo? {
val pattern = """^(https?|socks)://(?:([^\s:@]+):([^\s:@]+)@)?([^\s:@]+):(\d+)$""".toRegex()
val match = pattern.find(proxyUrl)
return when {
match != null -> {
val protocol = match.groupValues[1].toUpperCase()
val username = match.groupValues[2]
val password = match.groupValues[3]
val url = match.groupValues[4]
val port = match.groupValues[5]
ProxyInfo(
protocol,
url,
port.toInt(),
username,
password
)
}
else -> null
}
}
} }

View File

@@ -56,6 +56,9 @@ object Preferences {
const val PREFERENCE_TOS_READ = "PREFERENCE_TOS_READ" const val PREFERENCE_TOS_READ = "PREFERENCE_TOS_READ"
const val PREFERENCE_INSECURE_ANONYMOUS = "PREFERENCE_INSECURE_ANONYMOUS" const val PREFERENCE_INSECURE_ANONYMOUS = "PREFERENCE_INSECURE_ANONYMOUS"
const val PREFERENCE_PROXY_URL = "PREFERENCE_PROXY_URL"
const val PREFERENCE_PROXY_INFO = "PREFERENCE_PROXY_INFO"
const val PREFERENCE_PROXY_ENABLED = "PREFERENCE_PROXY_ENABLED"
const val PREFERENCE_UPDATES_EXTENDED = "PREFERENCE_UPDATES_EXTENDED" const val PREFERENCE_UPDATES_EXTENDED = "PREFERENCE_UPDATES_EXTENDED"
const val PREFERENCE_UPDATES_CHECK = "PREFERENCE_UPDATES_CHECK" const val PREFERENCE_UPDATES_CHECK = "PREFERENCE_UPDATES_CHECK"

View File

@@ -1081,7 +1081,7 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
} }
private fun inflateAppPrivacy(app: App) { private fun inflateAppPrivacy(app: App) {
viewModel.fetchAppReport(app.packageName) viewModel.fetchAppReport(requireContext(), app.packageName)
} }
private fun inflateAppDevInfo(B: LayoutDetailsDevBinding, app: App) { private fun inflateAppDevInfo(B: LayoutDetailsDevBinding, app: App) {

View File

@@ -48,6 +48,7 @@ import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSECURE_ANONYMOUS import com.aurora.store.util.Preferences.PREFERENCE_INSECURE_ANONYMOUS
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_INTRO import com.aurora.store.util.Preferences.PREFERENCE_INTRO
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_ENABLED
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
@@ -165,6 +166,7 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
/*Network*/ /*Network*/
save(PREFERENCE_INSECURE_ANONYMOUS, false) save(PREFERENCE_INSECURE_ANONYMOUS, false)
save(PREFERENCE_PROXY_ENABLED, false)
/*Customization*/ /*Customization*/
save(PREFERENCE_THEME_TYPE, 0) save(PREFERENCE_THEME_TYPE, 0)

View File

@@ -25,16 +25,82 @@ import androidx.appcompat.widget.Toolbar
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.aurora.Constants
import com.aurora.extensions.runAsync
import com.aurora.extensions.runOnUiThread import com.aurora.extensions.runOnUiThread
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.network.HttpClient
import com.aurora.store.util.CommonUtil
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.save
import com.google.gson.Gson
class NetworkPreference : PreferenceFragmentCompat() { class NetworkPreference : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_network, rootKey) setPreferencesFromResource(R.xml.preferences_network, rootKey)
val proxyUrl = Preferences.getString(requireContext(), Preferences.PREFERENCE_PROXY_URL, "")
val proxyInfo =
Preferences.getString(requireContext(), Preferences.PREFERENCE_PROXY_INFO, "{}")
val preferenceProxyUrl: Preference? = findPreference(Preferences.PREFERENCE_PROXY_URL)
preferenceProxyUrl?.summary = proxyUrl
preferenceProxyUrl?.let {
it.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { preference, newValue ->
val newProxyUrl = newValue.toString()
val newProxyInfo = CommonUtil.parseProxyUrl(newProxyUrl)
if (newProxyInfo == null) {
runOnUiThread {
requireContext().toast(getString(R.string.toast_proxy_invalid))
}
false
} else {
runAsync {
kotlin.runCatching {
val result = HttpClient
.getPreferredClient(requireContext())
.setProxy(newProxyInfo)
.get(
Constants.ANDROID_CONNECTIVITY_URL,
mapOf()
)
runOnUiThread {
if (result.code == 204) {
requireContext().toast(getString(R.string.toast_proxy_success))
save(Preferences.PREFERENCE_PROXY_URL, newProxyUrl)
save(
Preferences.PREFERENCE_PROXY_INFO,
Gson().toJson(newProxyInfo)
)
preference.summary = newProxyUrl
} else {
throw Exception("Failed to set proxy")
}
}
}.onFailure {
runOnUiThread {
requireContext().toast(getText(R.string.toast_proxy_failed))
save(Preferences.PREFERENCE_PROXY_URL, proxyUrl)
save(
Preferences.PREFERENCE_PROXY_INFO,
proxyInfo
)
}
}
}
true
}
}
}
val insecureAnonymous: Preference? = val insecureAnonymous: Preference? =
findPreference(Preferences.PREFERENCE_INSECURE_ANONYMOUS) findPreference(Preferences.PREFERENCE_INSECURE_ANONYMOUS)
@@ -56,4 +122,4 @@ class NetworkPreference : PreferenceFragmentCompat() {
setNavigationOnClickListener { findNavController().navigateUp() } setNavigationOnClickListener { findNavController().navigateUp() }
} }
} }
} }

View File

@@ -32,7 +32,7 @@ import java.lang.reflect.Modifier
abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel(application) { abstract class BaseAndroidViewModel(application: Application) : AndroidViewModel(application) {
val responseCode = HttpClient.getPreferredClient().responseCode val responseCode = HttpClient.getPreferredClient(application).responseCode
protected val gson: Gson = GsonBuilder() protected val gson: Gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC) .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)

View File

@@ -29,7 +29,7 @@ class MainViewModel : ViewModel() {
try { try {
val gson: Gson = val gson: Gson =
GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create() GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
val response = HttpClient.getPreferredClient().get(Constants.UPDATE_URL, mapOf()) val response = HttpClient.getPreferredClient(context).get(Constants.UPDATE_URL, mapOf())
val selfUpdate = val selfUpdate =
gson.fromJson(String(response.responseBytes), SelfUpdate::class.java) gson.fromJson(String(response.responseBytes), SelfUpdate::class.java)

View File

@@ -37,7 +37,7 @@ abstract class BaseAppsViewModel(application: Application) : BaseAndroidViewMode
.getAuthData() .getAuthData()
private val appDetailsHelper = AppDetailsHelper(authData) private val appDetailsHelper = AppDetailsHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
var blacklistProvider = BlacklistProvider var blacklistProvider = BlacklistProvider
.with(application) .with(application)

View File

@@ -37,7 +37,7 @@ class LibraryAppsViewModel(application: Application) : BaseAndroidViewModel(appl
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val clusterHelper: ClusterHelper = private val clusterHelper: ClusterHelper =
ClusterHelper(authData).using(HttpClient.getPreferredClient()) ClusterHelper(authData).using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<StreamCluster> = MutableLiveData() val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
var streamCluster: StreamCluster = StreamCluster() var streamCluster: StreamCluster = StreamCluster()

View File

@@ -41,7 +41,7 @@ class PurchasedViewModel(application: Application) : BaseAndroidViewModel(applic
private val authData = AuthProvider.with(application).getAuthData() private val authData = AuthProvider.with(application).getAuthData()
private val purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient()) private val purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient(application))
private var appList: MutableList<App> = mutableListOf() private var appList: MutableList<App> = mutableListOf()

View File

@@ -121,7 +121,7 @@ class AuthViewModel(application: Application) : BaseAndroidViewModel(application
properties = spoofProvider.getSpoofDeviceProperties() properties = spoofProvider.getSpoofDeviceProperties()
val playResponse = HttpClient val playResponse = HttpClient
.getPreferredClient() .getPreferredClient(getApplication())
.postAuth( .postAuth(
Constants.URL_DISPENSER, Constants.URL_DISPENSER,
gson.toJson(properties).toByteArray() gson.toJson(properties).toByteArray()
@@ -157,7 +157,7 @@ class AuthViewModel(application: Application) : BaseAndroidViewModel(application
properties = spoofProvider.getSpoofDeviceProperties() properties = spoofProvider.getSpoofDeviceProperties()
val playResponse = HttpClient val playResponse = HttpClient
.getPreferredClient() .getPreferredClient(getApplication())
.getAuth( .getAuth(
Constants.URL_DISPENSER Constants.URL_DISPENSER
) )
@@ -275,7 +275,7 @@ class AuthViewModel(application: Application) : BaseAndroidViewModel(application
private fun isValid(authData: AuthData): Boolean { private fun isValid(authData: AuthData): Boolean {
return try { return try {
AuthValidator(authData) AuthValidator(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(getApplication()))
.isValid() .isValid()
} catch (e: Exception) { } catch (e: Exception) {
false false

View File

@@ -38,7 +38,7 @@ class ExpandedStreamBrowseViewModel(application: Application) : BaseAndroidViewM
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val streamHelper: ExpandedBrowseHelper = ExpandedBrowseHelper(authData) private val streamHelper: ExpandedBrowseHelper = ExpandedBrowseHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<StreamCluster> = MutableLiveData() val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
var streamCluster: StreamCluster = StreamCluster() var streamCluster: StreamCluster = StreamCluster()

View File

@@ -38,7 +38,7 @@ class StreamBrowseViewModel(application: Application) : BaseAndroidViewModel(app
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val streamHelper: StreamHelper = StreamHelper(authData) private val streamHelper: StreamHelper = StreamHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<StreamCluster> = MutableLiveData() val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
var streamCluster: StreamCluster = StreamCluster() var streamCluster: StreamCluster = StreamCluster()

View File

@@ -39,7 +39,7 @@ abstract class BaseCategoryViewModel(application: Application) : BaseAndroidView
.getAuthData() .getAuthData()
private val streamHelper: CategoryHelper = CategoryHelper(authData) private val streamHelper: CategoryHelper = CategoryHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<List<Category>> = MutableLiveData() val liveData: MutableLiveData<List<Category>> = MutableLiveData()

View File

@@ -49,7 +49,7 @@ class AppDetailsViewModel : ViewModel() {
try { try {
val authData = AuthProvider.with(context).getAuthData() val authData = AuthProvider.with(context).getAuthData()
_app.emit( _app.emit(
AppDetailsHelper(authData).using(HttpClient.getPreferredClient()) AppDetailsHelper(authData).using(HttpClient.getPreferredClient(context))
.getAppByPackageName(packageName) .getAppByPackageName(packageName)
) )
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -63,7 +63,7 @@ class AppDetailsViewModel : ViewModel() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val authData = AuthProvider.with(context).getAuthData() val authData = AuthProvider.with(context).getAuthData()
_reviews.emit(ReviewsHelper(authData).using(HttpClient.getPreferredClient()) _reviews.emit(ReviewsHelper(authData).using(HttpClient.getPreferredClient(context))
.getReviewSummary(packageName)) .getReviewSummary(packageName))
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed to fetch app reviews", exception) Log.e(TAG, "Failed to fetch app reviews", exception)
@@ -77,7 +77,7 @@ class AppDetailsViewModel : ViewModel() {
try { try {
val authData = AuthProvider.with(context).getAuthData() val authData = AuthProvider.with(context).getAuthData()
_userReview.emit(ReviewsHelper(authData) _userReview.emit(ReviewsHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(context))
.addOrEditReview( .addOrEditReview(
packageName, packageName,
review.title, review.title,
@@ -93,7 +93,7 @@ class AppDetailsViewModel : ViewModel() {
} }
fun fetchAppReport(packageName: String) { fun fetchAppReport(context: Context,packageName: String) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val headers: MutableMap<String, String> = mutableMapOf() val headers: MutableMap<String, String> = mutableMapOf()
@@ -102,7 +102,7 @@ class AppDetailsViewModel : ViewModel() {
headers["Authorization"] = exodusApiKey headers["Authorization"] = exodusApiKey
val url = exodusBaseUrl + packageName val url = exodusBaseUrl + packageName
val playResponse = HttpClient.getPreferredClient().get(url, headers) val playResponse = HttpClient.getPreferredClient(context).get(url, headers)
_report.emit(parseResponse(String(playResponse.responseBytes), packageName)[0]) _report.emit(parseResponse(String(playResponse.responseBytes), packageName)[0])
} catch (exception: Exception) { } catch (exception: Exception) {

View File

@@ -40,7 +40,7 @@ import kotlinx.coroutines.supervisorScope
class DetailsClusterViewModel(application: Application) : BaseAndroidViewModel(application) { class DetailsClusterViewModel(application: Application) : BaseAndroidViewModel(application) {
private var authData: AuthData = AuthProvider.with(application).getAuthData() private var authData: AuthData = AuthProvider.with(application).getAuthData()
private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient()) private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient(application))
private var streamHelper = StreamHelper(authData) private var streamHelper = StreamHelper(authData)
val liveData: MutableLiveData<ViewState> = MutableLiveData() val liveData: MutableLiveData<ViewState> = MutableLiveData()

View File

@@ -41,7 +41,7 @@ import kotlinx.coroutines.supervisorScope
class DevProfileViewModel(application: Application) : BaseAndroidViewModel(application) { class DevProfileViewModel(application: Application) : BaseAndroidViewModel(application) {
private var authData: AuthData = AuthProvider.with(application).getAuthData() private var authData: AuthData = AuthProvider.with(application).getAuthData()
private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient()) private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient(application))
private var streamHelper = StreamHelper(authData) private var streamHelper = StreamHelper(authData)
val liveData: MutableLiveData<ViewState> = MutableLiveData() val liveData: MutableLiveData<ViewState> = MutableLiveData()

View File

@@ -40,7 +40,7 @@ open class BaseEditorChoiceViewModel(application: Application) : BaseAndroidView
.getAuthData() .getAuthData()
private val streamHelper: StreamHelper = StreamHelper(authData) private val streamHelper: StreamHelper = StreamHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
lateinit var category: StreamHelper.Category lateinit var category: StreamHelper.Category

View File

@@ -37,7 +37,7 @@ class EditorBrowseViewModel(application: Application) : BaseAndroidViewModel(app
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val streamHelper: StreamHelper = StreamHelper(authData) private val streamHelper: StreamHelper = StreamHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<MutableList<App>> = MutableLiveData() val liveData: MutableLiveData<MutableList<App>> = MutableLiveData()
val appList: MutableList<App> = mutableListOf() val appList: MutableList<App> = mutableListOf()

View File

@@ -39,8 +39,9 @@ import kotlinx.coroutines.supervisorScope
abstract class BaseClusterViewModel(application: Application) : BaseAndroidViewModel(application) { abstract class BaseClusterViewModel(application: Application) : BaseAndroidViewModel(application) {
var authData: AuthData = AuthProvider.with(application).getAuthData() var authData: AuthData = AuthProvider.with(application).getAuthData()
var streamHelper: StreamHelper = StreamHelper(authData) var streamHelper: StreamHelper = StreamHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<ViewState> = MutableLiveData() val liveData: MutableLiveData<ViewState> = MutableLiveData()
var streamBundle: StreamBundle = StreamBundle() var streamBundle: StreamBundle = StreamBundle()

View File

@@ -41,7 +41,7 @@ class ReviewViewModel(application: Application) : BaseAndroidViewModel(applicati
.getAuthData() .getAuthData()
var reviewsHelper: ReviewsHelper = ReviewsHelper(authData) var reviewsHelper: ReviewsHelper = ReviewsHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<ReviewCluster> = MutableLiveData() val liveData: MutableLiveData<ReviewCluster> = MutableLiveData()

View File

@@ -38,7 +38,7 @@ class AppSalesViewModel(application: Application) : BaseAndroidViewModel(applica
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val appSalesHelper: AppSalesHelper = private val appSalesHelper: AppSalesHelper =
AppSalesHelper(authData).using(HttpClient.getPreferredClient()) AppSalesHelper(authData).using(HttpClient.getPreferredClient(application))
private var page: Int = 0 private var page: Int = 0
private val appList: MutableList<App> = mutableListOf() private val appList: MutableList<App> = mutableListOf()

View File

@@ -45,7 +45,7 @@ class SearchResultViewModel(application: Application) : BaseAndroidViewModel(app
private val webSearchHelper: WebSearchHelper = WebSearchHelper(authData) private val webSearchHelper: WebSearchHelper = WebSearchHelper(authData)
private val searchHelper: SearchHelper = SearchHelper(authData) private val searchHelper: SearchHelper = SearchHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<SearchBundle> = MutableLiveData() val liveData: MutableLiveData<SearchBundle> = MutableLiveData()

View File

@@ -40,7 +40,7 @@ class SearchSuggestionViewModel(application: Application) : AndroidViewModel(app
private val webSearchHelper: WebSearchHelper = WebSearchHelper(authData) private val webSearchHelper: WebSearchHelper = WebSearchHelper(authData)
private val searchHelper: SearchHelper = SearchHelper(authData) private val searchHelper: SearchHelper = SearchHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveSearchSuggestions: MutableLiveData<List<SearchSuggestEntry>> = MutableLiveData() val liveSearchSuggestions: MutableLiveData<List<SearchSuggestEntry>> = MutableLiveData()

View File

@@ -40,7 +40,7 @@ class SubCategoryClusterViewModel(application: Application) : BaseAndroidViewMod
var authData: AuthData = AuthProvider.with(application).getAuthData() var authData: AuthData = AuthProvider.with(application).getAuthData()
var categoryHelper: CategoryHelper = CategoryHelper(authData) var categoryHelper: CategoryHelper = CategoryHelper(authData)
.using(HttpClient.getPreferredClient()) .using(HttpClient.getPreferredClient(application))
val liveData: MutableLiveData<ViewState> = MutableLiveData() val liveData: MutableLiveData<ViewState> = MutableLiveData()
var streamBundle: StreamBundle = StreamBundle() var streamBundle: StreamBundle = StreamBundle()

View File

@@ -37,7 +37,7 @@ abstract class BaseChartViewModel(application: Application) : BaseAndroidViewMod
private val authData: AuthData = AuthProvider.with(application).getAuthData() private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val topChartsHelper: TopChartsHelper = private val topChartsHelper: TopChartsHelper =
TopChartsHelper(authData).using(HttpClient.getPreferredClient()) TopChartsHelper(authData).using(HttpClient.getPreferredClient(application))
lateinit var type: TopChartsHelper.Type lateinit var type: TopChartsHelper.Type
lateinit var chart: TopChartsHelper.Chart lateinit var chart: TopChartsHelper.Chart

View File

@@ -240,6 +240,10 @@
<string name="pref_install_profile_summary">"Select the target profile to install apps to. Only works with the root installation method"</string> <string name="pref_install_profile_summary">"Select the target profile to install apps to. Only works with the root installation method"</string>
<string name="pref_install_profile_title">"Installation profiles (Experimental)"</string> <string name="pref_install_profile_title">"Installation profiles (Experimental)"</string>
<string name="pref_network_title">"Networking"</string> <string name="pref_network_title">"Networking"</string>
<string name="pref_network_proxy_title">Proxy</string>
<string name="pref_network_proxy_enable">"Enable proxy"</string>
<string name="pref_network_proxy_enable_desc">"Allow all traffic from app to go through the proxy"</string>
<string name="pref_network_proxy_url">"Proxy URL"</string>
<string name="pref_ui_accent_title">"Accent"</string> <string name="pref_ui_accent_title">"Accent"</string>
<string name="pref_ui_theme_black">"Black"</string> <string name="pref_ui_theme_black">"Black"</string>
<string name="pref_ui_theme_dark">"Dark"</string> <string name="pref_ui_theme_dark">"Dark"</string>
@@ -326,7 +330,10 @@
<string name="toast_export_success">Device config exported</string> <string name="toast_export_success">Device config exported</string>
<string name="toast_export_failed">Could not export device config</string> <string name="toast_export_failed">Could not export device config</string>
<string name="toast_import_success">Device config imported</string> <string name="toast_import_success">Device config imported</string>
<string name="toast_import_failed">Could not import device config</string> <string name="toast_import_failed">"Could not import device config"</string>
<string name="toast_proxy_invalid">"Invalid proxy URL, check format!"</string>
<string name="toast_proxy_success">"Proxy set successfully"</string>
<string name="toast_proxy_failed">"Failed to set proxy"</string>
<string name="notification_channel_updater_service">App background download service</string> <string name="notification_channel_updater_service">App background download service</string>
<string name="app_updater_service_notif_title">Background app download</string> <string name="app_updater_service_notif_title">Background app download</string>
<string name="app_updater_service_notif_text">Enables background app download</string> <string name="app_updater_service_notif_text">Enables background app download</string>

View File

@@ -19,6 +19,28 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"> <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:allowDividerAbove="true"
app:allowDividerBelow="true"
app:iconSpaceReserved="false"
app:key="PREFERENCE_CATEGORY_PROXY"
app:summary="protocol://user:password@host:port"
app:title="@string/pref_network_proxy_title">
<SwitchPreferenceCompat
app:disableDependentsState="false"
app:iconSpaceReserved="false"
app:key="PREFERENCE_PROXY_ENABLED"
app:summary="@string/pref_network_proxy_enable_desc"
app:title="@string/pref_network_proxy_enable" />
<EditTextPreference
app:dependency="PREFERENCE_PROXY_ENABLED"
app:iconSpaceReserved="false"
app:key="PREFERENCE_PROXY_URL"
app:title="@string/pref_network_proxy_url" />
</PreferenceCategory>
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:defaultValue="false" app:defaultValue="false"
app:iconSpaceReserved="false" app:iconSpaceReserved="false"