From c0fe52b2b3a0e17c34a32de5e2bcc8ebde2332e5 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 09:42:50 +0100 Subject: [PATCH 01/29] Replace removed FloatMath with Math class. FloatMath was removed since api 23. Signed-off-by: Yahor Berdnikau --- .../src/main/java/net/simonvt/menudrawer/Scroller.java | 5 ++--- .../handmark/pulltorefresh/library/PullToRefreshWebView.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/menudrawer/src/main/java/net/simonvt/menudrawer/Scroller.java b/menudrawer/src/main/java/net/simonvt/menudrawer/Scroller.java index 0b7464fd..4185cfd3 100644 --- a/menudrawer/src/main/java/net/simonvt/menudrawer/Scroller.java +++ b/menudrawer/src/main/java/net/simonvt/menudrawer/Scroller.java @@ -19,7 +19,6 @@ package net.simonvt.menudrawer; import android.content.Context; import android.hardware.SensorManager; import android.os.Build; -import android.util.FloatMath; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -371,7 +370,7 @@ class Scroller { float dx = (float) (mFinalX - mStartX); float dy = (float) (mFinalY - mStartY); - float hyp = FloatMath.sqrt(dx * dx + dy * dy); + float hyp = (float) Math.sqrt(dx * dx + dy * dy); float ndx = dx / hyp; float ndy = dy / hyp; @@ -388,7 +387,7 @@ class Scroller { mMode = FLING_MODE; mFinished = false; - float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY); + float velocity = (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY); mVelocity = velocity; final double l = Math.log(START_TENSION * velocity / ALPHA); diff --git a/pulltorefresh/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java b/pulltorefresh/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java index 3f873de0..a008e424 100644 --- a/pulltorefresh/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java +++ b/pulltorefresh/src/main/java/com/handmark/pulltorefresh/library/PullToRefreshWebView.java @@ -21,7 +21,6 @@ import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.util.AttributeSet; -import android.util.FloatMath; import android.webkit.WebChromeClient; import android.webkit.WebView; @@ -112,7 +111,7 @@ public class PullToRefreshWebView extends PullToRefreshBase { @Override protected boolean isReadyForPullEnd() { - float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); + float exactContentHeight = (float) Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()); return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight()); } @@ -158,7 +157,7 @@ public class PullToRefreshWebView extends PullToRefreshBase { } private int getScrollRange() { - return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) + return (int) Math.max(0, Math.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale()) - (getHeight() - getPaddingBottom() - getPaddingTop())); } } From ed78fce31c6466ef5aad9bf62340400a65b36dae Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 09:44:10 +0100 Subject: [PATCH 02/29] Remove deprecated HttpClient. Apache HttpClient package was removed from Android since api 23. Signed-off-by: Yahor Berdnikau --- .../org/moire/ultrasonic/service/MusicService.java | 1 - .../src/main/java/org/moire/ultrasonic/util/Util.java | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java index 65624b8f..17d3b42d 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java @@ -21,7 +21,6 @@ package org.moire.ultrasonic.service; import android.content.Context; import android.graphics.Bitmap; -import org.apache.http.HttpResponse; import org.moire.ultrasonic.domain.Bookmark; import org.moire.ultrasonic.domain.ChatMessage; import org.moire.ultrasonic.domain.Genre; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java index 4c6c11c9..f9994f3b 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java @@ -65,8 +65,6 @@ import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver; import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadService; import org.moire.ultrasonic.service.DownloadServiceImpl; - -import org.apache.http.HttpEntity; import org.moire.ultrasonic.service.MusicServiceFactory; import java.io.ByteArrayOutputStream; @@ -405,15 +403,6 @@ public class Util extends DownloadActivity return context.getSharedPreferences(Constants.PREFERENCES_FILE_NAME, 0); } - public static String getContentType(HttpEntity entity) - { - if (entity == null || entity.getContentType() == null) - { - return null; - } - return entity.getContentType().getValue(); - } - public static int getRemainingTrialDays(Context context) { SharedPreferences preferences = getPreferences(context); From 2e9935e182062c0a1b3021fec81a8ad7930891eb Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:00:10 +0100 Subject: [PATCH 03/29] Remove usage of HttpRequest from StreamProxy. Apache http client classes was removed since api 23. And actually this HttpRequest doesn't required here at all. Signed-off-by: Yahor Berdnikau --- .../moire/ultrasonic/util/StreamProxy.java | 118 ++++++++---------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/StreamProxy.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/StreamProxy.java index 848731f1..5e8c5d4a 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/StreamProxy.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/StreamProxy.java @@ -6,9 +6,6 @@ import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.DownloadService; -import org.apache.http.HttpRequest; -import org.apache.http.message.BasicHttpRequest; - import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; @@ -108,81 +105,66 @@ public class StreamProxy implements Runnable Log.i(TAG, "Proxy interrupted. Shutting down."); } - private class StreamToMediaPlayerTask implements Runnable - { + private class StreamToMediaPlayerTask implements Runnable { + String localPath; + Socket client; + int cbSkip; - String localPath; - Socket client; - int cbSkip; + StreamToMediaPlayerTask(Socket client) { + this.client = client; + } - public StreamToMediaPlayerTask(Socket client) - { - this.client = client; - } + private String readRequest() { + InputStream is; + String firstLine; + try { + is = client.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8192); + firstLine = reader.readLine(); + } catch (IOException e) { + Log.e(TAG, "Error parsing request", e); + return null; + } - private HttpRequest readRequest() - { - HttpRequest request; - InputStream is; - String firstLine; - try - { - is = client.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8192); - firstLine = reader.readLine(); - } - catch (IOException e) - { - Log.e(TAG, "Error parsing request", e); - return null; - } + if (firstLine == null) { + Log.i(TAG, "Proxy client closed connection without a request."); + return null; + } - if (firstLine == null) - { - Log.i(TAG, "Proxy client closed connection without a request."); - return null; - } + StringTokenizer st = new StringTokenizer(firstLine); + st.nextToken(); // method + String uri = st.nextToken(); + String realUri = uri.substring(1); + Log.i(TAG, realUri); - StringTokenizer st = new StringTokenizer(firstLine); - String method = st.nextToken(); - String uri = st.nextToken(); - String realUri = uri.substring(1); - Log.i(TAG, realUri); - request = new BasicHttpRequest(method, realUri); - return request; - } + return realUri; + } - public boolean processRequest() - { - HttpRequest request = readRequest(); - if (request == null) - { - return false; - } + boolean processRequest() { + final String uri = readRequest(); + if (uri == null || uri.isEmpty()) { + return false; + } - // Read HTTP headers - Log.i(TAG, "Processing request"); + // Read HTTP headers + Log.i(TAG, "Processing request: " + uri); - try - { - localPath = URLDecoder.decode(request.getRequestLine().getUri(), Constants.UTF_8); - } - catch (UnsupportedEncodingException e) - { - Log.e(TAG, "Unsupported encoding", e); - return false; - } + try { + localPath = URLDecoder.decode(uri, Constants.UTF_8); + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "Unsupported encoding", e); + return false; + } - Log.i(TAG, String.format("Processing request for file %s", localPath)); - File file = new File(localPath); - if (!file.exists()) - { - Log.e(TAG, String.format("File %s does not exist", localPath)); - return false; - } + Log.i(TAG, String.format("Processing request for file %s", localPath)); + File file = new File(localPath); + if (!file.exists()) { + Log.e(TAG, String.format("File %s does not exist", localPath)); + return false; + } - return true; - } + return true; + } @Override public void run() From f528ad884598238ca76d86819aa8fcda4c59ace2 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:01:05 +0100 Subject: [PATCH 04/29] Bump target api level to 23. Also set compile sdk level to 27 and android support libs version to 23.4.0. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 89a1869c..de2ff77d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,7 +1,7 @@ ext.versions = [ minSdk : 14, - targetSdk : 22, - compileSdk : 22, + targetSdk : 23, + compileSdk : 27, gradle : '4.3.1', buildTools : "25.0.3", @@ -12,7 +12,7 @@ ext.versions = [ jacoco : "0.7.9", jacocoAndroid : "0.1.2", - androidSupport : "22.2.1", + androidSupport : "23.4.0", kotlin : "1.1.60", From 0943276a502c999241e50df27885de89523a9142 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:03:37 +0100 Subject: [PATCH 05/29] Update gradle wrapper to 4.4.1 version. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 54712 -> 54333 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index de2ff77d..7be992f3 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,7 +2,7 @@ ext.versions = [ minSdk : 14, targetSdk : 23, compileSdk : 27, - gradle : '4.3.1', + gradle : '4.4.1', buildTools : "25.0.3", androidTools : "2.3.3", diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ed88a042a287c140a32e1639edfc91b2a233da8c..99340b4ad18d3c7e764794d300ffd35017036793 100644 GIT binary patch delta 15718 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 V8~Y#MrvpHQMOsL)!T)*s{{Wi*t`7hJ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 702c4b68..57c7d2d2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip From c76ffe4db76b84a1de9200b87087da830038aa2f Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:12:10 +0100 Subject: [PATCH 06/29] Update android plugin to 3.0.1 version. Yay! Signed-off-by: Yahor Berdnikau --- build.gradle | 2 ++ dependencies.gradle | 3 +-- library/build.gradle | 3 +-- menudrawer/build.gradle | 1 - pulltorefresh/build.gradle | 1 - ultrasonic/build.gradle | 25 ++++++++++++------------- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/build.gradle b/build.gradle index 883b3ebc..0d7e4ed2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ buildscript { repositories { jcenter() + google() maven { url "https://plugins.gradle.org/m2/" } } dependencies { @@ -23,6 +24,7 @@ allprojects { buildscript { repositories { jcenter() + google() } } diff --git a/dependencies.gradle b/dependencies.gradle index 7be992f3..62cacfd6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -4,8 +4,7 @@ ext.versions = [ compileSdk : 27, gradle : '4.4.1', - buildTools : "25.0.3", - androidTools : "2.3.3", + androidTools : "3.0.1", ktlint : "0.12.1", ktlintGradle : "2.3.0", detekt : "1.0.0.RC5-4", diff --git a/library/build.gradle b/library/build.gradle index c375cbc8..b2e07832 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'com.android.library' android { compileSdkVersion versions.compileSdk - buildToolsVersion versions.buildTools defaultConfig { minSdkVersion versions.minSdk @@ -23,5 +22,5 @@ android { } dependencies { - compile androidSupport.support + api androidSupport.support } diff --git a/menudrawer/build.gradle b/menudrawer/build.gradle index cd6f4945..1979d8cd 100644 --- a/menudrawer/build.gradle +++ b/menudrawer/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'com.android.library' android { compileSdkVersion versions.compileSdk - buildToolsVersion versions.buildTools defaultConfig { minSdkVersion versions.minSdk diff --git a/pulltorefresh/build.gradle b/pulltorefresh/build.gradle index cd6f4945..1979d8cd 100644 --- a/pulltorefresh/build.gradle +++ b/pulltorefresh/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'com.android.library' android { compileSdkVersion versions.compileSdk - buildToolsVersion versions.buildTools defaultConfig { minSdkVersion versions.minSdk diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 0f7ee918..822c2243 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -5,7 +5,6 @@ apply from: "../gradle_scripts/code_quality.gradle" android { compileSdkVersion versions.compileSdk - buildToolsVersion versions.buildTools defaultConfig { applicationId "org.moire.ultrasonic" @@ -42,21 +41,21 @@ android { } dependencies { - compile project(':menudrawer') - compile project(':pulltorefresh') - compile project(':library') - compile project(':subsonic-api') + implementation project(':menudrawer') + implementation project(':pulltorefresh') + implementation project(':library') + implementation project(':subsonic-api') - compile androidSupport.support - compile androidSupport.design + implementation androidSupport.support + implementation androidSupport.design - compile other.kotlinStdlib + implementation other.kotlinStdlib - testCompile other.kotlinReflect - testCompile testing.junit - testCompile testing.kotlinJunit - testCompile testing.mockitoKotlin - testCompile testing.kluent + testImplementation other.kotlinReflect + testImplementation testing.junit + testImplementation testing.kotlinJunit + testImplementation testing.mockitoKotlin + testImplementation testing.kluent } // Excluding all non-kotlin classes From 11c1b92098e6b7cee35eba0844e36d9bdc22021d Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:16:30 +0100 Subject: [PATCH 07/29] Update Detekt to 1.0.0.RC6 version. Signed-off-by: Yahor Berdnikau --- build.gradle | 5 +---- dependencies.gradle | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 0d7e4ed2..25ea3d17 100644 --- a/build.gradle +++ b/build.gradle @@ -11,10 +11,7 @@ buildscript { classpath gradlePlugins.androidTools classpath gradlePlugins.kotlin classpath gradlePlugins.ktlintGradle - classpath(gradlePlugins.detekt) { - exclude module: 'kotlin-compiler-embeddable' - exclude module: 'kotlin-stdlib' - } + classpath gradlePlugins.detekt classpath gradlePlugins.jacocoAndroid } } diff --git a/dependencies.gradle b/dependencies.gradle index 62cacfd6..a2744cd4 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -7,7 +7,7 @@ ext.versions = [ androidTools : "3.0.1", ktlint : "0.12.1", ktlintGradle : "2.3.0", - detekt : "1.0.0.RC5-4", + detekt : "1.0.0.RC6", jacoco : "0.7.9", jacocoAndroid : "0.1.2", From 07802e9206ffb3fc11abb0c726d44255f39eec11 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:44:19 +0100 Subject: [PATCH 08/29] Update KtLint to 0.14.0 version. Also fix style problems. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 2 +- .../api/subsonic/SubsonicAPIClientTest.kt | 4 +- .../api/subsonic/SubsonicApiGetAlbumTest.kt | 20 ++++---- .../api/subsonic/SubsonicApiGetArtistsTest.kt | 9 ++-- .../SubsonicApiGetMusicDirectoryTest.kt | 26 +++++----- .../subsonic/SubsonicApiGetPodcastsTest.kt | 12 +++-- .../SubsonicApiGetSongsByGenreTest.kt | 6 ++- .../subsonic/SubsonicApiGetVideosListTest.kt | 12 +++-- .../subsonic/SubsonicApiJukeboxControlTest.kt | 4 +- .../api/subsonic/SubsonicApiPasswordTest.kt | 8 ++-- .../api/subsonic/SubsonicApiSearchTest.kt | 3 +- .../subsonic/SubsonicApiSearchThreeTest.kt | 18 ++++--- .../api/subsonic/SubsonicApiSearchTwoTest.kt | 10 ++-- .../interceptors/VersionInterceptorTest.kt | 3 +- .../api/subsonic/ApiVersionCheckWrapper.kt | 4 +- .../api/subsonic/SubsonicAPIDefinition.kt | 47 ++++++++++--------- .../api/subsonic/SubsonicAPIVersions.kt | 3 +- .../interceptors/RangeHeaderInterceptor.kt | 4 +- .../api/subsonic/models/SearchResult.kt | 7 +-- .../subsonic/response/GetPlaylistsResponse.kt | 3 +- .../subsonic/response/SearchTwoResponse.kt | 9 ++-- .../data/APIMusicFolderConverter.kt | 4 +- .../ultrasonic/data/APIAlbumConverterTest.kt | 6 +-- .../ultrasonic/data/APIArtistConverterTest.kt | 3 +- .../data/APIIndexesConverterTest.kt | 4 +- .../data/APIMusicDirectoryConverterTest.kt | 3 +- 26 files changed, 135 insertions(+), 99 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index a2744cd4..aea34b15 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -5,7 +5,7 @@ ext.versions = [ gradle : '4.4.1', androidTools : "3.0.1", - ktlint : "0.12.1", + ktlint : "0.14.0", ktlintGradle : "2.3.0", detekt : "1.0.0.RC6", jacoco : "0.7.9", diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt index 4ecd67ac..6d4dfffd 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClientTest.kt @@ -14,7 +14,7 @@ abstract class SubsonicAPIClientTest { @Before fun setUp() { - client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME, PASSWORD, - CLIENT_VERSION, CLIENT_ID) + client = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, PASSWORD, CLIENT_VERSION, CLIENT_ID) } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAlbumTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAlbumTest.kt index 14ad35ba..32793390 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAlbumTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAlbumTest.kt @@ -50,16 +50,18 @@ class SubsonicApiGetAlbumTest : SubsonicAPIClientTest() { year `should equal to` 2008 genre `should equal to` "Hard Rock" songList.size `should equal to` 15 - songList[0] `should equal` MusicDirectoryChild(id = "6491", parent = "6475", isDir = false, - title = "Rock 'n' Roll Train", album = "Black Ice", artist = "AC/DC", - track = 1, year = 2008, genre = "Hard Rock", coverArt = "6475", size = 7205451, - contentType = "audio/mpeg", suffix = "mp3", duration = 261, bitRate = 219, - path = "AC_DC/Black Ice/01 Rock 'n' Roll Train.mp3", isVideo = false, - playCount = 0, discNumber = 1, created = parseDate("2016-10-23T15:31:20.000Z"), + songList[0] `should equal` MusicDirectoryChild(id = "6491", parent = "6475", + isDir = false, title = "Rock 'n' Roll Train", album = "Black Ice", + artist = "AC/DC", track = 1, year = 2008, genre = "Hard Rock", + coverArt = "6475", size = 7205451, contentType = "audio/mpeg", suffix = "mp3", + duration = 261, bitRate = 219, + path = "AC_DC/Black Ice/01 Rock 'n' Roll Train.mp3", + isVideo = false, playCount = 0, discNumber = 1, + created = parseDate("2016-10-23T15:31:20.000Z"), albumId = "618", artistId = "362", type = "music") - songList[5] `should equal` MusicDirectoryChild(id = "6492", parent = "6475", isDir = false, - title = "Smash 'n' Grab", album = "Black Ice", artist = "AC/DC", track = 6, - year = 2008, genre = "Hard Rock", coverArt = "6475", size = 6697204, + songList[5] `should equal` MusicDirectoryChild(id = "6492", parent = "6475", + isDir = false, title = "Smash 'n' Grab", album = "Black Ice", artist = "AC/DC", + track = 6, year = 2008, genre = "Hard Rock", coverArt = "6475", size = 6697204, contentType = "audio/mpeg", suffix = "mp3", duration = 246, bitRate = 216, path = "AC_DC/Black Ice/06 Smash 'n' Grab.mp3", isVideo = false, playCount = 0, discNumber = 1, created = parseDate("2016-10-23T15:31:20.000Z"), diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistsTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistsTest.kt index ac23364d..100042c5 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistsTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetArtistsTest.kt @@ -37,11 +37,14 @@ class SubsonicApiGetArtistsTest : SubsonicAPIClientTest() { indexList `should equal` listOf( Index(name = "A", artists = listOf( Artist(id = "362", name = "AC/DC", coverArt = "ar-362", albumCount = 2), - Artist(id = "254", name = "Acceptance", coverArt = "ar-254", albumCount = 1) + Artist(id = "254", name = "Acceptance", coverArt = "ar-254", + albumCount = 1) )), Index(name = "T", artists = listOf( - Artist(id = "516", name = "Tangerine Dream", coverArt = "ar-516", albumCount = 1), - Artist(id = "242", name = "Taproot", coverArt = "ar-242", albumCount = 2) + Artist(id = "516", name = "Tangerine Dream", coverArt = "ar-516", + albumCount = 1), + Artist(id = "242", name = "Taproot", coverArt = "ar-242", + albumCount = 2) )) ) } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetMusicDirectoryTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetMusicDirectoryTest.kt index b4deff6b..0801e673 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetMusicDirectoryTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetMusicDirectoryTest.kt @@ -50,19 +50,21 @@ class SubsonicApiGetMusicDirectoryTest : SubsonicAPIClientTest() { starred `should equal` null playCount `should equal to` 1 childList.size `should be` 2 - childList[0] `should equal` MusicDirectoryChild(id = "4844", parent = "4836", isDir = false, - title = "Crash", album = "12 Stones", artist = "12 Stones", track = 1, year = 2002, - genre = "Alternative Rock", coverArt = "4836", size = 5348318L, - contentType = "audio/mpeg", suffix = "mp3", duration = 222, bitRate = 192, - path = "12 Stones/12 Stones/01 Crash.mp3", isVideo = false, playCount = 0, - discNumber = 1, created = parseDate("2016-10-23T15:19:10.000Z"), + childList[0] `should equal` MusicDirectoryChild(id = "4844", parent = "4836", + isDir = false, title = "Crash", album = "12 Stones", artist = "12 Stones", + track = 1, year = 2002, genre = "Alternative Rock", coverArt = "4836", + size = 5348318L, contentType = "audio/mpeg", suffix = "mp3", duration = 222, + bitRate = 192, path = "12 Stones/12 Stones/01 Crash.mp3", isVideo = false, + playCount = 0, discNumber = 1, + created = parseDate("2016-10-23T15:19:10.000Z"), albumId = "454", artistId = "288", type = "music") - childList[1] `should equal` MusicDirectoryChild(id = "4845", parent = "4836", isDir = false, - title = "Broken", album = "12 Stones", artist = "12 Stones", track = 2, year = 2002, - genre = "Alternative Rock", coverArt = "4836", size = 4309043L, - contentType = "audio/mpeg", suffix = "mp3", duration = 179, bitRate = 192, - path = "12 Stones/12 Stones/02 Broken.mp3", isVideo = false, playCount = 0, - discNumber = 1, created = parseDate("2016-10-23T15:19:09.000Z"), + childList[1] `should equal` MusicDirectoryChild(id = "4845", parent = "4836", + isDir = false, title = "Broken", album = "12 Stones", artist = "12 Stones", + track = 2, year = 2002, genre = "Alternative Rock", coverArt = "4836", + size = 4309043L, contentType = "audio/mpeg", suffix = "mp3", duration = 179, + bitRate = 192, path = "12 Stones/12 Stones/02 Broken.mp3", isVideo = false, + playCount = 0, discNumber = 1, + created = parseDate("2016-10-23T15:19:09.000Z"), albumId = "454", artistId = "288", type = "music") } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetPodcastsTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetPodcastsTest.kt index e5ead1e3..985425e4 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetPodcastsTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetPodcastsTest.kt @@ -33,13 +33,16 @@ class SubsonicApiGetPodcastsTest : SubsonicAPIClientTest() { id `should equal to` "2" url `should equal to` "http://feeds.codenewbie.org/cnpodcast.xml" title `should equal to` "CodeNewbie" - description `should equal to` "Stories and interviews from people on their coding journey." + description `should equal to` "Stories and interviews from people on their coding " + + "journey." coverArt `should equal to` "pod-2" - originalImageUrl `should equal to` "http://codenewbie.blubrry.com/wp-content/uploads/powerpress/220808.jpg" + originalImageUrl `should equal to` "http://codenewbie.blubrry.com/wp-content/uploads/" + + "powerpress/220808.jpg" status `should equal to` "completed" errorMessage `should equal to` "" episodeList.size `should equal to` 10 - episodeList[0] `should equal` MusicDirectoryChild(id = "148", parent = "9959", isDir = false, + episodeList[0] `should equal` MusicDirectoryChild(id = "148", parent = "9959", + isDir = false, title = "S1:EP3 – How to teach yourself computer science (Vaidehi Joshi)", album = "CodeNewbie", artist = "podcasts", coverArt = "9959", size = 38274221, contentType = "audio/mpeg", suffix = "mp3", @@ -56,7 +59,8 @@ class SubsonicApiGetPodcastsTest : SubsonicAPIClientTest() { "CodeNewbie basecs 100 Days of Code Conway's Game of Life Hexes and " + "Other Magical Numbers (Vaidehi's blog post) Bits, Bytes, Building " + "With Binary (Vaidehi's blog post) Rust", - status = "completed", publishDate = parseDate("2017-08-29T00:01:01.000Z")) + status = "completed", + publishDate = parseDate("2017-08-29T00:01:01.000Z")) } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetSongsByGenreTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetSongsByGenreTest.kt index 2be1b688..c344c8b3 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetSongsByGenreTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetSongsByGenreTest.kt @@ -39,8 +39,10 @@ class SubsonicApiGetSongsByGenreTest : SubsonicAPIClientTest() { artist = "DJ Polyakov PPK Feat Kate Cameron", year = 2009, genre = "Trance", size = 26805932, contentType = "audio/mpeg", suffix = "mp3", duration = 670, bitRate = 320, - path = "DJ Polyakov PPK Feat Kate Cameron/668/00 My Heart (Vadim Zhukov Remix).mp3", - isVideo = false, playCount = 2, created = parseDate("2016-10-23T21:58:29.000Z"), + path = "DJ Polyakov PPK Feat Kate Cameron/668/00 My Heart (Vadim Zhukov " + + "Remix).mp3", + isVideo = false, playCount = 2, + created = parseDate("2016-10-23T21:58:29.000Z"), albumId = "5", artistId = "4", type = "music") } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetVideosListTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetVideosListTest.kt index 1e4cb79c..ef3d71ab 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetVideosListTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetVideosListTest.kt @@ -27,11 +27,13 @@ class SubsonicApiGetVideosListTest : SubsonicAPIClientTest() { assertResponseSuccessful(response) with(response.body().videosList) { size `should equal to` 1 - this[0] `should equal` MusicDirectoryChild(id = "10402", parent = "10401", isDir = false, - title = "MVI_0512", album = "Incoming", size = 21889646, - contentType = "video/avi", suffix = "avi", transcodedContentType = "video/x-flv", - transcodedSuffix = "flv", path = "Incoming/MVI_0512.avi", isVideo = true, - playCount = 0, created = parseDate("2017-11-19T12:34:33.000Z"), type = "video") + this[0] `should equal` MusicDirectoryChild(id = "10402", parent = "10401", + isDir = false, title = "MVI_0512", album = "Incoming", size = 21889646, + contentType = "video/avi", suffix = "avi", + transcodedContentType = "video/x-flv", transcodedSuffix = "flv", + path = "Incoming/MVI_0512.avi", isVideo = true, + playCount = 0, created = parseDate("2017-11-19T12:34:33.000Z"), + type = "video") } } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiJukeboxControlTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiJukeboxControlTest.kt index 29244674..d072a217 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiJukeboxControlTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiJukeboxControlTest.kt @@ -56,8 +56,8 @@ class SubsonicApiJukeboxControlTest : SubsonicAPIClientTest() { artist = "The Pretty Reckless", track = 2, year = 2014, genre = "Hard Rock", coverArt = "4186", size = 11089627, contentType = "audio/mpeg", suffix = "mp3", duration = 277, bitRate = 320, - path = "The Pretty Reckless/Going to Hell/02 Going to Hell.mp3", isVideo = false, - playCount = 0, discNumber = 1, + path = "The Pretty Reckless/Going to Hell/02 Going to Hell.mp3", + isVideo = false, playCount = 0, discNumber = 1, created = parseDate("2016-10-23T21:30:41.000Z"), albumId = "388", artistId = "238", type = "music") } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt index 0b6f23e3..1a10e6cb 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiPasswordTest.kt @@ -10,8 +10,8 @@ import org.junit.Test class SubsonicApiPasswordTest : SubsonicAPIClientTest() { @Test fun `Should pass PasswordMD5Interceptor in query params for api version 1 13 0`() { - val clientV12 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME, - PASSWORD, SubsonicAPIVersions.V1_14_0, CLIENT_ID) + val clientV12 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, PASSWORD, SubsonicAPIVersions.V1_14_0, CLIENT_ID) mockWebServerRule.enqueueResponse("ping_ok.json") clientV12.api.ping().execute() @@ -25,8 +25,8 @@ class SubsonicApiPasswordTest : SubsonicAPIClientTest() { @Test fun `Should pass PasswordHexInterceptor in query params for api version 1 12 0`() { - val clientV11 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), USERNAME, - PASSWORD, SubsonicAPIVersions.V1_12_0, CLIENT_ID) + val clientV11 = SubsonicAPIClient(mockWebServerRule.mockWebServer.url("/").toString(), + USERNAME, PASSWORD, SubsonicAPIVersions.V1_12_0, CLIENT_ID) mockWebServerRule.enqueueResponse("ping_ok.json") clientV11.api.ping().execute() diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTest.kt index d3a9d69b..cd850480 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTest.kt @@ -39,7 +39,8 @@ class SubsonicApiSearchTest : SubsonicAPIClientTest() { track = 17, year = 2005, genre = "Rap", coverArt = "5766", size = 5607024, contentType = "audio/mpeg", suffix = "mp3", duration = 233, bitRate = 192, - path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3", + path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels" + + ".mp3", isVideo = false, playCount = 0, discNumber = 1, created = parseDate("2016-10-23T20:09:02.000Z"), albumId = "568", artistId = "505", type = "music") diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchThreeTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchThreeTest.kt index 21ee05f6..e9aca121 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchThreeTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchThreeTest.kt @@ -32,20 +32,23 @@ class SubsonicApiSearchThreeTest : SubsonicAPIClientTest() { assertResponseSuccessful(response) with(response.body().searchResult) { artistList.size `should equal to` 1 - artistList[0] `should equal` Artist(id = "505", name = "The Prodigy", coverArt = "ar-505", - albumCount = 5) + artistList[0] `should equal` Artist(id = "505", name = "The Prodigy", + coverArt = "ar-505", albumCount = 5) albumList.size `should equal to` 1 - albumList[0] `should equal` Album(id = "855", name = "Always Outnumbered, Never Outgunned", + albumList[0] `should equal` Album(id = "855", + name = "Always Outnumbered, Never Outgunned", artist = "The Prodigy", artistId = "505", coverArt = "al-855", songCount = 12, duration = 3313, created = parseDate("2016-10-23T20:57:27.000Z"), year = 2004, genre = "Electronic") songList.size `should equal to` 1 - songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", isDir = false, + songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", + isDir = false, title = "You'll Be Under My Wheels", album = "Need for Speed Most Wanted", artist = "The Prodigy", track = 17, year = 2005, genre = "Rap", coverArt = "5766", size = 5607024, contentType = "audio/mpeg", suffix = "mp3", duration = 233, bitRate = 192, - path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3", + path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels" + + ".mp3", isVideo = false, playCount = 0, discNumber = 1, created = parseDate("2016-10-23T20:09:02.000Z"), albumId = "568", artistId = "505", type = "music") @@ -56,9 +59,10 @@ class SubsonicApiSearchThreeTest : SubsonicAPIClientTest() { fun `Should pass query as request param`() { val query = "some-wip-query" - mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json", apiRequest = { + mockWebServerRule.assertRequestParam(responseResourceName = "search3_ok.json", + expectedParam = "query=$query") { client.api.search3(query = query).execute() - }, expectedParam = "query=$query") + } } @Test diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTwoTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTwoTest.kt index 835ac77e..e572090f 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTwoTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSearchTwoTest.kt @@ -33,18 +33,20 @@ class SubsonicApiSearchTwoTest : SubsonicAPIClientTest() { artistList.size `should equal to` 1 artistList[0] `should equal` Artist(id = "522", name = "The Prodigy") albumList.size `should equal to` 1 - albumList[0] `should equal` MusicDirectoryChild(id = "8867", parent = "522", isDir = true, - title = "Always Outnumbered, Never Outgunned", + albumList[0] `should equal` MusicDirectoryChild(id = "8867", parent = "522", + isDir = true, title = "Always Outnumbered, Never Outgunned", album = "Always Outnumbered, Never Outgunned", artist = "The Prodigy", year = 2004, genre = "Electronic", coverArt = "8867", playCount = 0, created = parseDate("2016-10-23T20:57:27.000Z")) songList.size `should equal to` 1 - songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", isDir = false, + songList[0] `should equal` MusicDirectoryChild(id = "5831", parent = "5766", + isDir = false, title = "You'll Be Under My Wheels", album = "Need for Speed Most Wanted", artist = "The Prodigy", track = 17, year = 2005, genre = "Rap", coverArt = "5766", size = 5607024, contentType = "audio/mpeg", suffix = "mp3", duration = 233, bitRate = 192, - path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels.mp3", + path = "Compilations/Need for Speed Most Wanted/17 You'll Be Under My Wheels" + + ".mp3", isVideo = false, playCount = 0, discNumber = 1, created = parseDate("2016-10-23T20:09:02.000Z"), albumId = "568", artistId = "505", type = "music") diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt index c26abd4b..9b543fe9 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/VersionInterceptorTest.kt @@ -40,7 +40,8 @@ class VersionInterceptorTest : BaseInterceptorTest() { client.newCall(createRequest {}).execute() - (interceptor as VersionInterceptor).protocolVersion `should equal` SubsonicAPIVersions.V1_13_0 + (interceptor as VersionInterceptor) + .protocolVersion `should equal` SubsonicAPIVersions.V1_13_0 } @Test diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiVersionCheckWrapper.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiVersionCheckWrapper.kt index e97ecd07..3f7d998d 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiVersionCheckWrapper.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/ApiVersionCheckWrapper.kt @@ -271,7 +271,9 @@ internal class ApiVersionCheckWrapper( return api.getBookmarks() } - override fun createBookmark(id: String, position: Long, comment: String?): Call { + override fun createBookmark(id: String, + position: Long, + comment: String?): Call { checkVersion(V1_9_0) return api.createBookmark(id, position, comment) } diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt index 85887348..e115ccb3 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIDefinition.kt @@ -127,7 +127,8 @@ interface SubsonicAPIDefinition { @Query("comment") comment: String? = null, @Query("public") public: Boolean? = null, @Query("songIdToAdd") songIdsToAdd: List? = null, - @Query("songIndexToRemove") songIndexesToRemove: List? = null): Call + @Query("songIndexToRemove") songIndexesToRemove: List? = null): + Call @GET("getPodcasts.view") fun getPodcasts(@Query("includeEpisodes") includeEpisodes: Boolean? = null, @@ -143,35 +144,39 @@ interface SubsonicAPIDefinition { @Query("submission") submission: Boolean? = null): Call @GET("getAlbumList.view") - fun getAlbumList(@Query("type") type: AlbumListType, - @Query("size") size: Int? = null, - @Query("offset") offset: Int? = null, - @Query("fromYear") fromYear: Int? = null, - @Query("toYear") toYear: Int? = null, - @Query("genre") genre: String? = null, - @Query("musicFolderId") musicFolderId: String? = null): Call + fun getAlbumList( + @Query("type") type: AlbumListType, + @Query("size") size: Int? = null, + @Query("offset") offset: Int? = null, + @Query("fromYear") fromYear: Int? = null, + @Query("toYear") toYear: Int? = null, + @Query("genre") genre: String? = null, + @Query("musicFolderId") musicFolderId: String? = null): Call @GET("getAlbumList2.view") - fun getAlbumList2(@Query("type") type: AlbumListType, - @Query("size") size: Int? = null, - @Query("offset") offset: Int? = null, - @Query("fromYear") fromYear: Int? = null, - @Query("toYear") toYear: Int? = null, - @Query("genre") genre: String? = null, - @Query("musicFolderId") musicFolderId: String? = null): Call + fun getAlbumList2( + @Query("type") type: AlbumListType, + @Query("size") size: Int? = null, + @Query("offset") offset: Int? = null, + @Query("fromYear") fromYear: Int? = null, + @Query("toYear") toYear: Int? = null, + @Query("genre") genre: String? = null, + @Query("musicFolderId") musicFolderId: String? = null): Call @GET("getRandomSongs.view") - fun getRandomSongs(@Query("size") size: Int? = null, - @Query("genre") genre: String? = null, - @Query("fromYear") fromYear: Int? = null, - @Query("toYear") toYear: Int? = null, - @Query("musicFolderId") musicFolderId: String? = null): Call + fun getRandomSongs( + @Query("size") size: Int? = null, + @Query("genre") genre: String? = null, + @Query("fromYear") fromYear: Int? = null, + @Query("toYear") toYear: Int? = null, + @Query("musicFolderId") musicFolderId: String? = null): Call @GET("getStarred.view") fun getStarred(@Query("musicFolderId") musicFolderId: String? = null): Call @GET("getStarred2.view") - fun getStarred2(@Query("musicFolderId") musicFolderId: String? = null): Call + fun getStarred2( + @Query("musicFolderId") musicFolderId: String? = null): Call @Streaming @GET("getCoverArt.view") diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt index 94f68e59..79ccb19b 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIVersions.kt @@ -55,7 +55,8 @@ enum class SubsonicAPIVersions(val subsonicVersions: String, val restApiVersion: } class SubsonicAPIVersionsDeserializer : JsonDeserializer() { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): SubsonicAPIVersions { + override fun deserialize(p: JsonParser, + ctxt: DeserializationContext?): SubsonicAPIVersions { if (p.currentName != "version") { throw JsonParseException(p, "Not valid token for API version!") } diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/RangeHeaderInterceptor.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/RangeHeaderInterceptor.kt index cbb5d031..1e9381b5 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/RangeHeaderInterceptor.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/RangeHeaderInterceptor.kt @@ -36,6 +36,6 @@ internal class RangeHeaderInterceptor : Interceptor { // to avoid the thrashing effect seen when offset is combined with transcoding/downsampling // on the server. In that case, the server uses a long time before sending any data, // causing the client to time out. - private fun getReadTimeout(offset: Int) - = (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE).toInt() + private fun getReadTimeout(offset: Int) = + (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE).toInt() } diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/SearchResult.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/SearchResult.kt index 405feb13..1efade9a 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/SearchResult.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/models/SearchResult.kt @@ -2,6 +2,7 @@ package org.moire.ultrasonic.api.subsonic.models import com.fasterxml.jackson.annotation.JsonProperty -data class SearchResult(val offset: Int = 0, - val totalHits: Int = 0, - @JsonProperty("match") val matchList: List = emptyList()) +data class SearchResult( + val offset: Int = 0, + val totalHits: Int = 0, + @JsonProperty("match") val matchList: List = emptyList()) diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetPlaylistsResponse.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetPlaylistsResponse.kt index 8b31d438..a07c610d 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetPlaylistsResponse.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/GetPlaylistsResponse.kt @@ -16,4 +16,5 @@ class GetPlaylistsResponse(status: Status, get() = playlistsWrapper.playlistList } -private class PlaylistsWrapper(@JsonProperty("playlist") val playlistList: List = emptyList()) +private class PlaylistsWrapper( + @JsonProperty("playlist") val playlistList: List = emptyList()) diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/SearchTwoResponse.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/SearchTwoResponse.kt index 32a90b41..b1a97164 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/SearchTwoResponse.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/response/SearchTwoResponse.kt @@ -5,8 +5,9 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions import org.moire.ultrasonic.api.subsonic.SubsonicError import org.moire.ultrasonic.api.subsonic.models.SearchTwoResult -class SearchTwoResponse(status: Status, - version: SubsonicAPIVersions, - error: SubsonicError?, - @JsonProperty("searchResult2") val searchResult: SearchTwoResult = SearchTwoResult()) +class SearchTwoResponse( + status: Status, + version: SubsonicAPIVersions, + error: SubsonicError?, + @JsonProperty("searchResult2") val searchResult: SearchTwoResult = SearchTwoResult()) : SubsonicResponse(status, version, error) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/APIMusicFolderConverter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/APIMusicFolderConverter.kt index 431f139c..f7c5ebb6 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/APIMusicFolderConverter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/APIMusicFolderConverter.kt @@ -8,5 +8,5 @@ import org.moire.ultrasonic.api.subsonic.models.MusicFolder as APIMusicFolder fun APIMusicFolder.toDomainEntity(): MusicFolder = MusicFolder(this.id, this.name) -fun List.toDomainEntityList(): List - = this.map { it.toDomainEntity() } +fun List.toDomainEntityList(): List = + this.map { it.toDomainEntity() } diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIAlbumConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIAlbumConverterTest.kt index becc7bc8..c75aba42 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIAlbumConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIAlbumConverterTest.kt @@ -15,9 +15,9 @@ import java.util.Calendar class APIAlbumConverterTest { @Test fun `Should convert Album to domain entity`() { - val entity = Album(id = "387", name = "some-name", coverArt = "asdas", artist = "some-artist", - artistId = "390", songCount = 12, duration = 841, created = Calendar.getInstance(), - year = 2017, genre = "some-genre") + val entity = Album(id = "387", name = "some-name", coverArt = "asdas", + artist = "some-artist", artistId = "390", songCount = 12, duration = 841, + created = Calendar.getInstance(), year = 2017, genre = "some-genre") val convertedEntity = entity.toDomainEntity() diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIArtistConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIArtistConverterTest.kt index 0642c9fc..ec1928bf 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIArtistConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIArtistConverterTest.kt @@ -27,7 +27,8 @@ class APIArtistConverterTest { @Test fun `Should convert Artist entity to domain MusicDirectory entity`() { - val entity = Artist(id = "101", name = "artist-name", coverArt = "some-art", albumCount = 10, + val entity = Artist(id = "101", name = "artist-name", coverArt = "some-art", + albumCount = 10, albumsList = listOf(Album(id = "562", name = "some-name", coverArt = "zzz", artist = "artist-name", artistId = "256", songCount = 10, duration = 345, created = Calendar.getInstance(), year = 2011, genre = "Math Rock"))) diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIIndexesConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIIndexesConverterTest.kt index 6ea2b812..2778b926 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIIndexesConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIIndexesConverterTest.kt @@ -16,8 +16,8 @@ class APIIndexesConverterTest { @Test fun `Should convert Indexes entity`() { val artistsA = listOf( - Artist(id ="4", name = "AC/DC"), - Artist(id ="45", name = "ABBA")) + Artist(id = "4", name = "AC/DC"), + Artist(id = "45", name = "ABBA")) val artistsT = listOf( Artist(id = "10", name = "Taproot"), Artist(id = "12", name = "Teebee")) diff --git a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIMusicDirectoryConverterTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIMusicDirectoryConverterTest.kt index 064bf539..4d8cdd73 100644 --- a/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIMusicDirectoryConverterTest.kt +++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/data/APIMusicDirectoryConverterTest.kt @@ -37,7 +37,8 @@ class APIMusicDirectoryConverterTest { transcodedContentType = "some-transcoded-content-type", transcodedSuffix = "some-transcoded-suffix", duration = 11, bitRate = 256, path = "some-path", isDir = true, isVideo = true, playCount = 323, discNumber = 2, - created = Calendar.getInstance(), type = "some-type", starred = Calendar.getInstance()) + created = Calendar.getInstance(), type = "some-type", + starred = Calendar.getInstance()) val convertedEntity = entity.toDomainEntity() From b5c0fa10f327362a3fa87d8c82fe2f7992b1670e Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 10:52:43 +0100 Subject: [PATCH 09/29] Update kotlin to 1.2.10 version. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index aea34b15..3692f2d1 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -13,7 +13,7 @@ ext.versions = [ androidSupport : "23.4.0", - kotlin : "1.1.60", + kotlin : "1.2.10", retrofit : "2.1.0", jackson : "2.9.0", @@ -41,7 +41,7 @@ ext.androidSupport = [ ext.other = [ kotlinStdlib : "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin", - kotlinReflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin", + kotlinReflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin", retrofit : "com.squareup.retrofit2:retrofit:$versions.retrofit", gsonConverter : "com.squareup.retrofit2:converter-gson:$versions.retrofit", jacksonConverter : "com.squareup.retrofit2:converter-jackson:$versions.retrofit", From a86925d0b82efebb4c780c6bda382372934eceeb Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 21 Dec 2017 11:37:39 +0100 Subject: [PATCH 10/29] Delete old api resources. Delete resources that was dedicated to pre 14 api level. Signed-off-by: Yahor Berdnikau --- .../drawable-hdpi-v11/ic_stat_ultrasonic.png | Bin 673 -> 0 bytes .../res/drawable-hdpi-v9/ic_stat_ultrasonic.png | Bin 715 -> 0 bytes .../res/drawable-hdpi/ic_stat_ultrasonic.png | Bin 729 -> 673 bytes .../drawable-ldpi-v11/ic_stat_ultrasonic.png | Bin 382 -> 0 bytes .../res/drawable-ldpi-v9/ic_stat_ultrasonic.png | Bin 355 -> 0 bytes .../res/drawable-ldpi/ic_stat_ultrasonic.png | Bin 390 -> 382 bytes .../drawable-mdpi-v11/ic_stat_ultrasonic.png | Bin 480 -> 0 bytes .../res/drawable-mdpi-v9/ic_stat_ultrasonic.png | Bin 497 -> 0 bytes .../res/drawable-mdpi/ic_stat_ultrasonic.png | Bin 472 -> 480 bytes .../drawable-xhdpi-v11/ic_stat_ultrasonic.png | Bin 926 -> 0 bytes .../drawable-xhdpi-v9/ic_stat_ultrasonic.png | Bin 937 -> 0 bytes .../res/drawable-xhdpi/ic_stat_ultrasonic.png | Bin 930 -> 926 bytes 12 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ultrasonic/src/main/res/drawable-hdpi-v11/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-hdpi-v9/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-ldpi-v11/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-ldpi-v9/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-mdpi-v11/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-mdpi-v9/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-xhdpi-v11/ic_stat_ultrasonic.png delete mode 100644 ultrasonic/src/main/res/drawable-xhdpi-v9/ic_stat_ultrasonic.png diff --git a/ultrasonic/src/main/res/drawable-hdpi-v11/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-hdpi-v11/ic_stat_ultrasonic.png deleted file mode 100644 index 48ffc78e24bf8df61cdcb6f426c7236ef874b797..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmV;S0$%-zP)N2bPDNB8 zb~7$DE-^4L^m3s900JFJL_t(o!|m8lh)z)$$MN?a^S(3_`8z2KLzAU!SfEhUNH(Gr z8@rQWP5t8w(cHn2=)B>*n=2(S6IdH|R}%*c z^CRf01E?R9B|z8X7orzWqxo?xs$;o^66QS`FG>ss2P56*vP@Q~&X*HFQ&l9EN z-@`FvJ53dF5J!9z3pS z#Mqjll4l900000NkvXX Hu0mjfzmp{j diff --git a/ultrasonic/src/main/res/drawable-hdpi-v9/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-hdpi-v9/ic_stat_ultrasonic.png deleted file mode 100644 index 87a40aab2894590ed0da13f0c26f4b4918e9fa14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmV;+0yO=JP)N2bPDNB8 zb~7$DE-^4L^m3s900KuzL_t(Y$JN(6XcSQt2H?#m@sUJ)Yg8gCRw4@GqZ3gO6w%T~ z&?4Aaiin{2fe4~lSy&080SVYBYEoE=l~}|_k#qqS5g#dHd?q$G%VfNFv$NSk2M){3 zocsTC&pH3OXG^7j=|TxjD2LyMfi`0ycHuI5aV6Zh)=drO7gdLvB zy&phFEnmMU#5GUw0IeC{1$dpy#rEk4T5u;3nv>ab3_3WS^Ep?wWU(9{69hXW{u^?I z(3jn1EIfx2;NQ^|Su+nq8NXr7PZ2Z)b;bzZV^Kngy&1n>SXV9n{Rx7hScQaLAICj41!?AzESt z%kZTjLCoG^%alZiSZ+-;xq9ry6k!ID^)v{4(z+|V2=O0jFhNhHny{_G;1jrCsirVF zG@9UgMQ~V?llx3<9Q(65Xs^=f>aqSQ%`uz>ml_2AEJWll9jwRa8H8jI7qBGNP0L}n zsfVC1%&z6>riu>SD+oRu4*!~`x}{Z)&iqXJ7#oYur6tVA)m)#?+87RFZe62$AZZB# x>)b#)W002ovPDHLkV1f?ZHpl<~ diff --git a/ultrasonic/src/main/res/drawable-hdpi/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-hdpi/ic_stat_ultrasonic.png index 42d70a45812a258219d4ead674cbdbf1b306a9fe..48ffc78e24bf8df61cdcb6f426c7236ef874b797 100644 GIT binary patch delta 608 zcmV-m0-yca1)&8YiBL{Q4GJ0x0000DNk~Le0000a0000a2nGNE0O0_bn2{k)e;r9g zK~z}7?buI?O=h8_ zY^Equ6dMa03l`LvkYd#2?`Lez;&i)bGVZyrl2cE&?)lyBx99xM^PGDdjel{VfiASd z^{+v+u@Z-I0}nBRU1)U@W)22%e;-qrF2oNEqq`F@-8hD?B_MHp!_Hcl4^8X~FDn9k z!t=ttC+MxyGyQmzfZ2}$ye`ZQ){>>*n=2(S6IdH|R}%*c^CRf01E?R9B|z8X7orzW zqxo?xs$;o^66QS`FGCYWTanGH}E zZe|36fvP~G8G**Ju&RenWdu53x$9$NqOQy~o?&_AT7(`vu4v>jtj<{&cmLtm9}5|t zm;1!nnxW&TVghUG`UGlVLE#iQQ@jYL%x0sm5kXxHT|L$9Phx3DHvWJFs8;CQckm^G ud50q%`N5C?Yln}=?bwQCwJy>B5yme<#I-={6V2uT00005e^p6D zK~z}7?bthL6j2m_;ms!T5m$@}J`xEcVk4-9XtWVQK?D)3ECj(qZR`{T1wjzOS1H&C zf(A@uA!0CKCw5x+Dq8n?G4z%ZQycIx$3i!-8{?_>rwX+i=3yuUXac8E3l=?*`u-fXBCWME#7!FP9@-PS zop_aUaepBhiZ>jv8(?0EWxtm zEjC6;fDkL2X5q>|$qRJHDy{>Opz3m@X{&|!`WzW-py}+y=EzX>{;joHu&o7vU2D3~ zVpY!I(WnSTxak=-ZBExxyej~lug-B3Of(N42u{HSAgzH9dZVpRgcNf(#o;3nC z6z(>vMNLUH+?^%VXc$TgpTd`hX=iE%cNPz^uB>~D3KrqYG>zjV_G5mjS5N`24O>Fv yHx>f?JDy^1)Sjz}OS`U60{YNVitGNbjP)DR-iX%!SpD7r0000M;ob diff --git a/ultrasonic/src/main/res/drawable-ldpi-v11/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-ldpi-v11/ic_stat_ultrasonic.png deleted file mode 100644 index 4ae0a39f697085b6bdff67a1d29db7ab1b899eb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|mUKs7M+SzC{oH>NS%G}c0*}aI z1_r)^Ak4U9V)k30pj3%#L`iUdT1k0gQ7S_~VrE{6o}X)oLYc9ish**M!Iz?iKvlmy zT^vI!PA4ZQF#P-boBfcF!YmzzD z6>@-CbfvQ7C)tjj6PW(12<&G`3QOWCayELwF7Ww448xLe=8y6U&y_@O7T zo;gWd;U1spWVK@o{wB>E60MVx)--Z7uVXOeUU;Qu9!Il-!-NUlenN>CV)y7ZNnP_f zFk!pH8y^E*M$rzYkHRu-IV_c(H&{3i9dr02q3e^zXlQS_Q+|r-sRcjvq(lUHzA!MX Y%T``c9sq|Hs=OMNKhnH$=N~bPRsc$@)v|*px)AXO}K5T9?XXuIr zi7|7td3F}}KU}^0wcpR0ZQp;tTUnmZx4N2DVZ`+bQkSNBMPI4)EMIoV z%lobQ@}ODW8+2rUiYnVJUG|WJ`JSzoc#@;n`d`XZVlt$3kF-sh*JaIqDMhht$(Kam z*!jAd`vh)px>|hwlGnz>{mvq`!F;0pwn}GPHrf|N@r35v{x}-I(H{Hb*p?2C$MdW| xe0=|oW8t$nulr`9i)9lptg_wg^)bGV;nT-`JDHvbt_6CP!PC{xWt~$(697NKif;e_ diff --git a/ultrasonic/src/main/res/drawable-ldpi/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-ldpi/ic_stat_ultrasonic.png index 034c5dd9fc5df724344d21b5e65ccf44b2efd74b..4ae0a39f697085b6bdff67a1d29db7ab1b899eb9 100644 GIT binary patch delta 316 zcmV-C0mJ@=1O5UbiBL{Q4GJ0x0000DNk~Le0000I0000I2nGNE09MY9SCJu2e*pSP zL_t(I%VS_50RH{^2f|Dsnk)d+$OyzPKs*nKZv*jeAXX&R5D+f-2Lbq48+W& z7^DxxKcSir0v2<>`X8% z0TqY>@e>?|Yy^3U*aQi}#z6cSElg$sF+VxEACx7^p!qHu6cG&c006FQ5+kg~pt;ik O0000Ofs#R>KkSK^kq1$5P1N20p)hS5Wg3s7S zLe`#9xSPBPnKxUw$<55Xmz=zN=gt&GMxa26V&cF8_Hcy~2zCH`zOmwRSF6;wixnlD(~1>*Zpz7$jIH z*;|bAZ?ncE#$l9T*Vx4}<}t)0%3SO-!NxhjW!%4J&0CZ*`~^$%1KT;+ehoCvyV_E5 zk%{$lu%k@ur1p(swWeop=wU8*XwUzE-m%%}SA?1Pq|$kNZVdf6;W|!n8i=LhKjRA= WgHM~At?sA*0000N2bPDNB8 zb~7$DE-^4L^m3s900CS{L_t(Y$75g^4B+3te;~{b#HK)81;k}QtPB#ThT%*=oCL)8 zf%rcXehb7#6j=gwc?J;wMKbs$68|_5vr=M#CJ=u?GW-G%YXNZ|l9&UzhBE^3A|!+U zK@A7V2||4W5}!_1Fab3Q0r4j!3m$_a8Y$>zL&dKHF$*~s_@hPYVUVwoFYz2y!BLH<|?pfmjWQxuFhV0phz* zxmG-LfI z0WTxk7eIwYBpMD&1dPO5kVS?CQ%LcFF^NG0N?9tT!~qMmC?;g^Paxg{#D=7%A`rHO zrVe}-fND1;N-CQu9F}|p;zo)p8zf82fOtO;|A88QA6hq4wbEyXmY%vm%tckhDFpyA WiJX~^d36i`0000X1^@s6IQ*`u00004XF*Lt006O% z3;baP00009a7bBm000ia000ia0czHX2><{98gxZibW?9;ba!ELWdKlNX>N2bPDNB8 zb~7$DE-^4L^m3s900C`DL_t(I%eB-$OI$$|1@O1(YNtt13$?P)A^{r%DFh3f=qIpC zt)(bwe}!Ucr%ezn?X_?C@B31s_~^Z|XM*awV7NM zXv0CYv5e6`+>P_FzK@nXg0G46Q*=b?R>JxNdh_HjMc@i{QHb_TM!oNO=KW3xHsV>s z7`LIfkP>tR$uYrdvZn=yp|@6JB)V{y5UhvMVwF3gx0MofC!wj}4BJ@5a6+&b`;*}l zo)Ut%?y!l!;rpoODDfd@gnD&_&;Q2=X=@Z*tpl`G&DnYb!3+AU$5VO(K^X(-@ibE< z6ss|wYZ82ovDm~OTr>cD2m$+&1}Vcdo?jtYt_e_1)@Q?CQs4#VQGD$_EZ|!(^*%U# nj1i4osEL}7=tFx`;AWqH=)PdJbwE*d00000NkvXXu0mjf^(N0g diff --git a/ultrasonic/src/main/res/drawable-mdpi/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-mdpi/ic_stat_ultrasonic.png index c5cbeefdf0976c2412997bb7331d21109f74000f..aa3e66b7deb20e1ea12c33d97dc66960fe18696d 100644 GIT binary patch delta 413 zcmV;O0b>5x1Kmz_e_TmK zK~zY`V_+Bz;NQQ0Aj}TLra)W;#AQIN3=*e?;Y>iB1jP4&_&*YU3&chgSps!=1`z*6 zGWaDD|2Pn{QeuH75Pv~3`~nbb0dXIam;FYz2y!BLH<|?pfmjWQxuFhV0phz*xmG-LfI0WTxk7eIwYb0iuLO9YI> zT98GC1ye}zfia0e1WH*dq{IOWv?wNI@J}G#1H^`;rXmowgr*LB7JzCuCQ2%sC>)l2 z1mZ@DDjOtA%z$`55dVQ1eji#lQ?=4(hL)bXK+Hu|!zl#-F^Qa+j(K$q00000NkvXX Hu0mjf1K0y0iBL{Q4GJ0x0000DNk~Le0000P0000P2nGNE0L1BqW04_Fe^g0C zK~zY`&DOswc2OJv@W<~Muu!PiK#XjJ)N6eIfY)q(vyha*Apd~Lt_*A=9cVx&eqkk?Tk17<1%24TJ*rXL ztqX)+Tond?imn$>3TVP!G$8C_e+vIX>=LbIkQw}skOMS_6mCTEi82$tQ%KWd)c*s| zA-2K(U60T6@V&63af4_#ILhf8GhG52Htt*5nVaRQVnD%&OPR$ zzQG)1y#{g?7Q;N+3dnd4vQ`6$+j&6W?x%B* diff --git a/ultrasonic/src/main/res/drawable-xhdpi-v11/ic_stat_ultrasonic.png b/ultrasonic/src/main/res/drawable-xhdpi-v11/ic_stat_ultrasonic.png deleted file mode 100644 index 039dc1491dc641091198910aa0ec324ed565ba2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 926 zcmV;P17ZA$P)N2bPDNB8 zb~7$DE-^4L^m3s900SCHL_t(&-tE~zsMJ*y$MJddOrJiVzD!YGx=33@D{Uo2C1kir z6459@!U}6AL~0aBQ4kS93#oQixH3dC64AiGg+-u()h0;{gs`F{!Lal*Ejk0kpZ|Ym zo^S4*F}!d$^S^igzwiH^bMCq4)@rxf|FofoVa&y1Okga28)(tfu@I~A4o=|@{DQCX z66RxQ(1cIJ3Vev4{MQ;4{Yb3Hz=$8gv-rJ@=wjcC*?ot=ZFnA6Cn3Ip>v#h7eu^K$ z!}zm}&}-O_D<#jq!MuK&&IHbM_{wi%9(I=a1+43fLx<_S*+%?%JkzvujN@?0xKA+B zkFUQhLSq%cj@FnT=5{~+DvbLTOR_q12;0?0{NGq!6opwhSTg?c+yJ-ZbQ^#lFu%g- zH%jl3zrJxzrt89fO-e>X<#>oE9Q@d;MVcKVR0^i5gh6O z@N!jXjFjVpk~u50_yHE+TnB)yRbs-7Hk8cSk_+JOl2BOe8#(|iDG9-^Cr>Nl$3}3m;>O&DF8IuoRu$PB1aT@Zi)a^{Yr%Bdg>L}o(Q1n2lpo1kt2S8mJpzU z0~k$ZK99s#NwNMTh9;rk;k9R1n^eRojEq4vzY*1O93#B?=t~x zOyL7F@kIvl7jS0^fZ?dJr3QE<$+;lH=&C3erV@WJd@b2VHXfH0y%v5AA7fF{b8(~+ zn|ink7qJhI#x9?ws5J+l_CWkUQJJ2RwK^RT_eA0~6@d3Kt6z5mbv%ThZ$S9-AEE)d!-G+pYK$QPmh5lncIk6uS0xDE2llG8V*~fw^IA`ot*KM$PncLHH%S8{%sN z+ghp(%kUVUjtbq<{98gxZibW?9;ba!ELWdKlNX>N2bPDNB8 zb~7$DE-^4L^m3s900SjSL_t(o!|m8jh}KmU$MKnG=F!n*<-bMEcy`~REahd%UUHZH=2@pC`ADq6!Zmf;b+i7)X5 z-ohy6p}*6@16YL37{_mzEEpeQX-6PJd^XnMV2Nn+7{~m!-GN!yfInIgpBh_HZ%4d_ z2XHbYbfVz*hjI0cb|JQYMTKqu5%=PQg1-xDp2va}Hu*OS=%c zd>2lah`$|KQ45=aNXxT{x$9cW2+YMhCF19BPvfe>Wf$Yq#Qaw=s}TzXV5`=AXgpN^uZp`o0Xn?xM(!;2lrcJf4k%Siw~l@_tj% zKZ_mQmaus#n~o9v>noga>x;HC7V%)h=984$QuI420NkAcSd3p2=fNd8fFZn_5kH6P z%K{2>@LrSM?^s?X0QXk_xVFMhjwS4FD$N93ibEA#a6@JXYIrJP_fQUCF^*LLxIF{V zk0%p$kCmokq~NIv0HatCt=F7baWlXZ3A-&hfK`~N0B|_U@V9tBR=X7ShGunR!fxAC z@oETw=}`20Gz_vhg|g;%LjKVHA>MZ32{y*GtQCG#E#^2=D`LPR2n) zA8@*h0F(GG0`@$BI-cpW_|$k|TD#uRNc_?xfJ0c=d~iJ4Bk}X=LKKf(Jp`D<+D3pn zUh6Ty9gP5kc%_Hp$FZ!rd~WR_z=zY?v&e!qJre&5_fM;e0dQGVxNZZC#my`E4)Agp zg(t8ldWgbq_Qnc#f>y^Fd=?kikz7kQ)Zz2sYupl-)>`&z7Q1i-zUqMZ-*~vR11jlg zZ*8+KoW--rV5G(ZIlkMWBM!CP@s0u%aZvu=F!6Is?O>|9@tlZ|@Bf~2 z?z!jIYPZ_|w4sJ!%*A3%U@U$ce`wLuu@I~A4o=|@{DQCX66RxQ(1cIJ3Vev4{MQ;4 z{Yb3Hz=$8gv-rJ@=wjcC*?ot=ZFnA6Cn3Ip>v#h7eu^K$!}zm}&}-O_D<#jq!MuK& z&IHbM_{wi%9(I=a1+43fLx<_S*+%?%JkzvujN@?0xKA+BkFUQhLSq%ce~#9eALe#H z{wj?76-%-@a|qkjM*QDcUKE8{I9M|N@!SBn<8&K!@Z zm)Uj9y$hELJ`|Y2%zZ)_a@tsBYuCD5TJnr7)@n9kHldmCBRkO z*Yj0~Gw`0o;+uV2&ky>M$Dhmu@K(>AIX0oQnE+l(0Wgm5GXZQ&;R7@AMF#N~aAyjD z;i$5u26!dOxgf&mf2t@KrV@WJd@b2VHXfH0y%v5AA7fF{b8(~+n|ink7qJhI#x9?w zs5J+l_CWkUQJJ2RwK^RT_eA0~6@d3Kt6z5mbv%ThZ$S9-AEE)d!-G+pYK$ zQPmh5lncIk6uS0xDE2llG8V*~fw^IA`ot*KM$PncLHH%SH5=k<1KV1v4a@Kto{kFL p(zyJt-83y$rFK(^zSVB9{R_5@UB9WNlRW?c002ovPDHLkV1idaoW=kE delta 867 zcmV-p1DyPx2cicdiBL{Q4GJ0x0000DNk~Le0000o0000o2nGNE03JVxv5_H8e;!Fh zK~!ko?b%PL)KwI~@tHT1&*#4|3^&>=_@cYg{QG6h>gcJ2Nj;AX zZ6Ho!9`@yY{}{DSf{ej|7660z9&=E|w18?J_Y*FbNpyxYLFgmY~$dE(U-Fc z&*Dnc_<`7*q#$E)v<=9Pyhs#5;z-7vw^M`6z|}S&@8?}VBKK^~n6oum7!K2Tqd>-f zCvSJ-KbB<7IfUMnVfVuVe;}{7GLy(D7c(5bKNUzFy9dA5V1xX@TRCebIfGiDvs$?xJK(=Jedn*;l6#QNQ=ozdC(>tE= zeM2gcqCV0}wfMFv70ARelcADyZ`|dQoX9UFf((XvjVcqOf|u}5i6BF`j)kS2N@Vq~ zONMCZ{!%WG_jF9ICjvQNN<$(*mM4N3!rz!#21pejBm^12tp5YZZ*hRe;>*4=L8_1V{DlTv5sveLmY}yq15Y11ux=S2>_R|IoZQcJnC#8 zMu4+e9lf$Ak~Qe!RHhHp4fs}6khM;A;(Mag{^x%L*@C$57A_1&*6(Ubqef?7p(39$Or&qIq)jJ#ILdY4TnA*-TCf? t$N=h%gf=$ Date: Thu, 21 Dec 2017 11:53:15 +0100 Subject: [PATCH 11/29] Regenerate lint baseline. Also have to fix some of the lint issues, for some reason they don't get into baseline. Signed-off-by: Yahor Berdnikau --- ultrasonic/lint-baseline.xml | 2166 ++++++----------- .../service/DownloadServiceImpl.java | 1 + .../src/main/res/layout-port/download.xml | 2 +- ultrasonic/src/main/res/values-fr/strings.xml | 2 +- ultrasonic/src/main/res/values-pt/strings.xml | 20 +- 5 files changed, 704 insertions(+), 1487 deletions(-) diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml index bdd2edb8..916b6f0c 100644 --- a/ultrasonic/lint-baseline.xml +++ b/ultrasonic/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -41,7 +41,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2=" ~~~~~~~~~~~~~~~"> @@ -258,50 +258,6 @@ column="43"/> - - - - - - - - - - - - - - - - @@ -353,7 +309,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -364,7 +320,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -375,103 +331,19 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + id="ShowToast" + message="Toast created but not shown: did you forget to call `show()` ?" + errorLine1=" toast = Toast.makeText(context, message, shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);" + errorLine2=" ~~~~~~~~~~~~~~"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + line="551" + column="12"/> @@ -558,7 +430,7 @@ errorLine2=" ~~~~"> @@ -591,7 +463,7 @@ errorLine2=" ~~~~"> @@ -602,7 +474,7 @@ errorLine2=" ~~~~"> @@ -610,33 +482,33 @@ id="LongLogTag" message="The logging tag can be at most 23 characters, was 31 (Exception in CacheCleaner.clean)" errorLine1=" Log.w("Exception in CacheCleaner.clean", ex);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + column="10"/> + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + column="10"/> + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + column="10"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + id="EllipsizeMaxLines" + message="Combining `ellipsize=marquee` and `maxLines=1` can lead to crashes. Use `singleLine=true` instead." + errorLine1=" android:maxLines="1"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + column="17"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + message="This folder configuration (`v14`) is unnecessary; `minSdkVersion` is 14. Merge all the resources in this folder into `drawable-hdpi`."> + file="src/main/res/drawable-hdpi-v14"/> + message="This folder configuration (`v14`) is unnecessary; `minSdkVersion` is 14. Merge all the resources in this folder into `drawable-mdpi`."> + file="src/main/res/drawable-mdpi-v14"/> + message="This folder configuration (`v14`) is unnecessary; `minSdkVersion` is 14. Merge all the resources in this folder into `drawable-xhdpi`."> + file="src/main/res/drawable-xhdpi-v14"/> + id="StaticFieldLeak" + message="This AsyncTask class should be static or leaks might occur (org.moire.ultrasonic.activity.BookmarkActivity.GetDataTask)" + errorLine1=" private class GetDataTask extends AsyncTask<Void, Void, String[]>" + errorLine2=" ~~~~~~~~~~~"> + file="src/main/java/org/moire/ultrasonic/activity/BookmarkActivity.java" + line="467" + column="16"/> + id="StaticFieldLeak" + message="This AsyncTask class should be static or leaks might occur (org.moire.ultrasonic.util.CacheCleaner.BackgroundCleanup)" + errorLine1=" private class BackgroundCleanup extends AsyncTask<Void, Void, Void>" + errorLine2=" ~~~~~~~~~~~~~~~~~"> + file="src/main/java/org/moire/ultrasonic/util/CacheCleaner.java" + line="232" + column="16"/> + id="StaticFieldLeak" + message="This AsyncTask class should be static or leaks might occur (org.moire.ultrasonic.util.CacheCleaner.BackgroundSpaceCleanup)" + errorLine1=" private class BackgroundSpaceCleanup extends AsyncTask<Void, Void, Void>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/java/org/moire/ultrasonic/util/CacheCleaner.java" + line="266" + column="16"/> + id="StaticFieldLeak" + message="This AsyncTask class should be static or leaks might occur (org.moire.ultrasonic.util.CacheCleaner.BackgroundPlaylistsCleanup)" + errorLine1=" private class BackgroundPlaylistsCleanup extends AsyncTask<List<Playlist>, Void, Void>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/java/org/moire/ultrasonic/util/CacheCleaner.java" + line="301" + column="16"/> + id="StaticFieldLeak" + message="This AsyncTask class should be static or leaks might occur (org.moire.ultrasonic.activity.ChatActivity.GetDataTask)" + errorLine1=" private class GetDataTask extends AsyncTask<Void, Void, String[]>" + errorLine2=" ~~~~~~~~~~~"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + file="src/main/java/org/moire/ultrasonic/activity/ChatActivity.java" + line="273" + column="16"/> + errorLine2=" ~~~~~~"> + column="10"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + errorLine2=" ~~~~~~"> + column="12"/> + errorLine2=" ~~~~~~"> + column="10"/> + errorLine2=" ~~~~~~"> - - - - - - - - - - - - - - - - + column="10"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2131,6 +1303,316 @@ column="1"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - @@ -2222,28 +1660,6 @@ file="src/main/res/drawable-hdpi/ic_stat_play_light.png"/> - - - - - - - - - - @@ -2255,105 +1671,6 @@ column="30"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2402,13 +1719,6 @@ file="src/main/res/drawable-hdpi/list_pressed_holo_dark.9.png"/> - - - - - - - - + message="Custom view ``AutoRepeatButton`` has `setOnTouchListener` called on it but does not override `performClick`" + errorLine1=" this.setOnTouchListener(new OnTouchListener()" + errorLine2=" ^"> + line="35" + column="3"/> + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.00 Go 0 Ko 0.00 Mo - -:-- + —:—— 0:00 MX Player n\'est pas installé. Recevez gratuitement sur Play Store, ou modifier les paramètres vidéo. Obtenez MX Player diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 5ce59f3a..64d0035c 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -1,5 +1,5 @@ - + Carregando… Ocorreu um erro de rede. Verifique o endereço do servidor ou tente mais tarde. @@ -328,7 +328,7 @@ 0.00 GB 0 KB 0.00 MB - -:-- + —:—— 0:00 O player MX não está instalado. Descarregue da graça pela Play Store ou modifique as configurações de vídeo. Descarregar Player MX @@ -393,33 +393,33 @@ albumArt Múltiplos Anos - + Nenhuma música - 1 música + %d música %d músicas - 1 música selecionada para ser fixada. + %d música selecionada para ser fixada. %d músicas selecionadas para serem fixadas. - 1 música selecionada para descarregar. + %d música selecionada para descarregar. %d músicas selecionadas para serem descarregadas. - 1 música selecionada para ser desafixada. + %d música selecionada para ser desafixada. %d músicas selecionadas para serem desfixadas. - 1 música adicionada ao fim da fila. + %d música adicionada ao fim da fila. %d músicas adicionadas ao fim da fila. - 1 música inserida após a atual. + %d música inserida após a atual. %d músicas inseridas após a atual. - Resta 1 dia para o fim do período de teste + Resta %d dia para o fim do período de teste Restam %d dias para o fim do período de teste From c84482729d216d2b2f1c104f7b89de68806e17e5 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 27 Dec 2017 21:38:40 +0100 Subject: [PATCH 12/29] Add versioning plugin. This will allow to easy automate auto publishing of the app. Signed-off-by: Yahor Berdnikau --- build.gradle | 1 + dependencies.gradle | 12 +++++++----- ultrasonic/build.gradle | 18 ++++++++++++++++++ ultrasonic/src/main/AndroidManifest.xml | 4 +--- ultrasonic/version.properties | 2 ++ 5 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 ultrasonic/version.properties diff --git a/build.gradle b/build.gradle index 25ea3d17..97610b36 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ buildscript { classpath gradlePlugins.ktlintGradle classpath gradlePlugins.detekt classpath gradlePlugins.jacocoAndroid + classpath gradlePlugins.buildVersioning } } diff --git a/dependencies.gradle b/dependencies.gradle index 3692f2d1..ff608274 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -10,6 +10,7 @@ ext.versions = [ detekt : "1.0.0.RC6", jacoco : "0.7.9", jacocoAndroid : "0.1.2", + buildVersioning : "1.6.0", androidSupport : "23.4.0", @@ -27,11 +28,12 @@ ext.versions = [ ] ext.gradlePlugins = [ - androidTools : "com.android.tools.build:gradle:$versions.androidTools", - kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin", - ktlintGradle : "gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle", - detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt", - jacocoAndroid : "com.dicedmelon.gradle:jacoco-android:$versions.jacocoAndroid" + androidTools : "com.android.tools.build:gradle:$versions.androidTools", + kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin", + ktlintGradle : "gradle.plugin.org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle", + detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt", + jacocoAndroid : "com.dicedmelon.gradle:jacoco-android:$versions.jacocoAndroid", + buildVersioning : "org.moallemi.gradle.advanced-build-version:gradle-plugin:$versions.buildVersioning", ] ext.androidSupport = [ diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 822c2243..b2d2e8cd 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -1,13 +1,31 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'jacoco-android' +apply plugin: 'org.moallemi.advanced-build-version' apply from: "../gradle_scripts/code_quality.gradle" +advancedVersioning { + nameOptions { + versionMajor 2 + versionMinor 0 + versionPatch 0 + } + codeOptions { + versionCodeType org.moallemi.gradle.internal.VersionCodeType.AUTO_INCREMENT_ONE_STEP + } + outputOptions { + renameOutput true + } +} + android { compileSdkVersion versions.compileSdk defaultConfig { applicationId "org.moire.ultrasonic" + versionCode advancedVersioning.versionCode + versionName advancedVersioning.versionName + minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index d24a6d8d..3d7bad42 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -1,9 +1,7 @@ + a:installLocation="auto"> diff --git a/ultrasonic/version.properties b/ultrasonic/version.properties new file mode 100644 index 00000000..61c77636 --- /dev/null +++ b/ultrasonic/version.properties @@ -0,0 +1,2 @@ +#Tue Dec 26 22:52:35 CET 2017 +AI_VERSION_CODE=59 From ea3d36110d46b0934c2e9b7562cd022ce1f4e936 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 27 Dec 2017 22:25:59 +0100 Subject: [PATCH 13/29] Add supported (tested) subsonic api implementations in README. Signed-off-by: Yahor Berdnikau --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b1d44331..2b2277bc 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,15 @@ otherwise open [a new issue](https://github.com/ultrasonic/ultrasonic/issues/new See [CONTRIBUTING](CONTRIBUTING.md). +## Supported (tested) Subsonic API implementations + +- [Subsonic](http://www.subsonic.org/pages/index.jsp) +- [Airsonic](https://github.com/airsonic/airsonic) +- [Supysonic](https://github.com/spl0k/supysonic) + +Other *Subsonic API* implementations should work as well as long as they follow API +[documentation](http://www.subsonic.org/pages/api.jsp). + ## License This software is licensed under the terms of the GNU General Public License version 3 (GPLv3). From 70c2e5213c8192a3b958885c62722ade89b50328 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Wed, 27 Dec 2017 22:28:36 +0100 Subject: [PATCH 14/29] Fix checkstyle problem. Signed-off-by: Yahor Berdnikau --- .../org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt index 06ba4668..f67ae2e8 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiSSLTest.kt @@ -48,8 +48,8 @@ class SubsonicApiSSLTest { cert = (CertificateFactory.getInstance("X.509") .generateCertificate(certificatePemStream)) as X509Certificate } - val alias = cert?.subjectX500Principal?.name ?: - throw IllegalStateException("Failed to load certificate") + val alias = cert?.subjectX500Principal?.name + ?: throw IllegalStateException("Failed to load certificate") trustStore.setCertificateEntry(alias, cert) val tmf = TrustManagerFactory.getInstance("X509") From e96eae29d5d3013e38071ed2e4b0901d469f24f3 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Thu, 28 Dec 2017 22:02:09 +0100 Subject: [PATCH 15/29] Add option to resume playback on headphones insertion. This is disabled by default and should be enabled in app settings. Playback resume only happened when app is not in Jukebox mode and playback is paused. Signed-off-by: Yahor Berdnikau --- .../DownloadServiceLifecycleSupport.java | 71 ++++++++++++------- ultrasonic/src/main/res/values-es/strings.xml | 2 + ultrasonic/src/main/res/values-fr/strings.xml | 2 + ultrasonic/src/main/res/values-hu/strings.xml | 2 + .../src/main/res/values-pt-rBR/strings.xml | 2 + ultrasonic/src/main/res/values-pt/strings.xml | 2 + .../res/values/playback_preferences_keys.xml | 4 ++ ultrasonic/src/main/res/values/strings.xml | 2 + ultrasonic/src/main/res/xml/settings.xml | 6 ++ 9 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 ultrasonic/src/main/res/values/playback_preferences_keys.xml diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/DownloadServiceLifecycleSupport.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/DownloadServiceLifecycleSupport.java index 79f64e76..d9af9881 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/DownloadServiceLifecycleSupport.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/DownloadServiceLifecycleSupport.java @@ -18,17 +18,22 @@ */ package org.moire.ultrasonic.service; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.media.AudioManager; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; import android.view.KeyEvent; +import org.moire.ultrasonic.R; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.util.CacheCleaner; @@ -128,32 +133,9 @@ public class DownloadServiceLifecycleSupport executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleWithFixedDelay(downloadChecker, 5, 5, TimeUnit.SECONDS); - // Pause when headset is unplugged. - headsetEventReceiver = new BroadcastReceiver() - { - @Override - public void onReceive(Context context, Intent intent) - { - Bundle extras = intent.getExtras(); + registerHeadsetReceiver(); - if (extras == null) - { - return; - } - - Log.i(TAG, String.format("Headset event for: %s", extras.get("name"))); - if (extras.getInt("state") == 0) - { - if (!downloadService.isJukeboxEnabled()) - { - downloadService.pause(); - } - } - } - }; - downloadService.registerReceiver(headsetEventReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - - // Stop when SD card is ejected. + // Stop when SD card is ejected. ejectEventReceiver = new BroadcastReceiver() { @Override @@ -202,7 +184,44 @@ public class DownloadServiceLifecycleSupport new CacheCleaner(downloadService, downloadService).clean(); } - public void onStart(Intent intent) + private void registerHeadsetReceiver() { + // Pause when headset is unplugged. + final SharedPreferences sp = Util.getPreferences(downloadService); + final String spKey = downloadService + .getString(R.string.settings_playback_resume_play_on_headphones_plug); + + headsetEventReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final Bundle extras = intent.getExtras(); + + if (extras == null) { + return; + } + + Log.i(TAG, String.format("Headset event for: %s", extras.get("name"))); + final int state = extras.getInt("state"); + if (state == 0) { + if (!downloadService.isJukeboxEnabled()) { + downloadService.pause(); + } + } else if (state == 1) { + if (!downloadService.isJukeboxEnabled() && + sp.getBoolean(spKey, false) && + downloadService.getPlayerState() == PlayerState.PAUSED) { + downloadService.start(); + } + } + } + }; + @SuppressLint("InlinedApi") + IntentFilter headsetIntentFilter = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) ? + new IntentFilter(AudioManager.ACTION_HEADSET_PLUG) : + new IntentFilter(Intent.ACTION_HEADSET_PLUG); + downloadService.registerReceiver(headsetEventReceiver, headsetIntentFilter); + } + + public void onStart(Intent intent) { if (intent != null && intent.getExtras() != null) { diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index e96ec80f..5f633be2 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -249,6 +249,8 @@ 3 canciónes 5 canciónes Ilimitado + Reanudación de la inserción de auriculares + La aplicación reanudará la reproducción en pausa al insertar los auriculares en el dispositivo. Mantener la pantalla encendida mientras descarga mejora la velocidad de la misma. Mantener la pantalla encendida Recuerda configurar tu nombre de usuario y contraseña de Last.fm en el servidor de Subsonic diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index eeb7d42b..d32bb208 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -249,6 +249,8 @@ 3 morceaux 5 morceaux Illimité + Reprise de l\'insertion des écouteurs + L\'application reprendra la lecture en pause lors de l\'insertion du casque dans l\'appareil. Garder l\'écran allumé pendant le téléchargement permet d\'améliorer la vitesse de téléchargement. Garder écran allumé N\'oubliez pas de définir votre nom d\'utilisateur et mot de passe Last.fm sur le serveur Subsonic diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 6389458c..2c561cd2 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -249,6 +249,8 @@ 3 dal 5 dal Korlátlan + Folytatás a fejhallgató behelyezésekor + Az alkalmazás folytatja a szüneteltetett lejátszást a fejhallgató behelyezésekor a készülékbe. Képernyő ébrentartása a letöltés alatt, a magasabb letöltési sebesség érdekében. Képernyő ébrentartása A Last.fm felhasználónevet és jelszót be kell állítani a Subsonic kiszolgálón! diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 839c8643..553c9f89 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -252,6 +252,8 @@ 3 músicas 5 músicas Ilimitado + Currículo na inserção de fone de ouvido + O aplicativo retomará a reprodução em pausa na inserção dos fones de ouvido no dispositivo. Manter a tela ligada enquanto baixando aumenta a velocidade de download. Manter a Tela Ligada Lembre-se de definir seu usuário e senha do Last.fm no servidor Subsonic diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 0cf5ac22..ae8eaf4f 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -252,6 +252,8 @@ 3 músicas 5 músicas Ilimitado + Currículo na inserção de fone de ouvido + O aplicativo retomará a reprodução em pausa na inserção dos fones de ouvido no dispositivo. Manter o ecrã ligado enquanto descarrega aumenta a velocidade de download. Manter o Ecrã Ligado Lembre-se de definir seu usuário e senha do Last.fm no servidor Subsonic diff --git a/ultrasonic/src/main/res/values/playback_preferences_keys.xml b/ultrasonic/src/main/res/values/playback_preferences_keys.xml new file mode 100644 index 00000000..99c2cfa9 --- /dev/null +++ b/ultrasonic/src/main/res/values/playback_preferences_keys.xml @@ -0,0 +1,4 @@ + + + playback.resume_play_on_headphones_plug + \ No newline at end of file diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 7ed490c8..97687ad0 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -253,6 +253,8 @@ 3 songs 5 songs Unlimited + Resume on headphones insertion + App will resume paused playback on headphones insertion into device. Keeping the screen on while downloading improves download speed. Keep Screen On Remember to set up your Last.fm user and password on the Subsonic server diff --git a/ultrasonic/src/main/res/xml/settings.xml b/ultrasonic/src/main/res/xml/settings.xml index e4e2b4bd..e66f2b7e 100644 --- a/ultrasonic/src/main/res/xml/settings.xml +++ b/ultrasonic/src/main/res/xml/settings.xml @@ -100,6 +100,12 @@ a:entryValues="@array/incrementTimeValues" a:key="incrementTime" a:title="@string/settings.increment_time"/> + Date: Sun, 7 Jan 2018 13:17:53 +0100 Subject: [PATCH 16/29] Add displaying more specific api errors. Now app will show more specific api errors to user. Signed-off-by: Yahor Berdnikau --- .../ultrasonic/service/RESTMusicService.java | 2 +- .../service/parser/SubsonicRESTException.java | 33 ++++++++-------- .../moire/ultrasonic/util/BackgroundTask.java | 6 +++ .../ultrasonic/subsonic/RestErrorMapper.kt | 38 +++++++++++++++++++ ultrasonic/src/main/res/values-es/strings.xml | 16 ++++++-- ultrasonic/src/main/res/values-fr/strings.xml | 16 ++++++-- ultrasonic/src/main/res/values-hu/strings.xml | 16 ++++++-- .../src/main/res/values-pt-rBR/strings.xml | 16 ++++++-- ultrasonic/src/main/res/values-pt/strings.xml | 16 ++++++-- ultrasonic/src/main/res/values/strings.xml | 16 ++++++-- 10 files changed, 134 insertions(+), 41 deletions(-) create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java index 6b269680..41b57e72 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java @@ -698,7 +698,7 @@ public class RESTMusicService implements MusicService { throws SubsonicRESTException, IOException { if (response.hasError() || response.getStream() == null) { if (response.getApiError() != null) { - throw new SubsonicRESTException(response.getApiError().getCode(), "rest error"); + throw new SubsonicRESTException(response.getApiError()); } else { throw new IOException("Failed to make endpoint request, code: " + response.getResponseHttpCode()); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java index abc1373f..5ffbc3dd 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java @@ -1,26 +1,27 @@ package org.moire.ultrasonic.service.parser; +import org.moire.ultrasonic.api.subsonic.SubsonicError; + /** + * Exception returned by API with given {@code code}. + * * @author Sindre Mehus * @version $Id$ */ -public class SubsonicRESTException extends Exception -{ +public class SubsonicRESTException extends Exception { + private final SubsonicError error; - /** - * - */ - private static final long serialVersionUID = 859440717343258203L; - private final int code; + public SubsonicRESTException(final SubsonicError error) { + super("Api error: " + error.name()); + this.error = error; + } - public SubsonicRESTException(int code, String message) - { - super(message); - this.code = code; - } + public int getCode() + { + return error.getCode(); + } - public int getCode() - { - return code; - } + public SubsonicError getError() { + return error; + } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java index 76448a18..20ccf443 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java @@ -24,6 +24,8 @@ import android.util.Log; import org.moire.ultrasonic.R; +import org.moire.ultrasonic.service.parser.SubsonicRESTException; +import org.moire.ultrasonic.subsonic.RestErrorMapper; import org.xmlpull.v1.XmlPullParserException; import java.io.FileNotFoundException; @@ -90,6 +92,10 @@ public abstract class BackgroundTask implements ProgressListener return activity.getResources().getString(R.string.background_task_parse_error); } + if (error instanceof SubsonicRESTException) { + return RestErrorMapper.getLocalizedErrorMessage((SubsonicRESTException) error, activity); + } + String message = error.getMessage(); if (message != null) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt new file mode 100644 index 00000000..7ab5a93d --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt @@ -0,0 +1,38 @@ +@file:JvmName("RestErrorMapper") +package org.moire.ultrasonic.subsonic + +import android.content.Context +import org.moire.ultrasonic.R +import org.moire.ultrasonic.api.subsonic.SubsonicError.GENERIC +import org.moire.ultrasonic.api.subsonic.SubsonicError.INCOMPATIBLE_CLIENT_PROTOCOL_VERSION +import org.moire.ultrasonic.api.subsonic.SubsonicError.INCOMPATIBLE_SERVER_PROTOCOL_VERSION +import org.moire.ultrasonic.api.subsonic.SubsonicError.REQUESTED_DATA_WAS_NOT_FOUND +import org.moire.ultrasonic.api.subsonic.SubsonicError.REQUIRED_PARAM_MISSING +import org.moire.ultrasonic.api.subsonic.SubsonicError.TOKEN_AUTH_NOT_SUPPORTED_FOR_LDAP +import org.moire.ultrasonic.api.subsonic.SubsonicError.TRIAL_PERIOD_IS_OVER +import org.moire.ultrasonic.api.subsonic.SubsonicError.USER_NOT_AUTHORIZED_FOR_OPERATION +import org.moire.ultrasonic.api.subsonic.SubsonicError.WRONG_USERNAME_OR_PASSWORD +import org.moire.ultrasonic.service.parser.SubsonicRESTException + +/** + * Extension for [SubsonicRESTException] that returns localized error string, that can used to + * display error reason for user. + */ +fun SubsonicRESTException.getLocalizedErrorMessage(context: Context): String = + when (error) { + GENERIC -> context.getString(R.string.api_subsonic_generic) + REQUIRED_PARAM_MISSING -> context.getString(R.string.api_subsonic_param_missing) + INCOMPATIBLE_CLIENT_PROTOCOL_VERSION -> context + .getString(R.string.api_subsonic_upgrade_client) + INCOMPATIBLE_SERVER_PROTOCOL_VERSION -> context + .getString(R.string.api_subsonic_upgrade_server) + WRONG_USERNAME_OR_PASSWORD -> context.getString(R.string.api_subsonic_not_authenticated) + TOKEN_AUTH_NOT_SUPPORTED_FOR_LDAP -> context + .getString(R.string.api_subsonic_token_auth_not_supported_for_ldap) + USER_NOT_AUTHORIZED_FOR_OPERATION -> context + .getString(R.string.api_subsonic_not_authorized) + TRIAL_PERIOD_IS_OVER -> context.getString(R.string.api_subsonic_trial_period_is_over) + REQUESTED_DATA_WAS_NOT_FOUND -> context + .getString(R.string.api_subsonic_requested_data_was_not_found) + else -> context.getString(R.string.api_subsonic_unknown_api_error) + } diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 5f633be2..ce42c741 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -108,12 +108,8 @@ Medios sin conexión Se ha producido un error de red. Reintento %1$d de %2$d. Obtenido(s) %d artista(s). - Nombre de usuario o contraseña incorrectos. - No autorizado. Comprueba los permisos de usuario en el servidor de Subsonic. Leyendo del servidor. Leyendo del servidor. ¡Hecho! - Versiones incompatibles. Por favor actualiza la aplicación de Android UltraSonic. - Versiones incompatibles. Por favor actualiza el servidor de Subsonic. Listas de reproducción Actualizar Información Actualizada la información de la lista de reproducción para %s @@ -426,4 +422,16 @@ Podcast No hay canales de Podcasts registrados + + Error genérico de api. + La autenticación por token no es compatible con usuarios LDAP. + Nombre de usuario o contraseña incorrectos. + No autorizado. Comprueba los permisos de usuario en el servidor de Subsonic. + Falta el parámetro requerido. + No se encontraron los datos solicitados. + El período de prueba ha terminado. + Error de api desconocido. + Versiones incompatibles. Por favor actualiza la aplicación de Android UltraSonic. + Versiones incompatibles. Por favor actualiza el servidor de Subsonic. + \ No newline at end of file diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index d32bb208..4fde27db 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -108,12 +108,8 @@ Musique hors-ligne Une erreur de réseau s\'est produite. Essai %1$d de %2$d. %d artistes récupérés. - Mauvais nom d\'usager ou mot de passe. - Non autorisé. Vérifiez les permissions de l\'utilisateur dans le serveur Subsonic. Lecture du serveur. Lecture du serveur. Terminé! - Versions incompatible. Veuillez mette à jour l\'application Android UltraSonic. - Versions incompatible. Veuillez mette à jour le serveur Subsonic. Playlists Mise à jour des informations Informations de la playlist %s mises à jour @@ -426,4 +422,16 @@ Podcast No podcasts channels registered + + Erreur api générique. + L\'authentification par jeton n\'est pas prise en charge pour les utilisateurs LDAP. + Mauvais nom d\'usager ou mot de passe. + Non autorisé. Vérifiez les permissions de l\'utilisateur dans le serveur Subsonic. + Param nécessaire manquant. + Les données demandées n\'ont pas été trouvées. + La période d\'essai est terminée. + Erreur d\'api inconnue. + Versions incompatible. Veuillez mette à jour l\'application Android UltraSonic. + Versions incompatible. Veuillez mette à jour le serveur Subsonic. + \ No newline at end of file diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 2c561cd2..34a5e88a 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -108,12 +108,8 @@ Kapcsolat nélküli médiák Hálózati hiba történt! Újrapróbálkozás %1$d - %2$d. %d előadó található a médiakönyvtárban. - Hibás felhasználónév vagy jelszó! - Nem engedélyezett! Ellenőrizze a felhasználó jogosultságait a Subsonic kiszolgálón! Olvasás a kiszolgálóról… Olvasás a kiszolgálóról… Kész! - Nem kompatibilis verzió. Kérjük, frissítse az UltraSonic Android alkalmazást! - Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót! Lejátszási listák Módosítás Módosított lejátszási lista %s @@ -426,4 +422,16 @@ Podcast No podcasts channels registered + + Általános api hiba. + Az LDAP-felhasználók számára nem támogatott a token-hitelesítés. + Hibás felhasználónév vagy jelszó! + Nem engedélyezett! Ellenőrizze a felhasználó jogosultságait a Subsonic kiszolgálón! + A szükséges param hiányzik. + A keresett adatokat nem találtuk. + A próbaidő vége. + Ismeretlen api hiba. + Nem kompatibilis verzió. Kérjük, frissítse az UltraSonic Android alkalmazást! + Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót! + \ No newline at end of file diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 553c9f89..d84aa701 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -111,12 +111,8 @@ Mídia Offline Ocorreu um erro de rede. Tentativa %1$d de %2$d. Obtive %d Artistas. - Login ou senha errada. - Não autorizado. Verifique as permissões do usuário no servidor Subsonic. Lendo do servidor. Lendo do servidor. Pronto! - Versões incompativeis. Atualize o aplicativo UltraSonic para Android. - Versões incompativeis. Atualize o servidor UltraSonic. Playlists Atualizar Informação Informação da playlist atualizada para %s @@ -426,4 +422,16 @@ Restam %d dias para o fim do período de teste + + Erro de api genérico. + A autenticação por token não é suportada para usuários LDAP. + Login ou senha errada. + Não autorizado. Verifique as permissões do usuário no servidor Subsonic. + O parâmetro requerido está faltando. + Os dados solicitados não foram encontrados. + O período de avaliação acabou. + Erro de api desconhecido. + Versões incompativeis. Atualize o aplicativo UltraSonic para Android. + Versões incompativeis. Atualize o servidor UltraSonic. + diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index ae8eaf4f..aa4ffa69 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -111,12 +111,8 @@ Mídia Offline Ocorreu um erro de rede. Tentativa %1$d de %2$d. Obtive %d Artistas. - Login ou senha errada. - Não autorizado. Verifique as permissões do usuário no servidor Subsonic. Lendo do servidor. Lendo do servidor. Pronto! - Versões incompativeis. Atualize o aplicativo UltraSonic para Android. - Versões incompativeis. Atualize o servidor UltraSonic. Playlists Atualizar Informação Informação da playlist atualizada para %s @@ -426,4 +422,16 @@ Restam %d dias para o fim do período de teste + + Erro de api genérico. + A autenticação por token não é suportada para usuários LDAP. + Login ou senha errada. + Não autorizado. Verifique as permissões do usuário no servidor Subsonic. + O parâmetro requerido está faltando. + Os dados solicitados não foram encontrados. + O período de avaliação acabou. + Erro de api desconhecido. + Versões incompativeis. Atualize o aplicativo UltraSonic para Android. + Versões incompativeis. Atualize o servidor UltraSonic. + diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 97687ad0..b653822c 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -111,12 +111,8 @@ Offline Media A network error occurred. Retrying %1$d of %2$d. Got %d Artists. - Wrong username or password. - Not authorized. Check user permissions in Subsonic server. Reading from server. Reading from server. Done! - Incompatible versions. Please upgrade UltraSonic Android app. - Incompatible versions. Please upgrade Subsonic server. Playlists Update Information Updated playlist information for %s @@ -428,4 +424,16 @@ %d days left of trial period + + Generic api error. + Authentication by token is not supported for LDAP users. + Wrong username or password. + Not authorized. Check user permissions in Subsonic server. + Required param is missing. + Requested data was not found. + Trial period is over. + Unknown api error. + Incompatible versions. Please upgrade UltraSonic Android app. + Incompatible versions. Please upgrade Subsonic server. + \ No newline at end of file From ada94dc24f44987099e623a44f0e6057ad8cc9e6 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 7 Jan 2018 13:20:59 +0100 Subject: [PATCH 17/29] Change to handle json parse exception instead of xml. Signed-off-by: Yahor Berdnikau --- subsonic-api/build.gradle | 2 +- .../org/moire/ultrasonic/util/BackgroundTask.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/subsonic-api/build.gradle b/subsonic-api/build.gradle index 004778b0..4d6a0c05 100644 --- a/subsonic-api/build.gradle +++ b/subsonic-api/build.gradle @@ -12,7 +12,7 @@ sourceSets { dependencies { api other.kotlinStdlib api other.retrofit - implementation other.jacksonConverter + api other.jacksonConverter implementation(other.jacksonKotlin) { exclude module: 'kotlin-reflect' } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java index 20ccf443..4909a55b 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java @@ -22,11 +22,11 @@ import android.app.Activity; import android.os.Handler; import android.util.Log; -import org.moire.ultrasonic.R; +import com.fasterxml.jackson.core.JsonParseException; +import org.moire.ultrasonic.R; import org.moire.ultrasonic.service.parser.SubsonicRESTException; import org.moire.ultrasonic.subsonic.RestErrorMapper; -import org.xmlpull.v1.XmlPullParserException; import java.io.FileNotFoundException; import java.io.IOException; @@ -82,16 +82,15 @@ public abstract class BackgroundTask implements ProgressListener return activity.getResources().getString(R.string.background_task_not_found); } - if (error instanceof IOException) + if (error instanceof JsonParseException) { + return activity.getResources().getString(R.string.background_task_parse_error); + } + + if (error instanceof IOException) { return activity.getResources().getString(R.string.background_task_network_error); } - if (error instanceof XmlPullParserException) - { - return activity.getResources().getString(R.string.background_task_parse_error); - } - if (error instanceof SubsonicRESTException) { return RestErrorMapper.getLocalizedErrorMessage((SubsonicRESTException) error, activity); } From 1e7f2989eb41fe944f91a079ab020c16cb3e3efb Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Mon, 8 Jan 2018 19:45:55 +0100 Subject: [PATCH 18/29] Append generic error message to displayed text. As generic error can provide different reasons, it is worth to add received message to displayed error text. Signed-off-by: Yahor Berdnikau --- .../kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt | 2 +- ultrasonic/src/main/res/values-es/strings.xml | 2 +- ultrasonic/src/main/res/values-fr/strings.xml | 2 +- ultrasonic/src/main/res/values-hu/strings.xml | 2 +- ultrasonic/src/main/res/values-pt-rBR/strings.xml | 2 +- ultrasonic/src/main/res/values-pt/strings.xml | 2 +- ultrasonic/src/main/res/values/strings.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt index 7ab5a93d..32fc7982 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt @@ -20,7 +20,7 @@ import org.moire.ultrasonic.service.parser.SubsonicRESTException */ fun SubsonicRESTException.getLocalizedErrorMessage(context: Context): String = when (error) { - GENERIC -> context.getString(R.string.api_subsonic_generic) + GENERIC -> context.getString(R.string.api_subsonic_generic, message) REQUIRED_PARAM_MISSING -> context.getString(R.string.api_subsonic_param_missing) INCOMPATIBLE_CLIENT_PROTOCOL_VERSION -> context .getString(R.string.api_subsonic_upgrade_client) diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index ce42c741..63726c43 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -423,7 +423,7 @@ No hay canales de Podcasts registrados - Error genérico de api. + Error genérico de api: %1$s La autenticación por token no es compatible con usuarios LDAP. Nombre de usuario o contraseña incorrectos. No autorizado. Comprueba los permisos de usuario en el servidor de Subsonic. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 4fde27db..551a4d56 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -423,7 +423,7 @@ No podcasts channels registered - Erreur api générique. + Erreur api générique: %1$s L\'authentification par jeton n\'est pas prise en charge pour les utilisateurs LDAP. Mauvais nom d\'usager ou mot de passe. Non autorisé. Vérifiez les permissions de l\'utilisateur dans le serveur Subsonic. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 34a5e88a..168332d2 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -423,7 +423,7 @@ No podcasts channels registered - Általános api hiba. + Általános api hiba: %1$s Az LDAP-felhasználók számára nem támogatott a token-hitelesítés. Hibás felhasználónév vagy jelszó! Nem engedélyezett! Ellenőrizze a felhasználó jogosultságait a Subsonic kiszolgálón! diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index d84aa701..d55a773a 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -423,7 +423,7 @@ - Erro de api genérico. + Erro de api genérico: %1$s A autenticação por token não é suportada para usuários LDAP. Login ou senha errada. Não autorizado. Verifique as permissões do usuário no servidor Subsonic. diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index aa4ffa69..0afe06c6 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -423,7 +423,7 @@ - Erro de api genérico. + Erro de api genérico: %1$s A autenticação por token não é suportada para usuários LDAP. Login ou senha errada. Não autorizado. Verifique as permissões do usuário no servidor Subsonic. diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index b653822c..2a1905e7 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -425,7 +425,7 @@ - Generic api error. + Generic api error: %1$s Authentication by token is not supported for LDAP users. Wrong username or password. Not authorized. Check user permissions in Subsonic server. From 3e1dbe3476c8c71938586d90fc4ec63222138bad Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 13 Jan 2018 10:19:42 +0100 Subject: [PATCH 19/29] Fix api error parses always in generic error. Now it parses to right error representation. Also fix that right exception for error is not thrown on api call. Signed-off-by: Yahor Berdnikau --- .../api/subsonic/CommonFunctions.kt | 4 +- .../api/subsonic/SubsonicApiErrorsTest.kt | 51 +++++++++++++++++++ .../api/subsonic/SubsonicApiGetAvatarTest.kt | 4 +- .../subsonic/SubsonicApiGetCoverArtTest.kt | 4 +- .../api/subsonic/SubsonicApiStreamTest.kt | 4 +- .../resources/generic_error.json | 10 ++++ ...equest_data_not_found_error_response.json} | 4 +- .../resources/unexpected_error.json | 10 ++++ .../wrong_username_or_password_error.json | 10 ++++ .../ultrasonic/api/subsonic/SubsonicError.kt | 48 ++++++++++------- .../api/subsonic/SubsonicErrorTest.kt | 28 ---------- .../subsonic/response/StreamResponseTest.kt | 5 +- .../ultrasonic/service/JukeboxService.java | 1 - .../ultrasonic/service/RESTMusicService.java | 5 +- .../service/parser/SubsonicRESTException.java | 27 ---------- .../moire/ultrasonic/util/BackgroundTask.java | 2 +- .../service/SubsonicRESTException.kt | 10 ++++ .../ultrasonic/subsonic/RestErrorMapper.kt | 40 +++++++-------- ultrasonic/src/main/res/values-es/strings.xml | 1 - ultrasonic/src/main/res/values-fr/strings.xml | 1 - ultrasonic/src/main/res/values-hu/strings.xml | 1 - .../src/main/res/values-pt-rBR/strings.xml | 1 - ultrasonic/src/main/res/values-pt/strings.xml | 1 - ultrasonic/src/main/res/values/strings.xml | 1 - 24 files changed, 157 insertions(+), 116 deletions(-) create mode 100644 subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt create mode 100644 subsonic-api/src/integrationTest/resources/generic_error.json rename subsonic-api/src/integrationTest/resources/{generic_error_response.json => request_data_not_found_error_response.json} (60%) create mode 100644 subsonic-api/src/integrationTest/resources/unexpected_error.json create mode 100644 subsonic-api/src/integrationTest/resources/wrong_username_or_password_error.json delete mode 100644 subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicErrorTest.kt delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/SubsonicRESTException.kt diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/CommonFunctions.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/CommonFunctions.kt index f01b2af5..7ee3bcc4 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/CommonFunctions.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/CommonFunctions.kt @@ -59,14 +59,14 @@ fun parseDate(dateAsString: String): Calendar { fun checkErrorCallParsed(mockWebServerRule: MockWebServerRule, apiRequest: () -> Response): T { - mockWebServerRule.enqueueResponse("generic_error_response.json") + mockWebServerRule.enqueueResponse("request_data_not_found_error_response.json") val response = apiRequest() assertResponseSuccessful(response) with(response.body()) { status `should be` SubsonicResponse.Status.ERROR - error `should be` SubsonicError.GENERIC + error `should be` SubsonicError.RequestedDataWasNotFound } return response.body() } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt new file mode 100644 index 00000000..34234a73 --- /dev/null +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt @@ -0,0 +1,51 @@ +package org.moire.ultrasonic.api.subsonic + +import org.amshove.kluent.`should equal` +import org.amshove.kluent.`should not be` +import org.amshove.kluent.`should throw` +import org.junit.Test +import org.moire.ultrasonic.api.subsonic.SubsonicError.Generic +import org.moire.ultrasonic.api.subsonic.SubsonicError.WrongUsernameOrPassword +import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse +import retrofit2.Response +import java.io.IOException + +/** + * Integration test that checks validity of api errors parsing. + */ +class SubsonicApiErrorsTest : SubsonicAPIClientTest() { + @Test + fun `Should parse wrong username or password error`() { + mockWebServerRule.enqueueResponse("wrong_username_or_password_error.json") + + val response = client.api.ping().execute() + + response.assertError(WrongUsernameOrPassword) + } + + @Test + fun `Should parse generic error with message`() { + mockWebServerRule.enqueueResponse("generic_error.json") + + val response = client.api.ping().execute() + + response.assertError(Generic("Some generic error message.")) + } + + @Test + fun `Should fail on unknown error`() { + mockWebServerRule.enqueueResponse("unexpected_error.json") + + val fail = { + client.api.ping().execute() + } + + fail `should throw` IOException::class + } + + private fun Response.assertError(expectedError: SubsonicError) = + with(body()) { + error `should not be` null + error `should equal` expectedError + } +} diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAvatarTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAvatarTest.kt index deeab4a3..91acfe65 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAvatarTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetAvatarTest.kt @@ -13,14 +13,14 @@ import org.junit.Test class SubsonicApiGetAvatarTest : SubsonicAPIClientTest() { @Test fun `Should handle api error response`() { - mockWebServerRule.enqueueResponse("generic_error_response.json") + mockWebServerRule.enqueueResponse("request_data_not_found_error_response.json") val response = client.getAvatar("some") with(response) { stream `should be` null responseHttpCode `should equal to` 200 - apiError `should equal` SubsonicError.GENERIC + apiError `should equal` SubsonicError.RequestedDataWasNotFound } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetCoverArtTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetCoverArtTest.kt index 3b22d396..d327e06c 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetCoverArtTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiGetCoverArtTest.kt @@ -13,14 +13,14 @@ import org.junit.Test class SubsonicApiGetCoverArtTest : SubsonicAPIClientTest() { @Test fun `Should handle api error response`() { - mockWebServerRule.enqueueResponse("generic_error_response.json") + mockWebServerRule.enqueueResponse("request_data_not_found_error_response.json") val response = client.getCoverArt("some-id") with(response) { stream `should be` null responseHttpCode `should equal to` 200 - apiError `should equal` SubsonicError.GENERIC + apiError `should equal` SubsonicError.RequestedDataWasNotFound } } diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiStreamTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiStreamTest.kt index b0255d66..730ee8a1 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiStreamTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiStreamTest.kt @@ -13,14 +13,14 @@ import org.junit.Test class SubsonicApiStreamTest : SubsonicAPIClientTest() { @Test fun `Should handle api error response`() { - mockWebServerRule.enqueueResponse("generic_error_response.json") + mockWebServerRule.enqueueResponse("request_data_not_found_error_response.json") val response = client.stream("some-id") with(response) { stream `should be` null responseHttpCode `should equal to` 200 - apiError `should equal` SubsonicError.GENERIC + apiError `should equal` SubsonicError.RequestedDataWasNotFound } } diff --git a/subsonic-api/src/integrationTest/resources/generic_error.json b/subsonic-api/src/integrationTest/resources/generic_error.json new file mode 100644 index 00000000..7118efc9 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/generic_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 0, + "message": "Some generic error message." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/generic_error_response.json b/subsonic-api/src/integrationTest/resources/request_data_not_found_error_response.json similarity index 60% rename from subsonic-api/src/integrationTest/resources/generic_error_response.json rename to subsonic-api/src/integrationTest/resources/request_data_not_found_error_response.json index 1b5db1cc..bd97c058 100644 --- a/subsonic-api/src/integrationTest/resources/generic_error_response.json +++ b/subsonic-api/src/integrationTest/resources/request_data_not_found_error_response.json @@ -3,8 +3,8 @@ "status" : "failed", "version" : "1.13.0", "error" : { - "code" : 0, - "message" : "Generic error." + "code" : 70, + "message" : "Requested data was not found." } } } \ No newline at end of file diff --git a/subsonic-api/src/integrationTest/resources/unexpected_error.json b/subsonic-api/src/integrationTest/resources/unexpected_error.json new file mode 100644 index 00000000..fce1661b --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/unexpected_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 1000000, + "message": "New funky error message." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/wrong_username_or_password_error.json b/subsonic-api/src/integrationTest/resources/wrong_username_or_password_error.json new file mode 100644 index 00000000..a35e593b --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/wrong_username_or_password_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 40, + "message": "Wrong username or password." + } + } +} diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt index 06bbd508..89559cb2 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt @@ -9,29 +9,41 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize * Common API errors. */ @JsonDeserialize(using = SubsonicError.Companion.SubsonicErrorDeserializer::class) -enum class SubsonicError(val code: Int) { - GENERIC(0), - REQUIRED_PARAM_MISSING(10), - INCOMPATIBLE_CLIENT_PROTOCOL_VERSION(20), - INCOMPATIBLE_SERVER_PROTOCOL_VERSION(30), - WRONG_USERNAME_OR_PASSWORD(40), - TOKEN_AUTH_NOT_SUPPORTED_FOR_LDAP(41), - USER_NOT_AUTHORIZED_FOR_OPERATION(50), - TRIAL_PERIOD_IS_OVER(60), - REQUESTED_DATA_WAS_NOT_FOUND(70); +sealed class SubsonicError(val code: Int) { + data class Generic(val message: String) : SubsonicError(0) + object RequiredParamMissing : SubsonicError(10) + object IncompatibleClientProtocolVersion : SubsonicError(20) + object IncompatibleServerProtocolVersion : SubsonicError(30) + object WrongUsernameOrPassword : SubsonicError(40) + object TokenAuthNotSupportedForLDAP : SubsonicError(41) + object UserNotAuthorizedForOperation : SubsonicError(50) + object TrialPeriodIsOver : SubsonicError(60) + object RequestedDataWasNotFound : SubsonicError(70) companion object { - fun parseErrorFromJson(jsonErrorCode: Int) = SubsonicError.values() - .filter { it.code == jsonErrorCode }.firstOrNull() - ?: throw IllegalArgumentException("Unknown code $jsonErrorCode") + fun getError(code: Int, message: String) = when (code) { + 0 -> Generic(message) + 10 -> RequiredParamMissing + 20 -> IncompatibleClientProtocolVersion + 30 -> IncompatibleServerProtocolVersion + 40 -> WrongUsernameOrPassword + 41 -> TokenAuthNotSupportedForLDAP + 50 -> UserNotAuthorizedForOperation + 60 -> TrialPeriodIsOver + 70 -> RequestedDataWasNotFound + else -> throw IllegalArgumentException("Unknown code $code") + } class SubsonicErrorDeserializer : JsonDeserializer() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): SubsonicError { - p.nextToken() // "code" - val error = parseErrorFromJson(p.valueAsInt) - p.nextToken() // "message" - p.nextToken() // end of error object - return error + p.nextToken() // { -> "code" + p.nextToken() // "code" -> codeValue + val code = p.valueAsInt + p.nextToken() // codeValue -> "message" + p.nextToken() // "message" -> messageValue + val message = p.text + p.nextToken() // value -> } + return getError(code, message) } } } diff --git a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicErrorTest.kt b/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicErrorTest.kt deleted file mode 100644 index 28258de9..00000000 --- a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicErrorTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.moire.ultrasonic.api.subsonic - -import org.amshove.kluent.`should equal` -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized - -/** - * Unit test for [SubsonicError]. - */ -@RunWith(Parameterized::class) -class SubsonicErrorTest(private val error: SubsonicError) { - companion object { - @JvmStatic - @Parameterized.Parameters - fun data(): List = SubsonicError.values().toList() - } - - @Test - fun `Should proper convert error code to error`() { - SubsonicError.parseErrorFromJson(error.code) `should equal` error - } - - @Test(expected = IllegalArgumentException::class) - fun `Should throw IllegalArgumentException from unknown error code`() { - SubsonicError.parseErrorFromJson(error.code + 10000) - } -} diff --git a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/response/StreamResponseTest.kt b/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/response/StreamResponseTest.kt index 76085241..ee3c0253 100644 --- a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/response/StreamResponseTest.kt +++ b/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/response/StreamResponseTest.kt @@ -2,7 +2,7 @@ package org.moire.ultrasonic.api.subsonic.response import org.amshove.kluent.`should equal to` import org.junit.Test -import org.moire.ultrasonic.api.subsonic.SubsonicError.GENERIC +import org.moire.ultrasonic.api.subsonic.SubsonicError.RequestedDataWasNotFound /** * Unit test for [StreamResponse]. @@ -10,7 +10,8 @@ import org.moire.ultrasonic.api.subsonic.SubsonicError.GENERIC class StreamResponseTest { @Test fun `Should have error if subsonic error is not null`() { - StreamResponse(apiError = GENERIC, responseHttpCode = 200).hasError() `should equal to` true + StreamResponse(apiError = RequestedDataWasNotFound, responseHttpCode = 200) + .hasError() `should equal to` true } @Test diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxService.java index 849d73ac..4d85b0b6 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/JukeboxService.java @@ -31,7 +31,6 @@ import org.moire.ultrasonic.R; import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException; import org.moire.ultrasonic.domain.JukeboxStatus; import org.moire.ultrasonic.domain.PlayerState; -import org.moire.ultrasonic.service.parser.SubsonicRESTException; import org.moire.ultrasonic.util.Util; import java.util.ArrayList; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java index 41b57e72..d9c7ca7d 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java @@ -89,7 +89,6 @@ import org.moire.ultrasonic.domain.SearchCriteria; import org.moire.ultrasonic.domain.SearchResult; import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.UserInfo; -import org.moire.ultrasonic.service.parser.SubsonicRESTException; import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.FileUtil; import org.moire.ultrasonic.util.ProgressListener; @@ -1077,7 +1076,7 @@ public class RESTMusicService implements MusicService { } private void checkResponseSuccessful(@NonNull final Response response) - throws IOException { + throws SubsonicRESTException, IOException { if (response.isSuccessful() && response.body().getStatus() == SubsonicResponse.Status.OK) { return; @@ -1087,7 +1086,7 @@ public class RESTMusicService implements MusicService { throw new IOException("Server error, code: " + response.code()); } else if (response.body().getStatus() == SubsonicResponse.Status.ERROR && response.body().getError() != null) { - throw new IOException("Server error: " + response.body().getError().getCode()); + throw new SubsonicRESTException(response.body().getError()); } else { throw new IOException("Failed to perform request: " + response.code()); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java deleted file mode 100644 index 5ffbc3dd..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/SubsonicRESTException.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.moire.ultrasonic.service.parser; - -import org.moire.ultrasonic.api.subsonic.SubsonicError; - -/** - * Exception returned by API with given {@code code}. - * - * @author Sindre Mehus - * @version $Id$ - */ -public class SubsonicRESTException extends Exception { - private final SubsonicError error; - - public SubsonicRESTException(final SubsonicError error) { - super("Api error: " + error.name()); - this.error = error; - } - - public int getCode() - { - return error.getCode(); - } - - public SubsonicError getError() { - return error; - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java index 4909a55b..ae47bdcb 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java @@ -25,7 +25,7 @@ import android.util.Log; import com.fasterxml.jackson.core.JsonParseException; import org.moire.ultrasonic.R; -import org.moire.ultrasonic.service.parser.SubsonicRESTException; +import org.moire.ultrasonic.service.SubsonicRESTException; import org.moire.ultrasonic.subsonic.RestErrorMapper; import java.io.FileNotFoundException; diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/SubsonicRESTException.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/SubsonicRESTException.kt new file mode 100644 index 00000000..4d315d0a --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/SubsonicRESTException.kt @@ -0,0 +1,10 @@ +package org.moire.ultrasonic.service + +import org.moire.ultrasonic.api.subsonic.SubsonicError + +/** + * Exception returned by API with given `code`. + */ +class SubsonicRESTException(val error: SubsonicError) : Exception("Api error: ${error.code}") { + val code: Int get() = error.code +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt index 32fc7982..385cdb25 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt @@ -3,16 +3,16 @@ package org.moire.ultrasonic.subsonic import android.content.Context import org.moire.ultrasonic.R -import org.moire.ultrasonic.api.subsonic.SubsonicError.GENERIC -import org.moire.ultrasonic.api.subsonic.SubsonicError.INCOMPATIBLE_CLIENT_PROTOCOL_VERSION -import org.moire.ultrasonic.api.subsonic.SubsonicError.INCOMPATIBLE_SERVER_PROTOCOL_VERSION -import org.moire.ultrasonic.api.subsonic.SubsonicError.REQUESTED_DATA_WAS_NOT_FOUND -import org.moire.ultrasonic.api.subsonic.SubsonicError.REQUIRED_PARAM_MISSING -import org.moire.ultrasonic.api.subsonic.SubsonicError.TOKEN_AUTH_NOT_SUPPORTED_FOR_LDAP -import org.moire.ultrasonic.api.subsonic.SubsonicError.TRIAL_PERIOD_IS_OVER -import org.moire.ultrasonic.api.subsonic.SubsonicError.USER_NOT_AUTHORIZED_FOR_OPERATION -import org.moire.ultrasonic.api.subsonic.SubsonicError.WRONG_USERNAME_OR_PASSWORD -import org.moire.ultrasonic.service.parser.SubsonicRESTException +import org.moire.ultrasonic.api.subsonic.SubsonicError.Generic +import org.moire.ultrasonic.api.subsonic.SubsonicError.IncompatibleClientProtocolVersion +import org.moire.ultrasonic.api.subsonic.SubsonicError.IncompatibleServerProtocolVersion +import org.moire.ultrasonic.api.subsonic.SubsonicError.RequestedDataWasNotFound +import org.moire.ultrasonic.api.subsonic.SubsonicError.RequiredParamMissing +import org.moire.ultrasonic.api.subsonic.SubsonicError.TokenAuthNotSupportedForLDAP +import org.moire.ultrasonic.api.subsonic.SubsonicError.TrialPeriodIsOver +import org.moire.ultrasonic.api.subsonic.SubsonicError.UserNotAuthorizedForOperation +import org.moire.ultrasonic.api.subsonic.SubsonicError.WrongUsernameOrPassword +import org.moire.ultrasonic.service.SubsonicRESTException /** * Extension for [SubsonicRESTException] that returns localized error string, that can used to @@ -20,19 +20,19 @@ import org.moire.ultrasonic.service.parser.SubsonicRESTException */ fun SubsonicRESTException.getLocalizedErrorMessage(context: Context): String = when (error) { - GENERIC -> context.getString(R.string.api_subsonic_generic, message) - REQUIRED_PARAM_MISSING -> context.getString(R.string.api_subsonic_param_missing) - INCOMPATIBLE_CLIENT_PROTOCOL_VERSION -> context + is Generic -> context + .getString(R.string.api_subsonic_generic, (error as Generic).message) + RequiredParamMissing -> context.getString(R.string.api_subsonic_param_missing) + IncompatibleClientProtocolVersion -> context .getString(R.string.api_subsonic_upgrade_client) - INCOMPATIBLE_SERVER_PROTOCOL_VERSION -> context + IncompatibleServerProtocolVersion -> context .getString(R.string.api_subsonic_upgrade_server) - WRONG_USERNAME_OR_PASSWORD -> context.getString(R.string.api_subsonic_not_authenticated) - TOKEN_AUTH_NOT_SUPPORTED_FOR_LDAP -> context + WrongUsernameOrPassword -> context.getString(R.string.api_subsonic_not_authenticated) + TokenAuthNotSupportedForLDAP -> context .getString(R.string.api_subsonic_token_auth_not_supported_for_ldap) - USER_NOT_AUTHORIZED_FOR_OPERATION -> context + UserNotAuthorizedForOperation -> context .getString(R.string.api_subsonic_not_authorized) - TRIAL_PERIOD_IS_OVER -> context.getString(R.string.api_subsonic_trial_period_is_over) - REQUESTED_DATA_WAS_NOT_FOUND -> context + TrialPeriodIsOver -> context.getString(R.string.api_subsonic_trial_period_is_over) + RequestedDataWasNotFound -> context .getString(R.string.api_subsonic_requested_data_was_not_found) - else -> context.getString(R.string.api_subsonic_unknown_api_error) } diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 63726c43..e43aa0c3 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -430,7 +430,6 @@ Falta el parámetro requerido. No se encontraron los datos solicitados. El período de prueba ha terminado. - Error de api desconocido. Versiones incompatibles. Por favor actualiza la aplicación de Android UltraSonic. Versiones incompatibles. Por favor actualiza el servidor de Subsonic. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 551a4d56..936b85f1 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -430,7 +430,6 @@ Param nécessaire manquant. Les données demandées n\'ont pas été trouvées. La période d\'essai est terminée. - Erreur d\'api inconnue. Versions incompatible. Veuillez mette à jour l\'application Android UltraSonic. Versions incompatible. Veuillez mette à jour le serveur Subsonic. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 168332d2..8ed1f179 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -430,7 +430,6 @@ A szükséges param hiányzik. A keresett adatokat nem találtuk. A próbaidő vége. - Ismeretlen api hiba. Nem kompatibilis verzió. Kérjük, frissítse az UltraSonic Android alkalmazást! Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót! diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index d55a773a..9ef2d86b 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -430,7 +430,6 @@ O parâmetro requerido está faltando. Os dados solicitados não foram encontrados. O período de avaliação acabou. - Erro de api desconhecido. Versões incompativeis. Atualize o aplicativo UltraSonic para Android. Versões incompativeis. Atualize o servidor UltraSonic. diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 0afe06c6..4f8da965 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -430,7 +430,6 @@ O parâmetro requerido está faltando. Os dados solicitados não foram encontrados. O período de avaliação acabou. - Erro de api desconhecido. Versões incompativeis. Atualize o aplicativo UltraSonic para Android. Versões incompativeis. Atualize o servidor UltraSonic. diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 2a1905e7..9a1d99e9 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -432,7 +432,6 @@ Required param is missing. Requested data was not found. Trial period is over. - Unknown api error. Incompatible versions. Please upgrade UltraSonic Android app. Incompatible versions. Please upgrade Subsonic server. From fff0762e081e7bb1dbf3d5f7a2348096b0656bab Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 13 Jan 2018 10:54:30 +0100 Subject: [PATCH 20/29] Add more test for error responses. Signed-off-by: Yahor Berdnikau --- .../api/subsonic/SubsonicApiErrorsTest.kt | 70 +++++++++++++++++++ ...patible_client_protocol_version_error.json | 10 +++ ...patible_server_protocol_version_error.json | 10 +++ .../requested_data_was_not_found_error.json | 10 +++ .../required_param_missing_error.json | 10 +++ ...ken_auth_not_supported_for_ldap_error.json | 10 +++ .../resources/trial_period_is_over_error.json | 10 +++ ...er_not_authorized_for_operation_error.json | 10 +++ 8 files changed, 140 insertions(+) create mode 100644 subsonic-api/src/integrationTest/resources/incompatible_client_protocol_version_error.json create mode 100644 subsonic-api/src/integrationTest/resources/incompatible_server_protocol_version_error.json create mode 100644 subsonic-api/src/integrationTest/resources/requested_data_was_not_found_error.json create mode 100644 subsonic-api/src/integrationTest/resources/required_param_missing_error.json create mode 100644 subsonic-api/src/integrationTest/resources/token_auth_not_supported_for_ldap_error.json create mode 100644 subsonic-api/src/integrationTest/resources/trial_period_is_over_error.json create mode 100644 subsonic-api/src/integrationTest/resources/user_not_authorized_for_operation_error.json diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt index 34234a73..f91e4557 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt @@ -5,6 +5,13 @@ import org.amshove.kluent.`should not be` import org.amshove.kluent.`should throw` import org.junit.Test import org.moire.ultrasonic.api.subsonic.SubsonicError.Generic +import org.moire.ultrasonic.api.subsonic.SubsonicError.IncompatibleClientProtocolVersion +import org.moire.ultrasonic.api.subsonic.SubsonicError.IncompatibleServerProtocolVersion +import org.moire.ultrasonic.api.subsonic.SubsonicError.RequestedDataWasNotFound +import org.moire.ultrasonic.api.subsonic.SubsonicError.RequiredParamMissing +import org.moire.ultrasonic.api.subsonic.SubsonicError.TokenAuthNotSupportedForLDAP +import org.moire.ultrasonic.api.subsonic.SubsonicError.TrialPeriodIsOver +import org.moire.ultrasonic.api.subsonic.SubsonicError.UserNotAuthorizedForOperation import org.moire.ultrasonic.api.subsonic.SubsonicError.WrongUsernameOrPassword import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse import retrofit2.Response @@ -43,6 +50,69 @@ class SubsonicApiErrorsTest : SubsonicAPIClientTest() { fail `should throw` IOException::class } + @Test + fun `Should parse required param missing error`() { + mockWebServerRule.enqueueResponse("required_param_missing_error.json") + + val response = client.api.ping().execute() + + response.assertError(RequiredParamMissing) + } + + @Test + fun `Should parse incompatible client protocol version error`() { + mockWebServerRule.enqueueResponse("incompatible_client_protocol_version_error.json") + + val response = client.api.ping().execute() + + response.assertError(IncompatibleClientProtocolVersion) + } + + @Test + fun `Should parse incompatible server protocol version error`() { + mockWebServerRule.enqueueResponse("incompatible_server_protocol_version_error.json") + + val response = client.api.ping().execute() + + response.assertError(IncompatibleServerProtocolVersion) + } + + @Test + fun `Should parse token auth not supported for ldap error`() { + mockWebServerRule.enqueueResponse("token_auth_not_supported_for_ldap_error.json") + + val response = client.api.ping().execute() + + response.assertError(TokenAuthNotSupportedForLDAP) + } + + @Test + fun `Should parse user not authorized for operation error`() { + mockWebServerRule.enqueueResponse("user_not_authorized_for_operation_error.json") + + val response = client.api.ping().execute() + + response.assertError(UserNotAuthorizedForOperation) + } + + @Test + fun `Should parse trial period is over error`() { + mockWebServerRule.enqueueResponse("trial_period_is_over_error.json") + + val response = client.api.ping().execute() + + response.assertError(TrialPeriodIsOver) + } + + @Test + fun `Should parse requested data was not found error`() { + mockWebServerRule.enqueueResponse("requested_data_was_not_found_error.json") + + val response = client.api.ping().execute() + + response.assertError(RequestedDataWasNotFound) + } + private fun Response.assertError(expectedError: SubsonicError) = with(body()) { error `should not be` null diff --git a/subsonic-api/src/integrationTest/resources/incompatible_client_protocol_version_error.json b/subsonic-api/src/integrationTest/resources/incompatible_client_protocol_version_error.json new file mode 100644 index 00000000..4cf99209 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/incompatible_client_protocol_version_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 20, + "message": "Client protocol version 1.17.0 is not supported." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/incompatible_server_protocol_version_error.json b/subsonic-api/src/integrationTest/resources/incompatible_server_protocol_version_error.json new file mode 100644 index 00000000..bebc9120 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/incompatible_server_protocol_version_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 30, + "message": "Server doesn't support 1.10.0 protocol version." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/requested_data_was_not_found_error.json b/subsonic-api/src/integrationTest/resources/requested_data_was_not_found_error.json new file mode 100644 index 00000000..c55cc1f6 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/requested_data_was_not_found_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 70, + "message": "Requested data was not found." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/required_param_missing_error.json b/subsonic-api/src/integrationTest/resources/required_param_missing_error.json new file mode 100644 index 00000000..aa007e48 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/required_param_missing_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 10, + "message": "Param musicFolderId is missing." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/token_auth_not_supported_for_ldap_error.json b/subsonic-api/src/integrationTest/resources/token_auth_not_supported_for_ldap_error.json new file mode 100644 index 00000000..4f5ff447 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/token_auth_not_supported_for_ldap_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 41, + "message": "Token auth is not supported for ldap users." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/trial_period_is_over_error.json b/subsonic-api/src/integrationTest/resources/trial_period_is_over_error.json new file mode 100644 index 00000000..2196e7ff --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/trial_period_is_over_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 60, + "message": "Trial period is over." + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/user_not_authorized_for_operation_error.json b/subsonic-api/src/integrationTest/resources/user_not_authorized_for_operation_error.json new file mode 100644 index 00000000..ad679b03 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/user_not_authorized_for_operation_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0", + "error": { + "code": 50, + "message": "User is not authorized for this operation." + } + } +} From 0b5cc5d3f3680ae1586b65b87ef7a8259e1220ab Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 10:56:58 +0100 Subject: [PATCH 21/29] Make subsonic error parser more fail safe. Now it should parse more malformed error json, though having "code" field is still mandatory. Signed-off-by: Yahor Berdnikau --- .../api/subsonic/SubsonicApiErrorsTest.kt | 36 +++++++++++++++++++ .../resources/error_first_generic_error.json | 10 ++++++ .../reversed_tokens_generic_error.json | 10 ++++++ ..._additional_json_object_generic_error.json | 13 +++++++ .../without_message_generic_error.json | 9 +++++ .../ultrasonic/api/subsonic/SubsonicError.kt | 20 +++++++---- 6 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 subsonic-api/src/integrationTest/resources/error_first_generic_error.json create mode 100644 subsonic-api/src/integrationTest/resources/reversed_tokens_generic_error.json create mode 100644 subsonic-api/src/integrationTest/resources/with_additional_json_object_generic_error.json create mode 100644 subsonic-api/src/integrationTest/resources/without_message_generic_error.json diff --git a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt index f91e4557..3b8c66f9 100644 --- a/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt +++ b/subsonic-api/src/integrationTest/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicApiErrorsTest.kt @@ -113,6 +113,42 @@ class SubsonicApiErrorsTest : SubsonicAPIClientTest() { response.assertError(RequestedDataWasNotFound) } + @Test + fun `Should parse error with reversed tokens order`() { + mockWebServerRule.enqueueResponse("reversed_tokens_generic_error.json") + + val response = client.api.ping().execute() + + response.assertError(Generic("Video streaming not supported")) + } + + @Test + fun `Should parse error if json contains error first before other fields`() { + mockWebServerRule.enqueueResponse("error_first_generic_error.json") + + val response = client.api.ping().execute() + + response.assertError(Generic("Video streaming not supported")) + } + + @Test + fun `Should parse error if json doesn't contain message field`() { + mockWebServerRule.enqueueResponse("without_message_generic_error.json") + + val response = client.api.ping().execute() + + response.assertError(Generic("")) + } + + @Test + fun `Should parse error if error json contains additional object`() { + mockWebServerRule.enqueueResponse("with_additional_json_object_generic_error.json") + + val response = client.api.ping().execute() + + response.assertError(Generic("")) + } + private fun Response.assertError(expectedError: SubsonicError) = with(body()) { error `should not be` null diff --git a/subsonic-api/src/integrationTest/resources/error_first_generic_error.json b/subsonic-api/src/integrationTest/resources/error_first_generic_error.json new file mode 100644 index 00000000..a278910e --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/error_first_generic_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "error": { + "message": "Video streaming not supported", + "code": 0 + }, + "version": "1.8.0", + "status": "failed" + } +} diff --git a/subsonic-api/src/integrationTest/resources/reversed_tokens_generic_error.json b/subsonic-api/src/integrationTest/resources/reversed_tokens_generic_error.json new file mode 100644 index 00000000..549214c5 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/reversed_tokens_generic_error.json @@ -0,0 +1,10 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.8.0", + "error": { + "message": "Video streaming not supported", + "code": 0 + } + } +} \ No newline at end of file diff --git a/subsonic-api/src/integrationTest/resources/with_additional_json_object_generic_error.json b/subsonic-api/src/integrationTest/resources/with_additional_json_object_generic_error.json new file mode 100644 index 00000000..5b44bb90 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/with_additional_json_object_generic_error.json @@ -0,0 +1,13 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.8.0", + "error": { + "code": 0, + "unicorn" : { + "code": 41, + "message": "Unicorns doesn't exist!" + } + } + } +} diff --git a/subsonic-api/src/integrationTest/resources/without_message_generic_error.json b/subsonic-api/src/integrationTest/resources/without_message_generic_error.json new file mode 100644 index 00000000..904d1508 --- /dev/null +++ b/subsonic-api/src/integrationTest/resources/without_message_generic_error.json @@ -0,0 +1,9 @@ +{ + "subsonic-response": { + "status": "failed", + "version": "1.8.0", + "error": { + "code": 0 + } + } +} diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt index 89559cb2..ad1419e4 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicError.kt @@ -1,6 +1,8 @@ package org.moire.ultrasonic.api.subsonic import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.JsonToken.END_OBJECT +import com.fasterxml.jackson.core.JsonToken.START_OBJECT import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.annotation.JsonDeserialize @@ -36,13 +38,17 @@ sealed class SubsonicError(val code: Int) { class SubsonicErrorDeserializer : JsonDeserializer() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): SubsonicError { - p.nextToken() // { -> "code" - p.nextToken() // "code" -> codeValue - val code = p.valueAsInt - p.nextToken() // codeValue -> "message" - p.nextToken() // "message" -> messageValue - val message = p.text - p.nextToken() // value -> } + var code = -1 + var message = "" + while (p.nextToken() != END_OBJECT) { + when { + p.currentToken == START_OBJECT -> p.skipChildren() + "code".equals(p.currentName, ignoreCase = true) -> + code = p.nextIntValue(-1) + "message".equals(p.currentName, ignoreCase = true) -> + message = p.nextTextValue() + } + } return getError(code, message) } } From 749c257731bf4c54e0f23283577dbebf2cff2623 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 11:12:00 +0100 Subject: [PATCH 22/29] Replace generic error empty message with custom message. Signed-off-by: Yahor Berdnikau --- .../org/moire/ultrasonic/subsonic/RestErrorMapper.kt | 11 +++++++++-- ultrasonic/src/main/res/values-es/strings.xml | 1 + ultrasonic/src/main/res/values-fr/strings.xml | 1 + ultrasonic/src/main/res/values-hu/strings.xml | 1 + ultrasonic/src/main/res/values-pt-rBR/strings.xml | 1 + ultrasonic/src/main/res/values-pt/strings.xml | 1 + ultrasonic/src/main/res/values/strings.xml | 1 + 7 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt index 385cdb25..5d84442e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt @@ -20,8 +20,15 @@ import org.moire.ultrasonic.service.SubsonicRESTException */ fun SubsonicRESTException.getLocalizedErrorMessage(context: Context): String = when (error) { - is Generic -> context - .getString(R.string.api_subsonic_generic, (error as Generic).message) + is Generic -> { + val message = (error as Generic).message + val errorMessage = if (message == "") { + context.getString(R.string.api_subsonic_generic_no_message) + } else { + message + } + context.getString(R.string.api_subsonic_generic, errorMessage) + } RequiredParamMissing -> context.getString(R.string.api_subsonic_param_missing) IncompatibleClientProtocolVersion -> context .getString(R.string.api_subsonic_upgrade_client) diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index e43aa0c3..c047418a 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -424,6 +424,7 @@ Error genérico de api: %1$s + ningún mensaje dado desde el servidor La autenticación por token no es compatible con usuarios LDAP. Nombre de usuario o contraseña incorrectos. No autorizado. Comprueba los permisos de usuario en el servidor de Subsonic. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 936b85f1..0dfb8284 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -424,6 +424,7 @@ Erreur api générique: %1$s + aucun message donné par le serveur L\'authentification par jeton n\'est pas prise en charge pour les utilisateurs LDAP. Mauvais nom d\'usager ou mot de passe. Non autorisé. Vérifiez les permissions de l\'utilisateur dans le serveur Subsonic. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 8ed1f179..d94a0a88 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -424,6 +424,7 @@ Általános api hiba: %1$s + nincs üzenet a szerverről Az LDAP-felhasználók számára nem támogatott a token-hitelesítés. Hibás felhasználónév vagy jelszó! Nem engedélyezett! Ellenőrizze a felhasználó jogosultságait a Subsonic kiszolgálón! diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 9ef2d86b..ba5042ac 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -424,6 +424,7 @@ Erro de api genérico: %1$s + nenhuma mensagem fornecida pelo servidor A autenticação por token não é suportada para usuários LDAP. Login ou senha errada. Não autorizado. Verifique as permissões do usuário no servidor Subsonic. diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 4f8da965..b5f1e6c5 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -424,6 +424,7 @@ Erro de api genérico: %1$s + nenhuma mensagem fornecida pelo servidor A autenticação por token não é suportada para usuários LDAP. Login ou senha errada. Não autorizado. Verifique as permissões do usuário no servidor Subsonic. diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 9a1d99e9..21ac1e74 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -426,6 +426,7 @@ Generic api error: %1$s + no message given from server Authentication by token is not supported for LDAP users. Wrong username or password. Not authorized. Check user permissions in Subsonic server. From 697a68963279c869043b38a94cb1fd4505d1a9a4 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 11:27:34 +0100 Subject: [PATCH 23/29] Decrease target sdk version to 22. This is required for a new release as runtime permissions support is not yet implemented. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index ff608274..6c932026 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,6 +1,6 @@ ext.versions = [ minSdk : 14, - targetSdk : 23, + targetSdk : 22, compileSdk : 27, gradle : '4.4.1', @@ -12,7 +12,7 @@ ext.versions = [ jacocoAndroid : "0.1.2", buildVersioning : "1.6.0", - androidSupport : "23.4.0", + androidSupport : "22.2.1", kotlin : "1.2.10", From 88053c0ccc29868b34cff0b6e4978259a38d16bf Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 11:28:34 +0100 Subject: [PATCH 24/29] Change ci image api to 27. To be on the same value with compile sdk. Signed-off-by: Yahor Berdnikau --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 28eac599..fc710f6c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/android:api-26-alpha + - image: circleci/android:api-27-alpha working_directory: ~/ultrasonic envoronment: JVM_OPTS: -Xmx3200m From 1cdf76db8b698c9e93596cb83c5dab0ce5bb5bdf Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 12:52:17 +0100 Subject: [PATCH 25/29] Add some several gradle optimizations. Enabled parallel build, configure on demand, caching output of tasks. Signed-off-by: Yahor Berdnikau --- gradle.properties | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..68967e5e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.parallel=true +org.gradle.configureondemand=true +org.gradle.caching=true \ No newline at end of file From 365bd96f568c46583e93164bc9dcb17767fed4ed Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 20 Jan 2018 12:53:26 +0100 Subject: [PATCH 26/29] Remove unnecessary cast. Signed-off-by: Yahor Berdnikau --- .../kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt index 5d84442e..d4470f6f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/RestErrorMapper.kt @@ -21,7 +21,7 @@ import org.moire.ultrasonic.service.SubsonicRESTException fun SubsonicRESTException.getLocalizedErrorMessage(context: Context): String = when (error) { is Generic -> { - val message = (error as Generic).message + val message = error.message val errorMessage = if (message == "") { context.getString(R.string.api_subsonic_generic_no_message) } else { From 7830ed3dbf9c8b636a813ff8c4793f5a2c9085d9 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 21 Jan 2018 18:06:41 +0100 Subject: [PATCH 27/29] Add flag to force using hex password authentication. Signed-off-by: Yahor Berdnikau --- .../ultrasonic/api/subsonic/SubsonicAPIClient.kt | 8 ++++++-- .../interceptors/ProxyPasswordInterceptor.kt | 10 ++++++++-- .../interceptors/ProxyPasswordInterceptorTest.kt | 13 ++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt index db769ccc..fa1c1cd2 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt @@ -41,13 +41,17 @@ class SubsonicAPIClient(baseUrl: String, minimalProtocolVersion: SubsonicAPIVersions, clientID: String, allowSelfSignedCertificate: Boolean = false, + enableLdapUserSupport: Boolean = false, debug: Boolean = false) { private val versionInterceptor = VersionInterceptor(minimalProtocolVersion) { protocolVersion = it } - private val proxyPasswordInterceptor = ProxyPasswordInterceptor(minimalProtocolVersion, - PasswordHexInterceptor(password), PasswordMD5Interceptor(password)) + private val proxyPasswordInterceptor = ProxyPasswordInterceptor( + minimalProtocolVersion, + PasswordHexInterceptor(password), + PasswordMD5Interceptor(password), + enableLdapUserSupport) /** * Get currently used protocol version. diff --git a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptor.kt b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptor.kt index 0273f984..ca796a17 100644 --- a/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptor.kt +++ b/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptor.kt @@ -7,15 +7,21 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions /** * Proxy [Interceptor] that uses one of [hexInterceptor] or [mD5Interceptor] depends on [apiVersion]. + * + * To force [hexInterceptor] set [forceHexPassword] to `true`. Usually it should be done only for + * ldap users. */ internal class ProxyPasswordInterceptor( initialAPIVersions: SubsonicAPIVersions, private val hexInterceptor: PasswordHexInterceptor, - private val mD5Interceptor: PasswordMD5Interceptor) : Interceptor { + private val mD5Interceptor: PasswordMD5Interceptor, + private val forceHexPassword: Boolean = false +) : Interceptor { var apiVersion: SubsonicAPIVersions = initialAPIVersions override fun intercept(chain: Chain): Response = - if (apiVersion < SubsonicAPIVersions.V1_13_0) { + if (apiVersion < SubsonicAPIVersions.V1_13_0 || + forceHexPassword) { hexInterceptor.intercept(chain) } else { mD5Interceptor.intercept(chain) diff --git a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptorTest.kt b/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptorTest.kt index 2b7d9bcd..4bac103f 100644 --- a/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptorTest.kt +++ b/subsonic-api/src/test/kotlin/org/moire/ultrasonic/api/subsonic/interceptors/ProxyPasswordInterceptorTest.kt @@ -6,6 +6,7 @@ import okhttp3.Interceptor.Chain import org.junit.Test import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions.V1_12_0 import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions.V1_13_0 +import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions.V1_16_0 /** * Unit test for [ProxyPasswordInterceptor]. @@ -16,7 +17,7 @@ class ProxyPasswordInterceptorTest { private val mockChain = mock() private val proxyInterceptor = ProxyPasswordInterceptor(V1_12_0, - mockPasswordHexInterceptor, mockPasswordMd5Interceptor) + mockPasswordHexInterceptor, mockPasswordMd5Interceptor, false) @Test fun `Should use hex password on versions less then 1 13 0`() { @@ -33,4 +34,14 @@ class ProxyPasswordInterceptorTest { verify(mockPasswordMd5Interceptor).intercept(mockChain) } + + @Test + fun `Should use hex password if forceHex is true`() { + val interceptor = ProxyPasswordInterceptor(V1_16_0, mockPasswordHexInterceptor, + mockPasswordMd5Interceptor, true) + + interceptor.intercept(mockChain) + + verify(mockPasswordHexInterceptor).intercept(mockChain) + } } From ae6b73fab4f931526f1d3133c4cb1121f9a98700 Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sun, 21 Jan 2018 18:15:02 +0100 Subject: [PATCH 28/29] Add setting to force using old-authorization for LDAP users. Subsonic api doesn't support new md5 based authorization for LDAP users. Signed-off-by: Yahor Berdnikau --- .../ultrasonic/fragment/ServerSettingsFragment.java | 13 +++++++++++++ .../ultrasonic/service/MusicServiceFactory.java | 8 ++++++-- .../java/org/moire/ultrasonic/util/Constants.java | 1 + ultrasonic/src/main/res/values-es/strings.xml | 3 +++ ultrasonic/src/main/res/values-fr/strings.xml | 3 +++ ultrasonic/src/main/res/values-hu/strings.xml | 3 +++ ultrasonic/src/main/res/values-pt-rBR/strings.xml | 3 +++ ultrasonic/src/main/res/values-pt/strings.xml | 3 +++ ultrasonic/src/main/res/values/strings.xml | 4 ++++ ultrasonic/src/main/res/xml/server_settings.xml | 7 +++++++ 10 files changed, 46 insertions(+), 2 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ServerSettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ServerSettingsFragment.java index b9fcb529..85456be2 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ServerSettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/ServerSettingsFragment.java @@ -38,6 +38,7 @@ public class ServerSettingsFragment extends PreferenceFragment private CheckBoxPreference equalizerPref; private CheckBoxPreference jukeboxPref; private CheckBoxPreference allowSelfSignedCertificatePref; + private CheckBoxPreference enableLdapUserSupportPref; private Preference removeServerPref; private Preference testConnectionPref; @@ -77,6 +78,9 @@ public class ServerSettingsFragment extends PreferenceFragment testConnectionPref = findPreference(getString(R.string.settings_test_connection_title)); allowSelfSignedCertificatePref = (CheckBoxPreference) findPreference( getString(R.string.settings_allow_self_signed_certificate)); + enableLdapUserSupportPref = (CheckBoxPreference) findPreference( + getString(R.string.settings_enable_ldap_user_support) + ); setupPreferencesValues(); setupPreferencesListeners(); @@ -140,6 +144,11 @@ public class ServerSettingsFragment extends PreferenceFragment .putBoolean(Constants.PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + serverId, (Boolean) newValue) .apply(); return true; + } else if (preference == enableLdapUserSupportPref) { + sharedPreferences.edit() + .putBoolean(Constants.PREFERENCES_KEY_LDAP_SUPPORT + serverId, (Boolean) newValue) + .apply(); + return true; } return false; } @@ -175,6 +184,9 @@ public class ServerSettingsFragment extends PreferenceFragment allowSelfSignedCertificatePref.setChecked(sharedPreferences .getBoolean(Constants.PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + serverId, false)); + + enableLdapUserSupportPref.setChecked(sharedPreferences + .getBoolean(Constants.PREFERENCES_KEY_LDAP_SUPPORT + serverId, false)); } private void updatePassword() { @@ -213,6 +225,7 @@ public class ServerSettingsFragment extends PreferenceFragment equalizerPref.setOnPreferenceChangeListener(this); jukeboxPref.setOnPreferenceChangeListener(this); allowSelfSignedCertificatePref.setOnPreferenceChangeListener(this); + enableLdapUserSupportPref.setOnPreferenceChangeListener(this); removeServerPref.setOnPreferenceClickListener(this); testConnectionPref.setOnPreferenceClickListener(this); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicServiceFactory.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicServiceFactory.java index e274412a..4d2b3d4b 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicServiceFactory.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicServiceFactory.java @@ -86,6 +86,8 @@ public class MusicServiceFactory { String password = preferences.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null); boolean allowSelfSignedCertificate = preferences .getBoolean(Constants.PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + instance, false); + boolean enableLdapUserSupport = preferences + .getBoolean(Constants.PREFERENCES_KEY_LDAP_SUPPORT + instance , false); if (serverUrl == null || username == null || @@ -93,11 +95,13 @@ public class MusicServiceFactory { Log.i("MusicServiceFactory", "Server credentials is not available"); return new SubsonicAPIClient("http://localhost", "", "", SubsonicAPIVersions.fromApiVersion(Constants.REST_PROTOCOL_VERSION), - Constants.REST_CLIENT_ID, allowSelfSignedCertificate, BuildConfig.DEBUG); + Constants.REST_CLIENT_ID, allowSelfSignedCertificate, + enableLdapUserSupport, BuildConfig.DEBUG); } return new SubsonicAPIClient(serverUrl, username, password, SubsonicAPIVersions.fromApiVersion(Constants.REST_PROTOCOL_VERSION), - Constants.REST_CLIENT_ID, allowSelfSignedCertificate, BuildConfig.DEBUG); + Constants.REST_CLIENT_ID, allowSelfSignedCertificate, + enableLdapUserSupport, BuildConfig.DEBUG); } } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java index a3aab7e5..de3b0a70 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java @@ -77,6 +77,7 @@ public final class Constants public static final String PREFERENCES_KEY_USERNAME = "username"; public static final String PREFERENCES_KEY_PASSWORD = "password"; public static final String PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE = "allowSSCertificate"; + public static final String PREFERENCES_KEY_LDAP_SUPPORT = "enableLdapSupport"; public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime"; public static final String PREFERENCES_KEY_THEME = "theme"; public static final String PREFERENCES_KEY_DISPLAY_BITRATE_WITH_ARTIST = "displayBitrateWithArtist"; diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index c047418a..525a11ee 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -299,6 +299,9 @@ Claro Tema Permir certificado HTTPS autofirmado + Habilitar soporte para usuarios LDAP + Esto obliga a la aplicación a enviar siempre la contraseña en modo antiguo, + porque Subsonic api no soporta nueva autorización para usuarios LDAP. Usar carpetas para el nombre del artista Se asume que la carpeta en el nivel mal alto es el nombre del artista del álbum Navegar usando las etiquetas ID3 diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 0dfb8284..54b0c249 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -299,6 +299,9 @@ Clair Thème Autoriser le certificat HTTPS auto-signé + Activer la prise en charge des utilisateurs LDAP + Cela force l\'application à toujours envoyer le mot de passe à l\'ancienne, + parce que Subsonic api ne supporte pas les nouvelles autorisations pour les utilisateurs LDAP. Utilisez des dossiers pour les noms d\'artistes Dossier de niveau supérieur devient le nom de l\'artiste de l\'album Naviguer en utilisant ID3 Tags diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index d94a0a88..0bf3027c 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -299,6 +299,9 @@ Világos Téma Engedélyezze az önaláírt HTTPS tanúsítványt + Az LDAP-felhasználók támogatásának engedélyezése + Ez arra kényszeríti az alkalmazást, hogy mindig jelszót küldjön régi módon, + mert a Subsonic api nem támogatja az LDAP-felhasználók új engedélyezését. Mappanevek használata az előadók neveként Feltételezi, hogy a legfelső szintű mappa az előadó neve. Böngészés ID3 Tag használatával diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index ba5042ac..2fe7fba0 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -302,6 +302,9 @@ Claro Tema Permitir o certificado HTTPS auto-assinado + Ative o suporte para usuários LDAP + Isso força o aplicativo a enviar sempre a senha de forma antiga, + porque o Subsonic api não suporta nova autorização para usuários LDAP. Pasta para Nome do Artista Assume que a pasta mais acima é o nome do artista Navegar Usando Etiquetas ID3 diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index b5f1e6c5..3908daca 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -302,6 +302,9 @@ Claro Tema Permitir o certificado HTTPS auto-assinado + Ative o suporte para usuários LDAP + Isso força o aplicativo a enviar sempre a senha de forma antiga, + porque o Subsonic api não suporta nova autorização para usuários LDAP. Pasta para Nome do Artista Assume que a pasta mais acima é o nome do artista Navegar Usando Etiquetas ID3 diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 21ac1e74..91777b4f 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -143,6 +143,7 @@ No saved playlists on server Contacting server, please wait. allowSelfSignedCertificate + enableLdapUserSupport Appearance Buffer Length Disabled @@ -303,6 +304,9 @@ Light Theme Allow self-signed HTTPS certificate + Enable support for LDAP users + This forces app to always send password in old-way, + because Subsonic api does not support new authorization for LDAP users. Use Folders For Artist Name Assume top-level folder is the name of the album artist Browse Using ID3 Tags diff --git a/ultrasonic/src/main/res/xml/server_settings.xml b/ultrasonic/src/main/res/xml/server_settings.xml index 3f755b58..46ea55dd 100644 --- a/ultrasonic/src/main/res/xml/server_settings.xml +++ b/ultrasonic/src/main/res/xml/server_settings.xml @@ -44,6 +44,13 @@ android:defaultValue="false" android:title="@string/settings.title.allow_self_signed_certificate" /> + Date: Sun, 21 Jan 2018 21:01:21 +0100 Subject: [PATCH 29/29] Add more specific error message related to ssl connection problems. Signed-off-by: Yahor Berdnikau --- .../moire/ultrasonic/util/BackgroundTask.java | 57 +++++++++---------- ultrasonic/src/main/res/values-es/strings.xml | 2 + ultrasonic/src/main/res/values-fr/strings.xml | 2 + ultrasonic/src/main/res/values-hu/strings.xml | 2 + .../src/main/res/values-pt-rBR/strings.xml | 2 + ultrasonic/src/main/res/values-pt/strings.xml | 2 + ultrasonic/src/main/res/values/strings.xml | 2 + 7 files changed, 40 insertions(+), 29 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java index ae47bdcb..f0e32cb0 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/BackgroundTask.java @@ -30,6 +30,10 @@ import org.moire.ultrasonic.subsonic.RestErrorMapper; import java.io.FileNotFoundException; import java.io.IOException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLException; /** * @author Sindre Mehus @@ -69,39 +73,34 @@ public abstract class BackgroundTask implements ProgressListener new ErrorDialog(activity, getErrorMessage(error), true); } - protected String getErrorMessage(Throwable error) - { - - if (error instanceof IOException && !Util.isNetworkConnected(activity)) - { - return activity.getResources().getString(R.string.background_task_no_network); - } - - if (error instanceof FileNotFoundException) - { - return activity.getResources().getString(R.string.background_task_not_found); - } - - if (error instanceof JsonParseException) { + protected String getErrorMessage(Throwable error) { + if (error instanceof IOException && !Util.isNetworkConnected(activity)) { + return activity.getResources().getString(R.string.background_task_no_network); + } else if (error instanceof FileNotFoundException) { + return activity.getResources().getString(R.string.background_task_not_found); + } else if (error instanceof JsonParseException) { return activity.getResources().getString(R.string.background_task_parse_error); - } - - if (error instanceof IOException) - { - return activity.getResources().getString(R.string.background_task_network_error); - } - - if (error instanceof SubsonicRESTException) { + } else if (error instanceof SSLException) { + if (error.getCause() instanceof CertificateException && + error.getCause().getCause() instanceof CertPathValidatorException) { + return activity.getResources() + .getString(R.string.background_task_ssl_cert_error, + error.getCause().getCause().getMessage()); + } else { + return activity.getResources().getString(R.string.background_task_ssl_error); + } + } else if (error instanceof IOException) { + return activity.getResources().getString(R.string.background_task_network_error); + } else if (error instanceof SubsonicRESTException) { return RestErrorMapper.getLocalizedErrorMessage((SubsonicRESTException) error, activity); } - String message = error.getMessage(); - if (message != null) - { - return message; - } - return error.getClass().getSimpleName(); - } + String message = error.getMessage(); + if (message != null) { + return message; + } + return error.getClass().getSimpleName(); + } @Override public abstract void updateProgress(final String message); diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 525a11ee..514f1875 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -6,6 +6,8 @@ Este programa requiere acceso a la red. Por favor enciende la Wi-Fi o la red móvil. Recurso no encontrado. Por favor comprueba la dirección del servidor. No se entiende la respuesta. Por favor comprueba la dirección del servidor. + Error del certificado HTTPS: %1$s. + Excepción de conexión SSL. Compruebe el certificado del servidor. Por favor espera… Marcadores Biblioteca diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 54b0c249..0c6616f6 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -6,6 +6,8 @@ Cette application requiert un accès au réseau. Veuillez activer le Wi-Fi ou le réseau mobile. Ressources introuvables. Veuillez vérifier l\'adresse du serveur. Réponse incorrecte. Veuillez vérifier l\'adresse du serveur. + Erreur de certificat HTTPS: %1$s. + Exception de connexion SSL. Veuillez vérifier le certificat du serveur. Veuillez patienter… Signets Bibliothèque musicale diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 0bf3027c..571ad7fc 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -6,6 +6,8 @@ Az alkalmazás hálózati hozzáférést igényel. Kérjük, kapcsolja be a Wi-Fi-t vagy a mobilhálózatot! Az erőforrás nem található! Kérjük, ellenőrizze a kiszolgáló címét! Értelmezhetetlen válasz! Kérjük, ellenőrizze a kiszolgáló címét! + HTTPS tanúsítványhiba: %1$s. + SSL kapcsolat kivétel. Kérjük, ellenőrizze a szerver tanúsítványát. Kérem várjon!… Könyvjelzők Médiakönyvtár diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 2fe7fba0..88dd330a 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -6,6 +6,8 @@ Este aplicativo requer acesso à rede. Ligue o Wi-Fi ou a rede de dados. Recurso não encontrado. Verifique o endereço do servidor. Não entendi a resposta. Verifique o endereço do servidor. + Erro de certificado HTTPS: %1$s. + Exceção de conexão SSL. Verifique o certificado do servidor. Por favor aguarde… Favoritos Biblioteca de Mídia diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 3908daca..9286d0e5 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -6,6 +6,8 @@ Este aplicativo requer acesso à rede. Ligue o Wi-Fi ou a rede de dados. Recurso não encontrado. Verifique o endereço do servidor. Não entendi a resposta. Verifique o endereço do servidor. + Erro de certificado HTTPS: %1$s. + Exceção de conexão SSL. Verifique o certificado do servidor. Por favor aguarde… Favoritos Biblioteca de Mídia diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 91777b4f..4748a3eb 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -6,6 +6,8 @@ This program requires network access. Please turn on Wi-Fi or mobile network. Resource not found. Please check the server address. Didn\'t understand the reply. Please check the server address. + HTTPS certificate error: %1$s. + SSL connection exception. Please check server certificate. Please wait… Bookmarks Media Library