From 5895b2c8d890a85bc06dcfaf391983c11d562923 Mon Sep 17 00:00:00 2001 From: Jarne Demeulemeester <32322857+jarnedemeulemeester@users.noreply.github.com> Date: Sat, 5 Nov 2022 23:59:44 +0100 Subject: [PATCH] Add network settings (#192) * Add new network settings with socket timeout option * Add socketTimeout to AppPreferences * Format with ktlint * Add request timeout and connect timeout as options --- .../dev/jdtech/jellyfin/api/JellyfinApi.kt | 32 ++++++++++++++-- .../java/dev/jdtech/jellyfin/di/ApiModule.kt | 9 ++++- .../jellyfin/dialogs/ErrorDialogFragment.kt | 2 +- .../fragments/SettingsNetworkFragment.kt | 19 ++++++++++ .../jdtech/jellyfin/utils/AppPreferences.kt | 16 ++++++++ .../dev/jdtech/jellyfin/utils/Constants.kt | 8 ++++ .../dev/jdtech/jellyfin/utils/extensions.kt | 4 +- app/src/main/res/drawable/ic_network.xml | 37 +++++++++++++++++++ app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/xml/fragment_settings.xml | 5 +++ .../res/xml/fragment_settings_network.xml | 18 +++++++++ 11 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/dev/jdtech/jellyfin/fragments/SettingsNetworkFragment.kt create mode 100644 app/src/main/res/drawable/ic_network.xml create mode 100644 app/src/main/res/xml/fragment_settings_network.xml diff --git a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt index eb02c98b..5b792ae9 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/api/JellyfinApi.kt @@ -2,7 +2,9 @@ package dev.jdtech.jellyfin.api import android.content.Context import dev.jdtech.jellyfin.BuildConfig +import dev.jdtech.jellyfin.utils.Constants import java.util.UUID +import org.jellyfin.sdk.api.client.HttpClientOptions import org.jellyfin.sdk.api.client.extensions.devicesApi import org.jellyfin.sdk.api.client.extensions.itemsApi import org.jellyfin.sdk.api.client.extensions.mediaInfoApi @@ -21,15 +23,27 @@ import org.jellyfin.sdk.model.ClientInfo * Jellyfin API class using org.jellyfin.sdk:jellyfin-platform-android * * @param androidContext The context + * @param socketTimeout The socket timeout * @constructor Creates a new [JellyfinApi] instance */ -class JellyfinApi(androidContext: Context) { +class JellyfinApi( + androidContext: Context, + requestTimeout: Long = Constants.NETWORK_DEFAULT_REQUEST_TIMEOUT, + connectTimeout: Long = Constants.NETWORK_DEFAULT_CONNECT_TIMEOUT, + socketTimeout: Long = Constants.NETWORK_DEFAULT_SOCKET_TIMEOUT +) { val jellyfin = createJellyfin { clientInfo = ClientInfo(name = androidContext.applicationInfo.loadLabel(androidContext.packageManager).toString(), version = BuildConfig.VERSION_NAME) context = androidContext } - val api = jellyfin.createApi() + val api = jellyfin.createApi( + httpClientOptions = HttpClientOptions( + requestTimeout = requestTimeout, + connectTimeout = connectTimeout, + socketTimeout = socketTimeout + ) + ) var userId: UUID? = null val devicesApi = api.devicesApi @@ -48,11 +62,21 @@ class JellyfinApi(androidContext: Context) { @Volatile private var INSTANCE: JellyfinApi? = null - fun getInstance(context: Context): JellyfinApi { + fun getInstance( + context: Context, + requestTimeout: Long = Constants.NETWORK_DEFAULT_REQUEST_TIMEOUT, + connectTimeout: Long = Constants.NETWORK_DEFAULT_CONNECT_TIMEOUT, + socketTimeout: Long = Constants.NETWORK_DEFAULT_SOCKET_TIMEOUT + ): JellyfinApi { synchronized(this) { var instance = INSTANCE if (instance == null) { - instance = JellyfinApi(context.applicationContext) + instance = JellyfinApi( + androidContext = context.applicationContext, + requestTimeout = requestTimeout, + connectTimeout = connectTimeout, + socketTimeout = socketTimeout + ) INSTANCE = instance } return instance diff --git a/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt b/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt index f086ac6a..0933e0c1 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/di/ApiModule.kt @@ -9,6 +9,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.database.ServerDatabaseDao +import dev.jdtech.jellyfin.utils.AppPreferences import javax.inject.Singleton @Module @@ -19,9 +20,15 @@ object ApiModule { fun provideJellyfinApi( @ApplicationContext application: Context, sharedPreferences: SharedPreferences, + appPreferences: AppPreferences, serverDatabase: ServerDatabaseDao ): JellyfinApi { - val jellyfinApi = JellyfinApi.getInstance(application) + val jellyfinApi = JellyfinApi.getInstance( + context = application, + requestTimeout = appPreferences.requestTimeout, + connectTimeout = appPreferences.connectTimeout, + socketTimeout = appPreferences.socketTimeout + ) val serverId = sharedPreferences.getString("selectedServer", null) if (serverId != null) { diff --git a/app/src/main/java/dev/jdtech/jellyfin/dialogs/ErrorDialogFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/dialogs/ErrorDialogFragment.kt index 9c6dfbd3..b0097ecd 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/dialogs/ErrorDialogFragment.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/dialogs/ErrorDialogFragment.kt @@ -10,7 +10,7 @@ import dev.jdtech.jellyfin.utils.serializable import java.io.Serializable import java.lang.IllegalStateException -class ErrorDialogFragment: DialogFragment() { +class ErrorDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val error = requireArguments().serializable("error")!! return activity?.let { diff --git a/app/src/main/java/dev/jdtech/jellyfin/fragments/SettingsNetworkFragment.kt b/app/src/main/java/dev/jdtech/jellyfin/fragments/SettingsNetworkFragment.kt new file mode 100644 index 00000000..c4b1fbfe --- /dev/null +++ b/app/src/main/java/dev/jdtech/jellyfin/fragments/SettingsNetworkFragment.kt @@ -0,0 +1,19 @@ +package dev.jdtech.jellyfin.fragments + +import android.os.Bundle +import android.text.InputType +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceFragmentCompat +import dev.jdtech.jellyfin.R +import dev.jdtech.jellyfin.utils.Constants + +@Suppress("unused") +class SettingsNetworkFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.fragment_settings_network, rootKey) + + findPreference(Constants.PREF_NETWORK_SOCKET_TIMEOUT)?.setOnBindEditTextListener { editText -> + editText.inputType = InputType.TYPE_CLASS_NUMBER + } + } +} diff --git a/app/src/main/java/dev/jdtech/jellyfin/utils/AppPreferences.kt b/app/src/main/java/dev/jdtech/jellyfin/utils/AppPreferences.kt index c328d29f..12fb418b 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/utils/AppPreferences.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/utils/AppPreferences.kt @@ -43,4 +43,20 @@ constructor( DEFAULT_SEEK_FORWARD_INCREMENT_MS.toString() )!!.toLongOrNull() ?: DEFAULT_SEEK_FORWARD_INCREMENT_MS val mpvDisableHwDec = sharedPreferences.getBoolean("mpv_disable_hwdec", false) + + // Network + val requestTimeout = sharedPreferences.getString( + Constants.PREF_NETWORK_REQUEST_TIMEOUT, + Constants.NETWORK_DEFAULT_REQUEST_TIMEOUT.toString() + )!!.toLongOrNull() ?: Constants.NETWORK_DEFAULT_REQUEST_TIMEOUT + + val connectTimeout = sharedPreferences.getString( + Constants.PREF_NETWORK_CONNECT_TIMEOUT, + Constants.NETWORK_DEFAULT_CONNECT_TIMEOUT.toString() + )!!.toLongOrNull() ?: Constants.NETWORK_DEFAULT_CONNECT_TIMEOUT + + val socketTimeout = sharedPreferences.getString( + Constants.PREF_NETWORK_SOCKET_TIMEOUT, + Constants.NETWORK_DEFAULT_SOCKET_TIMEOUT.toString() + )!!.toLongOrNull() ?: Constants.NETWORK_DEFAULT_SOCKET_TIMEOUT } diff --git a/app/src/main/java/dev/jdtech/jellyfin/utils/Constants.kt b/app/src/main/java/dev/jdtech/jellyfin/utils/Constants.kt index 347a3e0e..408f0f7e 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/utils/Constants.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/utils/Constants.kt @@ -19,6 +19,9 @@ object Constants { const val PREF_IMAGE_CACHE_SIZE = "pref_image_cache_size" const val PREF_THEME = "theme" const val PREF_DYNAMIC_COLORS = "dynamic_colors" + const val PREF_NETWORK_REQUEST_TIMEOUT = "pref_network_request_timeout" + const val PREF_NETWORK_CONNECT_TIMEOUT = "pref_network_connect_timeout" + const val PREF_NETWORK_SOCKET_TIMEOUT = "pref_network_socket_timeout" // caching const val DEFAULT_CACHE_SIZE = 20 @@ -27,4 +30,9 @@ object Constants { const val FAVORITE_TYPE_MOVIES = 0 const val FAVORITE_TYPE_SHOWS = 1 const val FAVORITE_TYPE_EPISODES = 2 + + // network + const val NETWORK_DEFAULT_REQUEST_TIMEOUT = 30_000L + const val NETWORK_DEFAULT_CONNECT_TIMEOUT = 6_000L + const val NETWORK_DEFAULT_SOCKET_TIMEOUT = 10_000L } diff --git a/app/src/main/java/dev/jdtech/jellyfin/utils/extensions.kt b/app/src/main/java/dev/jdtech/jellyfin/utils/extensions.kt index 1ffad32f..d426ff26 100644 --- a/app/src/main/java/dev/jdtech/jellyfin/utils/extensions.kt +++ b/app/src/main/java/dev/jdtech/jellyfin/utils/extensions.kt @@ -12,9 +12,9 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import dev.jdtech.jellyfin.AppNavigationDirections import dev.jdtech.jellyfin.models.View +import java.io.Serializable import org.jellyfin.sdk.model.api.BaseItemDto import timber.log.Timber -import java.io.Serializable fun BaseItemDto.toView(): View { return View( @@ -58,4 +58,4 @@ fun ImageButton.setTintColorAttribute(@AttrRes attributeId: Int, theme: Resource inline fun Bundle.serializable(key: String): T? = when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializable(key, T::class.java) else -> @Suppress("DEPRECATION") getSerializable(key) as? T -} \ No newline at end of file +} diff --git a/app/src/main/res/drawable/ic_network.xml b/app/src/main/res/drawable/ic_network.xml new file mode 100644 index 00000000..cfbb40ff --- /dev/null +++ b/app/src/main/res/drawable/ic_network.xml @@ -0,0 +1,37 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2fddbc53..e9bb9cc7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -138,4 +138,8 @@ Follow system Light Dark + Network + Request timeout (ms) + Connect timeout (ms) + Socket timeout (ms) \ No newline at end of file diff --git a/app/src/main/res/xml/fragment_settings.xml b/app/src/main/res/xml/fragment_settings.xml index a346cf2d..9fbba1c2 100644 --- a/app/src/main/res/xml/fragment_settings.xml +++ b/app/src/main/res/xml/fragment_settings.xml @@ -31,6 +31,11 @@ app:title="@string/settings_category_device" app:icon="@drawable/ic_smartphone" /> + + diff --git a/app/src/main/res/xml/fragment_settings_network.xml b/app/src/main/res/xml/fragment_settings_network.xml new file mode 100644 index 00000000..5e5bd8a2 --- /dev/null +++ b/app/src/main/res/xml/fragment_settings_network.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file