From 0557b7ca6f499fb916b0d2bc1a49a5a53427a579 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Mon, 10 Jun 2024 14:18:52 +0200 Subject: [PATCH 1/2] Fix: App crashes on second open when local address empty --- .../main/java/com/cappielloantonio/tempo/util/Preferences.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt index 636a26cd..714c4f50 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/util/Preferences.kt @@ -174,7 +174,7 @@ object Preferences { @JvmStatic fun isInUseServerAddressLocal(): Boolean { - return getInUseServerAddress() == getLocalAddress() + return getInUseServerAddress() == getLocalAddress() && !getLocalAddress().isNullOrEmpty() } @JvmStatic @@ -187,7 +187,7 @@ object Preferences { fun isServerSwitchable(): Boolean { return App.getInstance().preferences.getLong( NEXT_SERVER_SWITCH, 0 - ) + 15000 < System.currentTimeMillis() + ) + 15000 < System.currentTimeMillis() && !getServer().isNullOrEmpty() && !getLocalAddress().isNullOrEmpty() } @JvmStatic From 33c69d49fc8d3c98861f02b62ffd486251d037d1 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Thu, 13 Jun 2024 15:47:35 +0200 Subject: [PATCH 2/2] Support HTTP basic auth --- .../tempo/glide/CustomGlideRequest.java | 23 ++++++++++++--- .../tempo/subsonic/RetrofitClient.kt | 29 ++++++++++++++++--- .../tempo/subsonic/Subsonic.java | 12 ++++++++ .../tempo/util/DownloadUtil.java | 10 +++++++ 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java index fe57c163..c9dc8211 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java +++ b/app/src/main/java/com/cappielloantonio/tempo/glide/CustomGlideRequest.java @@ -12,6 +12,8 @@ import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.RequestManager; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.load.model.LazyHeaders; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; @@ -111,7 +113,7 @@ public static String createUrl(String item, int size) { public static class Builder { private final RequestManager requestManager; - private Object item; + private String item; private Builder(Context context, String item, ResourceType type) { this.requestManager = Glide.with(context); @@ -128,9 +130,22 @@ public static Builder from(Context context, String item, ResourceType type) { } public RequestBuilder build() { - return requestManager - .load(item) - .transition(DrawableTransitionOptions.withCrossFade()); + + RequestBuilder requestBuilder; + + String basicAuthHeader = App.getSubsonicClientInstance(false).getBasicAuthHeader(); + if (basicAuthHeader == null || item == null) { + requestBuilder = requestManager.load(item); + } else { + //Add HTTP basic auth + requestBuilder = requestManager.load(new GlideUrl(item, + new LazyHeaders.Builder() + .addHeader("Authorization", basicAuthHeader) + .build() + )); + } + + return requestBuilder.transition(DrawableTransitionOptions.withCrossFade()); } } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/subsonic/RetrofitClient.kt b/app/src/main/java/com/cappielloantonio/tempo/subsonic/RetrofitClient.kt index f05238ce..60c2ffae 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/subsonic/RetrofitClient.kt +++ b/app/src/main/java/com/cappielloantonio/tempo/subsonic/RetrofitClient.kt @@ -4,25 +4,30 @@ import com.cappielloantonio.tempo.App import com.cappielloantonio.tempo.subsonic.utils.CacheUtil import com.google.gson.GsonBuilder import okhttp3.Cache +import okhttp3.Interceptor import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import java.io.IOException import java.util.concurrent.TimeUnit class RetrofitClient(subsonic: Subsonic) { var retrofit: Retrofit init { + retrofit = Retrofit.Builder() .baseUrl(subsonic.url) .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create())) .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create())) - .client(getOkHttpClient()) + .client(getOkHttpClient(subsonic)) .build() } - private fun getOkHttpClient(): OkHttpClient { + private fun getOkHttpClient(subsonic: Subsonic): OkHttpClient { val cacheUtil = CacheUtil(60, 60 * 60 * 24 * 30) // BrowsingClient 60 @@ -35,7 +40,7 @@ class RetrofitClient(subsonic: Subsonic) { // SystemClient 60 // AlbumSongListClient 60 - return OkHttpClient.Builder() + val okHttpClient = OkHttpClient.Builder() .callTimeout(2, TimeUnit.MINUTES) .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) @@ -44,7 +49,12 @@ class RetrofitClient(subsonic: Subsonic) { .addInterceptor(cacheUtil.offlineInterceptor) // .addNetworkInterceptor(cacheUtil.onlineInterceptor) .cache(getCache()) - .build() + + if (subsonic.basicAuthHeader != null) { + okHttpClient.addInterceptor(BasicAuthInterceptor(subsonic.basicAuthHeader)) + } + + return okHttpClient.build(); } private fun getHttpLoggingInterceptor(): HttpLoggingInterceptor { @@ -57,4 +67,15 @@ class RetrofitClient(subsonic: Subsonic) { val cacheSize = 10 * 1024 * 1024 return Cache(App.getContext().cacheDir, cacheSize.toLong()) } + + class BasicAuthInterceptor(private val basicAuthHeader: String) : Interceptor { + + @Throws(IOException::class) + override fun intercept(chain: Interceptor.Chain): Response { + val request: Request = chain.request() + val authenticatedRequest: Request = request.newBuilder() + .header("Authorization", basicAuthHeader).build() + return chain.proceed(authenticatedRequest) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/subsonic/Subsonic.java b/app/src/main/java/com/cappielloantonio/tempo/subsonic/Subsonic.java index de4b36b7..7236d331 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/subsonic/Subsonic.java +++ b/app/src/main/java/com/cappielloantonio/tempo/subsonic/Subsonic.java @@ -1,5 +1,7 @@ package com.cappielloantonio.tempo.subsonic; +import android.util.Base64; + import com.cappielloantonio.tempo.subsonic.api.albumsonglist.AlbumSongListClient; import com.cappielloantonio.tempo.subsonic.api.bookmarks.BookmarksClient; import com.cappielloantonio.tempo.subsonic.api.browsing.BrowsingClient; @@ -15,6 +17,7 @@ import com.cappielloantonio.tempo.subsonic.api.system.SystemClient; import com.cappielloantonio.tempo.subsonic.base.Version; +import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -159,4 +162,13 @@ public Map getParams() { return params; } + + public String getBasicAuthHeader() { + URI parsedUri = URI.create(getUrl()); + if (parsedUri.getUserInfo() != null) { + return "Basic " + Base64.encodeToString(parsedUri.getUserInfo().getBytes(), Base64.NO_WRAP); + } + + return null; + } } diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java index 0a5e982d..dd997f45 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/DownloadUtil.java @@ -22,6 +22,7 @@ import androidx.media3.exoplayer.offline.DownloadManager; import androidx.media3.exoplayer.offline.DownloadNotificationHelper; +import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.service.DownloaderManager; import java.io.File; @@ -29,6 +30,8 @@ import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Executors; @UnstableApi @@ -74,6 +77,13 @@ public static synchronized DataSource.Factory getHttpDataSourceFactory() { httpDataSourceFactory = new DefaultHttpDataSource .Factory() .setAllowCrossProtocolRedirects(true); + + String basicAuthHeader = App.getSubsonicClientInstance(false).getBasicAuthHeader(); + if (basicAuthHeader != null) { + Map headers = new HashMap<>(); + headers.put("Authorization", basicAuthHeader); + ((DefaultHttpDataSource.Factory) httpDataSourceFactory).setDefaultRequestProperties(headers); + } } return httpDataSourceFactory;