├── .gitignore
├── .idea
├── caches
│ └── build_file_checksums.ser
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jarRepositories.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE.txt
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── vpaliy
│ │ │ └── soundcloud_api
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_single.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── vpaliy
│ └── soundcloud
│ └── ExampleUnitTest.java
├── art
└── sound_pop_up.png
├── build.gradle
├── dependencies.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── soundcloud-api@
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── vpaliy
│ │ └── soundcloud
│ │ ├── Endpoints.java
│ │ ├── SoundCloud.java
│ │ ├── SoundCloudService.java
│ │ ├── auth
│ │ ├── AuthService.java
│ │ ├── Connect.java
│ │ ├── LoginActivity.java
│ │ └── SoundCloudAuth.java
│ │ ├── model
│ │ ├── AppEntity.java
│ │ ├── CommentEntity.java
│ │ ├── ConnectionEntity.java
│ │ ├── MeEntity.java
│ │ ├── MiniUserEntity.java
│ │ ├── MyActivityEntity.java
│ │ ├── Page.java
│ │ ├── PlaylistEntity.java
│ │ ├── SecretToken.java
│ │ ├── Token.java
│ │ ├── TrackEntity.java
│ │ ├── UserEntity.java
│ │ └── WebProfileEntity.java
│ │ └── utils
│ │ └── Adapter.java
└── res
│ ├── layout
│ └── activity_web.xml
│ └── values
│ └── strings.xml
└── test
└── java
└── com
└── vpaliy
└── soundcloud
└── ExampleUnitTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2017 Vasyl Paliy
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SoundCloud-API for Android.
2 | [](https://jitpack.io/#vpaliyX/SoundCloud-API)
3 |
4 | This project is a wrapper for the [SoundCloud API](https://developers.soundcloud.com/).
5 |
6 | The SoundCloud API exposes SoundCloud resources like *tracks*, *playlists*, *users*, *comments*, etc.
7 | The API gives you the ability to access a **sound's stream URL** and use your own player to play sounds from SoundCloud.
8 |
9 | This repository uses Retrofit2 to create Java interfaces from API endpoints. It returns a `Single` which makes it very easy
10 | to handle asynchronous operations and you can convert an existing data structure into another Single . Please, refer to [Single Utility Operators](http://reactivex.io/documentation/single.html) for more details.
11 |
12 | ## How do I use this wrapper? ##
13 |
14 | ### Step 1 ###
15 |
16 | Add this to your root `build.gradle` file:
17 |
18 | ``` gradle
19 | allprojects {
20 | repositories {
21 | maven { url 'https://jitpack.io' }
22 | }
23 | }
24 | ```
25 | ### Step 2 ###
26 |
27 | Add the dependency
28 |
29 | ``` gradle
30 | dependencies {
31 | compile 'com.github.vpaliyX:SoundCloud-API:-SNAPSHOT'
32 | }
33 |
34 | ```
35 |
36 | ### Making Request ###
37 | You need to get your `client_id` and `client_secret` by registering your app [here](https://developers.soundcloud.com/docs/api/guide). After you have obtained that, you can start using the API.
38 |
39 |
40 | Basically, most of the calls will look like this one:
41 |
42 | ```java
43 | final SoundCloud api = new SoundCloud.Builder(context, Config.CLIENT_ID)
44 | .setToken(token)
45 | .setInterceptor(interceptor)
46 | .build();
47 | final SoundCloudService service = api.getSoundCloudService();
48 | service.fetchTrack("123456678") //some dummy track id
49 | .subscribeOn(Schedulers.io())
50 | .observeOn(AndroidSchedulers.mainThread())
51 | .subscribe(track->{
52 | //do something with the track
53 | });
54 |
55 | ```
56 |
57 | It's pretty simple. If you want to pass query paramaters, use the following structure:
58 |
59 | ```java
60 | service.searchTracks(Track.Filter.start()
61 | .byName("Imagine Dragons")
62 | .byGenres("rock","indie","alternative")
63 | .byTags("popular","rock","imagine","dragons")
64 | .byLicense(Track.License.ALL_RIGHTS_RESERVED)
65 | .byTypes(Track.Type.ORIGINAL, Track.Type.LIVE)
66 | .limit(30)
67 | .offset(10).createOptions())
68 | .subscribeOn(Schedulers.io())
69 | .observeOn(AndroidSchedulers.mainThread())
70 | .subscribe(tracks->{
71 | //do something
72 | });
73 |
74 | ```
75 | The `Filter` class is a nested class inside of a model class like `User`, `Playlist` or `Track`. Most of them offer a different set of methods because not all models can be filtered in the same way.
76 | Whenever you need to filter a request, just use the `Filter` class and its available methods. After you've listed all things you need, just call the `Filter.createOptions()` method.
77 |
78 |
79 | ## Authentication ##
80 |
81 | SoundCloud authentication uses **OAuth 2.0**, a popular open standard used by many popular API providers.
82 | OAuth 2.0 allows users to authorize your application without disclosing their username and password.
83 |
84 | I've created a class called `SoundCloudAuth` which is responsible for the authentication.
85 | The main purpose of this class is to obtain an **access token** for your app.
86 |
87 | There are 3 ways you can do this:
88 | - with user's credentials (username, password)
89 | - with the authorization code
90 | - refresh token
91 |
92 | **1.** Use the credentials to obtain a token:
93 | ```java
94 | SoundCloudAuth.create(Config.CLIENT_ID,Config.CLIENT_SECRET_ID)
95 | .addRedirectUri(Config.REDIRECT_URI)
96 | .tokenWithCredentials("username","password")
97 | .subscribeOn(Schedulers.io())
98 | .observeOn(AndroidSchedulers.mainThread())
99 | .subscribe(token->{
100 | //use your token
101 | //launch another activity passing the token
102 | //or save using the shared preferences
103 | //eventually you will use the token to do this SoundCloud.appendToken(token)
104 | });
105 | ```
106 | **2**. In order to get an authorization code, you need to open their url in a `WebView`.
107 | A pop-up window will be opened allowing the user to log in to SoundCloud and approve your app's authorization request.
108 |
109 | Approximately it will look like this:
110 |
111 | 
112 |
113 | To make the flow smoother, you can use a `redirect_uri` with a custom protocol scheme and set your app as a handler for that protocol scheme.
114 | That's how you should set up your activity in the manifest file:
115 | ```XML
116 |
117 |
118 |
119 |
120 |
121 |
124 |
125 |
126 | ```
127 | In the case above my `redirect_url` looks like this: `app_name://soundcloud/redirect`.
128 |
129 | Basically, I did this for you, you just need to handle the response in your activity.
130 | That's how the entire process should look like:
131 |
132 | ```java
133 | SoundCloudAuth.create(Config.CLIENT_ID,Config.CLIENT_SECRET_ID)
134 | .loginWithActivity(this,Config.REDIRECT_URI,REQUEST_CODE);
135 | ```
136 | It launches the login activity(that popup), which returns the authorization code wrapped into Intent.class.
137 | You need to handle this in the `onActivityResult()` method. Here's an example:
138 |
139 | ```java
140 | @Override
141 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
142 | super.onActivityResult(requestCode, resultCode, data);
143 | if(requestCode==REQUEST_CODE){
144 | if(resultCode==RESULT_OK){
145 | String string=data.getDataString();
146 | String code= Uri.parse(string).getQueryParameter("code");
147 | //get your token
148 | SoundCloudAuth.create(Config.CLIENT_ID,Config.CLIENT_SECRET_ID)
149 | .addRedirectUri(Config.REDIRECT_URI)
150 | .tokenWithAuthCode(code)
151 | .subscribeOn(Schedulers.io())
152 | .observeOn(AndroidSchedulers.mainThread())
153 | .subscribe(token->{
154 |
155 | });
156 | }
157 | }
158 | }
159 | ```
160 | Once you have received your authorization code, request an access token as showed above.
161 |
162 | **3**.If you received your token from using user's credentials, you will need to periodically refresh your access token when it expires. In order to achieve that, just call this method:
163 |
164 | ```java
165 | SoundCloudAuth.create(Config.CLIENT_ID, Config.CLIENT_SECRET_ID)
166 | .refreshToken(expiredToken)
167 | .subscribeOn(Schedulers.io())
168 | .observeOn(AndroidSchedulers.mainThread())
169 | .subscribe(token->{
170 | //use your token here
171 | });
172 | ```
173 |
174 | ## Additional Documentation And Support ##
175 | - The [SoundCloud API Documentation](https://developers.soundcloud.com/docs/api/reference).
176 | - The [SoundCloud API Discussion Group](https://groups.google.com/forum/#!forum/soundcloudapi).
177 | - If you have any questions or you have found some issues, feel free to write in the [Issue Section](https://github.com/vpaliyX/SoundCloud-API/issues).
178 |
179 | ## Even More Examples ##
180 | Let's suppose that you want to fetch playlists and you have an array of different names; you can solve the problem this way:
181 |
182 | ```java
183 | //just a dummy Single object
184 | Single> start = Single.just(new LinkedList<>());
185 | for(String name:names){
186 | //combine all of them
187 | start=Single.zip(start,service.searchPlaylists(PlaylistEntity
188 | .Filter.start()
189 | .byName(name)
190 | .createOptions())
191 | //if an error occurs, we want to skip it
192 | .onErrorResumeNext(Single.just(new ArrayList<>())),(first,second)->{
193 | if(second!=null){
194 | first.addAll(second);
195 | }
196 | return first;
197 | });
198 |
199 | //call all of them
200 | start.subscribeOn(Schedulers.io())
201 | .observeOn(AndroidSchedulers.mainThread())
202 | .subscribe(list->{
203 | //do something
204 | });
205 | ```
206 |
207 |
208 |
209 | ## The End. ##
210 |
211 | ``````
212 | MIT License
213 |
214 | Copyright (c) 2017 Vasyl Paliy
215 |
216 | Permission is hereby granted, free of charge, to any person obtaining a copy
217 | of this software and associated documentation files (the "Software"), to deal
218 | in the Software without restriction, including without limitation the rights
219 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
220 | copies of the Software, and to permit persons to whom the Software is
221 | furnished to do so, subject to the following conditions:
222 |
223 | The above copyright notice and this permission notice shall be included in all
224 | copies or substantial portions of the Software.
225 |
226 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
227 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
228 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
229 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
230 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
231 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
232 | SOFTWARE.
233 | ``````
234 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 |
5 | }
6 | android {
7 | compileSdkVersion 29
8 |
9 | defaultConfig {
10 | applicationId "com.example.myapplication"
11 | minSdkVersion 21
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 |
30 | packagingOptions {
31 | exclude 'META-INF/DEPENDENCIES.txt'
32 | exclude 'META-INF/LICENSE.txt'
33 | exclude 'META-INF/NOTICE.txt'
34 | exclude 'META-INF/NOTICE'
35 | exclude 'META-INF/LICENSE'
36 | exclude 'META-INF/DEPENDENCIES'
37 | exclude 'META-INF/notice.txt'
38 | exclude 'META-INF/license.txt'
39 | exclude 'META-INF/dependencies.txt'
40 | exclude 'META-INF/LGPL2.1'
41 | exclude 'META-INF/rxjava.properties'
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation project(':soundcloud-api@')
47 | implementation 'androidx.core:core-ktx:1.3.2'
48 | implementation 'androidx.appcompat:appcompat:1.2.0'
49 | implementation 'com.google.android.material:material:1.3.0'
50 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
51 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
52 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
53 | testImplementation 'junit:junit:4.+'
54 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
55 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
56 | }
57 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/vpaliy/Android/Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/vpaliy/soundcloud_api/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud_api;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.appcompat.app.AppCompatActivity;
6 |
7 | public class MainActivity extends AppCompatActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_single.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SoundCloud-API
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/vpaliy/soundcloud/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/art/sound_pop_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/art/sound_pop_up.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | apply from: 'dependencies.gradle'
2 |
3 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
4 | buildscript {
5 | ext.kotlin_version = "1.3.72"
6 | repositories {
7 | google()
8 | jcenter()
9 | }
10 | dependencies {
11 | classpath "com.android.tools.build:gradle:4.1.3"
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | classpath 'me.tatarka:gradle-retrolambda:3.6.0'
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
--------------------------------------------------------------------------------
/dependencies.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | /* Android Configuration */
3 | configuration =
4 | [
5 | package : "com.vpaliy.soundcloud_api",
6 | compileSdkVersion : 29,
7 | minSdkVersion : 21,
8 | targetSdkVersion : 29,
9 | versionCode : 1,
10 | versionName : "1.0",
11 | testInstrumentationRunner: "android.support.test.runner.AndroidJUnitRunner"
12 |
13 | ]
14 |
15 | /* Library versions */
16 | rxJavaVersion = '3.0.12'
17 | rxAndroidVersion = '3.0.0'
18 | javaxAnnotationVersion = '1.0'
19 | gsonVersion = '2.8.6'
20 | okHttpVersion = '4.9.0'
21 | retrofitVersion = "2.9.0"
22 | retrofitConverterVersion = "2.1.0"
23 | retrofitAdapterVersion = "2.2.0"
24 |
25 |
26 | /* Testing versions */
27 | robolectricVersion = '3.1.1'
28 | jUnitVersion = '4.12'
29 | assertJVersion = '1.7.1'
30 | mockitoVersion = '2.8.47'
31 | dexmakerVersion = '1.0'
32 | espressoVersion = '2.0'
33 | testingSupportLibVersion = '0.1'
34 |
35 | /* Other stuff */
36 | leakCanaryVersion = '1.4'
37 |
38 | /** Main dependencies **/
39 | dependencies = [
40 | RxJava : "io.reactivex.rxjava3:rxjava:${rxJavaVersion}",
41 | RxAndroid : "io.reactivex.rxjava3:rxandroid:${rxAndroidVersion}",
42 | javaxAnnotation : "javax.annotation:jsr250-api:${javaxAnnotationVersion}",
43 | gson : "com.google.code.gson:gson:${gsonVersion}",
44 | okHttp : "com.squareup.okhttp3:okhttp:${okHttpVersion}",
45 | retrofit : "com.squareup.retrofit2:retrofit:${retrofitVersion}",
46 | retrofitConverter: "com.squareup.retrofit2:converter-gson:${retrofitConverterVersion}",
47 | retrofitAdapter : 'com.squareup.retrofit2:adapter-rxjava2:2.2.0',
48 | leakCanary : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
49 | ]
50 |
51 | testDependencies = [
52 | jUnit : "junit:junit:${jUnitVersion}",
53 | mockito : "org.mockito:mockito-core:${mockitoVersion}",
54 | testingSupportLib: "com.android.support.test:testing-support-lib:${testingSupportLibVersion}",
55 | ]
56 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vpaliy/SoundCloud-API/f11c2018619c2210892e2c457bf8743ba3cdf882/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 07 13:28:48 EDT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':soundcloud-api@'
2 |
--------------------------------------------------------------------------------
/soundcloud-api@/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/soundcloud-api@/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def config = rootProject.ext.configuration;
4 | def libs = rootProject.ext.dependencies;
5 | def testLibs = rootProject.ext.testDependencies;
6 |
7 | android {
8 | compileSdkVersion config.compileSdkVersion
9 |
10 | defaultConfig {
11 | minSdkVersion config.minSdkVersion
12 | targetSdkVersion config.targetSdkVersion
13 | versionCode config.versionCode
14 | }
15 |
16 | compileOptions {
17 | sourceCompatibility JavaVersion.VERSION_1_8
18 | targetCompatibility JavaVersion.VERSION_1_8
19 | }
20 |
21 | }
22 |
23 | dependencies {
24 | implementation 'androidx.appcompat:appcompat:1.2.0'
25 |
26 | implementation libs.RxJava
27 | implementation libs.retrofit
28 | implementation libs.retrofitAdapter
29 | implementation libs.retrofitConverter
30 | implementation libs.okHttp
31 | implementation libs.gson
32 | implementation libs.RxAndroid
33 | implementation libs.javaxAnnotation
34 | testImplementation testLibs.jUnit
35 | }
36 |
--------------------------------------------------------------------------------
/soundcloud-api@/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/vpaliy/Android/Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/Endpoints.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud;
2 |
3 | @SuppressWarnings({"UnusedDeclaration"})
4 | public interface Endpoints {
5 | String TOKEN = "oauth2/token";
6 |
7 | String TRACKS = "tracks";
8 | String TRACK_DETAILS = "tracks/{id}";
9 | String TRACK_COMMENTS = "tracks/{id}/comments";
10 | String TRACK_COMMENT = "tracks/{id}/comments/{comment-id}";
11 | String TRACK_FAVORITERS = "tracks/{id}/favoriters";
12 | String TRACK_FAVORITER = "tracks/{id}/favoriters/{user-id}";
13 | String TRACK_SECRET_TOKEN = "tracks/{id}/secret-token";
14 |
15 | String PLAYLISTS = "playlists";
16 | String PLAYLIST_DETAILS = "playlists/{id}";
17 | String PLAYLIST_SECRET_TOKEN = "playlists/{id}/secret-token";
18 | String PLAYLIST_TRACKS = "playlists/{id}/tracks";
19 |
20 | String USERS = "users";
21 | String USER_DETAILS = "users/{id}";
22 | String USER_FOLLOWINGS = "users/{id}/followings";
23 | String USER_FOLLOWERS = "users/{id}/followers";
24 | String USER_FOLLOWING = "users/{id}/followings/{following-id}";
25 | String USER_FOLLOWER = "users/{id}/followers/{follower-id}";
26 | String USER_TRACKS = "users/{id}/tracks";
27 | String USER_COMMENTS = "user/{id}/comments";
28 | String USER_FAVORITES = "users/{id}/favorites";
29 | String USER_FAVORITE = "users/{id}/favorite/{favorite-id}";
30 | String USER_PLAYLISTS = "users/{id}/playlists";
31 | String USER_WEB_PROFILES = "users/{id}/web-profiles";
32 |
33 | String ME = "me";
34 | String ME_CONNECTIONS = "me/connections";
35 | String ME_CONNECTION = "me/connections/{id}";
36 | String ME_ACTIVITIES = "me/activities/tracks";
37 | String ME_EXCLUSIVE_TRACKS = "me/activities/tracks/exclusive";
38 | String ME_NEWS = "me/activities/all/own";
39 | String ME_CONFIRMATION = "me/email-confirmations";
40 | String ME_FRIENDS = "me/connections/friends";
41 | String ME_DEVICES = "me/devices";
42 | String ME_FAVORITE_TRACK = "me/favorites/{id}";
43 | String ME_FOLLOW = "me/followings/{id}";
44 | String ME_FAVORITE_TRACKS = "me/favorites";
45 | String ME_FOLLOWERS = "me/followings";
46 |
47 | String SUGGESTED_USERS = "users/suggested";
48 |
49 | String RESOLVE = "resolve";
50 |
51 | String APPS = "apps";
52 |
53 | String SEND_PASSWORD = "passwords/reset-instructions";
54 | String CONNECT = "connect";
55 | String FACEBOOK_CONNECT = "connect/via/facebook";
56 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/SoundCloud.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud;
2 |
3 |
4 | import android.content.Context;
5 |
6 | import com.google.gson.Gson;
7 | import com.google.gson.GsonBuilder;
8 | import com.vpaliy.soundcloud.model.Token;
9 | import com.vpaliy.soundcloud.utils.Adapter;
10 |
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import okhttp3.Cache;
14 | import okhttp3.HttpUrl;
15 | import okhttp3.Interceptor;
16 | import okhttp3.OkHttpClient;
17 | import okhttp3.Request;
18 | import retrofit2.Retrofit;
19 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
20 | import retrofit2.converter.gson.GsonConverterFactory;
21 |
22 | public class SoundCloud {
23 |
24 | private final SoundCloudService soundCloudService;
25 |
26 | private SoundCloud(SoundCloudService soundCloudService) {
27 | this.soundCloudService = soundCloudService;
28 | }
29 |
30 | public SoundCloudService getSoundCloudService() {
31 | return soundCloudService;
32 | }
33 |
34 | public static class Builder {
35 | private static final String API_QUERY = "client_id";
36 | private static final String OAUTH_TOKEN = "oauth_token";
37 |
38 | private static final long CACHE_SIZE = 10 * 1024 * 1024;
39 | private static final int CONNECT_TIMEOUT = 60;
40 | private static final int WRITE_TIMEOUT = 60;
41 | private static final int READ_TIMEOUT = 60;
42 |
43 | private final String apiKey;
44 | private final Context context;
45 |
46 | private Token token;
47 | private Interceptor interceptor;
48 | private OkHttpClient okHttpClient;
49 |
50 | public Builder(Context context, String apiKey) {
51 | if (apiKey == null || context == null) {
52 | throw new IllegalArgumentException("ClientId or Context cannot be null");
53 | }
54 | this.apiKey = apiKey;
55 | this.context = context;
56 | }
57 |
58 | public Builder setInterceptor(Interceptor interceptor) {
59 | this.interceptor = interceptor;
60 | return this;
61 | }
62 |
63 | public Builder setOkHttpClient(OkHttpClient client) {
64 | this.okHttpClient = okHttpClient;
65 | return this;
66 | }
67 |
68 | public Builder setToken(Token token) {
69 | this.token = token;
70 | return this;
71 | }
72 |
73 | private OkHttpClient provideOkHttpClient(Context context, Interceptor interceptor) {
74 | OkHttpClient.Builder builder = new OkHttpClient.Builder()
75 | .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
76 | .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
77 | .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
78 | .addInterceptor(provideDefaultInterceptor())
79 | .cache(new Cache(context.getCacheDir(), CACHE_SIZE));
80 | if (interceptor != null) {
81 | builder.addInterceptor(interceptor);
82 | }
83 | return builder.build();
84 | }
85 |
86 | private Interceptor provideDefaultInterceptor() {
87 | return chain -> {
88 | final Request request = chain.request();
89 | final HttpUrl url = request.url();
90 | final HttpUrl.Builder newUrlBuilder = url.newBuilder()
91 | .addEncodedQueryParameter(API_QUERY, apiKey);
92 | if (token != null) {
93 | newUrlBuilder.addEncodedQueryParameter(OAUTH_TOKEN, token.access);
94 | }
95 | final Request newRequest = request.newBuilder()
96 | .url(newUrlBuilder.build())
97 | .build();
98 | return chain.proceed(newRequest);
99 | };
100 | }
101 |
102 | private Retrofit provideRetrofit(OkHttpClient okHttpClient) {
103 | Gson gson = new GsonBuilder()
104 | .registerTypeAdapterFactory(new Adapter())
105 | .create();
106 | return new Retrofit.Builder()
107 | .baseUrl(buildBaseUrl())
108 | .addConverterFactory(GsonConverterFactory.create(gson))
109 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
110 | .client(okHttpClient)
111 | .build();
112 | }
113 |
114 | private HttpUrl buildBaseUrl() {
115 | return new HttpUrl.Builder()
116 | .scheme("https")
117 | .host("api.soundcloud.com")
118 | .build();
119 | }
120 |
121 | public SoundCloud build() {
122 | if (okHttpClient == null) {
123 | okHttpClient = provideOkHttpClient(context, interceptor);
124 | }
125 | final SoundCloudService service = provideRetrofit(okHttpClient).create(SoundCloudService.class);
126 | return new SoundCloud(service);
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/SoundCloudService.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud;
2 |
3 | import com.vpaliy.soundcloud.model.AppEntity;
4 | import com.vpaliy.soundcloud.model.MyActivityEntity;
5 | import com.vpaliy.soundcloud.model.CommentEntity;
6 | import com.vpaliy.soundcloud.model.ConnectionEntity;
7 | import com.vpaliy.soundcloud.model.Page;
8 | import com.vpaliy.soundcloud.model.PlaylistEntity;
9 | import com.vpaliy.soundcloud.model.SecretToken;
10 | import com.vpaliy.soundcloud.model.TrackEntity;
11 | import com.vpaliy.soundcloud.model.UserEntity;
12 | import com.vpaliy.soundcloud.model.WebProfileEntity;
13 |
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | import io.reactivex.Completable;
18 | import io.reactivex.Single;
19 | import retrofit2.http.DELETE;
20 | import retrofit2.http.GET;
21 | import retrofit2.http.PUT;
22 | import retrofit2.http.Path;
23 | import retrofit2.http.QueryMap;
24 |
25 | @SuppressWarnings("unused")
26 | public interface SoundCloudService {
27 |
28 | /**
29 | * Tracks
30 | **/
31 |
32 | @GET(Endpoints.TRACKS)
33 | Single> searchTracks(@QueryMap Map options);
34 |
35 | @GET(Endpoints.TRACKS)
36 | Single> searchTracksPage(@QueryMap Map options);
37 |
38 | @GET(Endpoints.TRACK_DETAILS)
39 | Single fetchTrack(@Path("id") String id);
40 |
41 | @GET(Endpoints.TRACK_COMMENTS)
42 | Single> fetchTrackComments(@Path("id") String id);
43 |
44 | @GET(Endpoints.TRACK_COMMENT)
45 | Single fetchTrackComment(@Path("id") String id);
46 |
47 | @GET(Endpoints.TRACK_FAVORITERS)
48 | Single> fetchTrackFavoriters(@Path("id") String id);
49 |
50 | @GET(Endpoints.TRACK_FAVORITER)
51 | Single fetchTrackFavoriter(@Path("id") String id, @Path("user-id") String userId);
52 |
53 | @GET(Endpoints.TRACK_SECRET_TOKEN)
54 | Single fetchTrackSecretToken(@Path("id") String id);
55 |
56 | /**
57 | * Playlists
58 | **/
59 |
60 | @GET(Endpoints.PLAYLISTS)
61 | Single> searchPlaylists(@QueryMap Map options);
62 |
63 | @GET(Endpoints.PLAYLISTS)
64 | Single> searchPlaylistsPage(@QueryMap Map options);
65 |
66 | @GET(Endpoints.PLAYLIST_DETAILS)
67 | Single fetchPlaylist(@Path("id") String id);
68 |
69 | @GET(Endpoints.PLAYLIST_TRACKS)
70 | Single> fetchPlaylistTracks(@Path("id") String id);
71 |
72 | @GET(Endpoints.PLAYLIST_SECRET_TOKEN)
73 | Single fetchPlaylistSecretToken(@Path("id") String id);
74 |
75 | /**
76 | * Users
77 | **/
78 |
79 | @GET(Endpoints.USERS)
80 | Single> searchUsers(@QueryMap Map options);
81 |
82 | @GET(Endpoints.USERS)
83 | Single> searchUsersPage(@QueryMap Map options);
84 |
85 | @GET(Endpoints.USER_DETAILS)
86 | Single fetchUser(@Path("id") String id);
87 |
88 | @GET(Endpoints.USER_TRACKS)
89 | Single> fetchUserTracks(@Path("id") String id);
90 |
91 | @GET(Endpoints.USER_PLAYLISTS)
92 | Single> fetchUserPlaylists(@Path("id") String id);
93 |
94 | @GET(Endpoints.USER_FOLLOWERS)
95 | Single> fetchUserFollowers(@Path("id") String id);
96 |
97 | @GET(Endpoints.USER_FOLLOWINGS)
98 | Single> fetchUserFollowings(@Path("id") String id);
99 |
100 | @GET(Endpoints.USER_FOLLOWER)
101 | Single fetchUserFollower(@Path("id") String userId, @Path("follower-id") String followerId);
102 |
103 | @GET(Endpoints.USER_FOLLOWING)
104 | Single fetchUserFollowing(@Path("id") String userId, @Path("following-id") String followingId);
105 |
106 | @GET(Endpoints.USER_COMMENTS)
107 | Single> fetchUserComments(@Path("id") String id);
108 |
109 | @GET(Endpoints.USER_FAVORITES)
110 | Single> fetchUserFavoriteTracks(@Path("id") String id);
111 |
112 | @GET(Endpoints.USER_FAVORITE)
113 | Single fetchUserFavoriteTrack(@Path("id") String id, @Path("favorite-id") String trackId);
114 |
115 | @GET(Endpoints.USER_WEB_PROFILES)
116 | Single> fetchUserWebProfiles(@Path("id") String id);
117 |
118 |
119 | /**
120 | * ME
121 | **/
122 |
123 | @GET(Endpoints.ME)
124 | Single me();
125 |
126 | @GET(Endpoints.ME_ACTIVITIES)
127 | Single> fetchMyConnections();
128 |
129 | @GET(Endpoints.ME_FOLLOWERS)
130 | Single> fetchMyFollowings();
131 |
132 | @GET(Endpoints.ME_FAVORITE_TRACKS)
133 | Single> fetchMyFavoriteTracks();
134 |
135 | @GET(Endpoints.ME_CONNECTION)
136 | Single fetchMyConnection(@Path("id") String connectionId);
137 |
138 | @GET(Endpoints.ME_ACTIVITIES)
139 | Single> fetchMyActivities();
140 |
141 | @GET(Endpoints.SUGGESTED_USERS)
142 | Single> fetchSuggestedUsers();
143 |
144 | @PUT(Endpoints.ME_FAVORITE_TRACK)
145 | Completable loveTrack(@Path("id") String id);
146 |
147 | @DELETE(Endpoints.ME_FAVORITE_TRACK)
148 | Completable unloveTrack(@Path("id") String id);
149 |
150 | @PUT(Endpoints.ME_FOLLOW)
151 | Completable followUser(@Path("id") String id);
152 |
153 | @DELETE(Endpoints.ME_FOLLOW)
154 | Completable unfollowUser(@Path("id") String id);
155 |
156 | @GET(Endpoints.ME_FOLLOW)
157 | Completable amIFollowing(@Path("id") String id);
158 |
159 | @GET(Endpoints.ME_FAVORITE_TRACK)
160 | Completable didILike(@Path("id") String id);
161 |
162 | /**
163 | * APPS
164 | **/
165 |
166 | @GET(Endpoints.APPS)
167 | Single> fetchApps();
168 | }
169 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/auth/AuthService.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.auth;
2 |
3 | import com.vpaliy.soundcloud.Endpoints;
4 | import com.vpaliy.soundcloud.model.Token;
5 |
6 | import java.util.Map;
7 |
8 | import io.reactivex.Single;
9 | import retrofit2.http.FormUrlEncoded;
10 | import retrofit2.http.FieldMap;
11 | import retrofit2.http.POST;
12 |
13 | interface AuthService {
14 | @FormUrlEncoded
15 | @POST(Endpoints.TOKEN)
16 | Single requestToken(@FieldMap Map authMap);
17 | }
18 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/auth/Connect.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.auth;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | @SuppressWarnings({"unused", "WeakerAccess"})
7 | class Connect implements Parcelable {
8 | final String url;
9 | final String redirectUri;
10 |
11 | public Connect(Parcel parcel) {
12 | this.url = parcel.readString();
13 | this.redirectUri = parcel.readString();
14 | }
15 |
16 | public Connect(String url, String redirectUri) {
17 | this.url = url;
18 | this.redirectUri = redirectUri;
19 | }
20 |
21 | @Override
22 | public int describeContents() {
23 | return 0;
24 | }
25 |
26 | @Override
27 | public void writeToParcel(Parcel dest, int flags) {
28 | dest.writeString(url);
29 | dest.writeString(redirectUri);
30 | }
31 |
32 | public static Creator CREATOR = new Creator() {
33 | @Override
34 | public Connect createFromParcel(Parcel source) {
35 | return new Connect(source);
36 | }
37 |
38 | @Override
39 | public Connect[] newArray(int size) {
40 | return new Connect[size];
41 | }
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/auth/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.auth;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.webkit.WebView;
8 | import android.webkit.WebViewClient;
9 |
10 | import com.vpaliy.soundcloud.R;
11 |
12 | import androidx.annotation.Nullable;
13 | import androidx.appcompat.app.AppCompatActivity;
14 |
15 | public class LoginActivity extends AppCompatActivity {
16 |
17 | static final String EXTRA_CONNECT_DATA = LoginActivity.class.getCanonicalName() + "Extra.Connect.Data";
18 | private Connect connect;
19 |
20 | @SuppressLint("SetJavaScriptEnabled")
21 | @Override
22 | protected void onCreate(@Nullable Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_web);
25 | WebView webView = findViewById(R.id.web);
26 | webView.getSettings().setJavaScriptEnabled(true);
27 | webView.setWebViewClient(new WebViewAuthClient());
28 | connect = getIntent().getParcelableExtra(EXTRA_CONNECT_DATA);
29 | if (connect.url != null) {
30 | webView.loadUrl(connect.url);
31 | } else {
32 | finish();
33 | }
34 | }
35 |
36 | private class WebViewAuthClient extends WebViewClient {
37 |
38 | private final Intent resultIntent = new Intent();
39 |
40 | @Override
41 | public void onPageFinished(WebView view, String url) {
42 | super.onPageFinished(view, url);
43 |
44 | if (url.startsWith(connect.redirectUri)) {
45 | Uri data = Uri.parse(url);
46 | resultIntent.setData(data);
47 | if (url.contains("?code=")) {
48 | setResult(RESULT_OK, resultIntent);
49 | } else {
50 | setResult(RESULT_CANCELED, resultIntent);
51 | }
52 | finish();
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/auth/SoundCloudAuth.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.auth;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.text.TextUtils;
6 |
7 | import com.vpaliy.soundcloud.model.Token;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | import io.reactivex.Single;
13 | import okhttp3.HttpUrl;
14 | import okhttp3.Interceptor;
15 | import okhttp3.OkHttpClient;
16 | import okhttp3.Request;
17 | import retrofit2.Retrofit;
18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
19 | import retrofit2.converter.gson.GsonConverterFactory;
20 |
21 | @SuppressWarnings({"unused"})
22 | public class SoundCloudAuth {
23 |
24 | private String clientId;
25 | private String clientSecret;
26 | private String redirectUri;
27 |
28 | private String GRANT_TYPE = "grant_type";
29 | private String CLIENT_ID = "client_id";
30 | private String CLIENT_SECRET = "client_secret";
31 | private String USERNAME = "username";
32 | private String REDIRECT_URI = "redirect_uri";
33 | private String CODE = "code";
34 | private String RESPONSE_TYPE = "response_type";
35 | private String SCOPE = "scope";
36 | private String DISPLAY = "display";
37 | private String STATE = "state";
38 | private String PASSWORD = "password";
39 |
40 |
41 | private SoundCloudAuth(String clientId, String clientSecret) {
42 | if (clientId == null || clientSecret == null) {
43 | throw new IllegalArgumentException("client_id or client_secret is null");
44 | }
45 | this.clientId = clientId;
46 | this.clientSecret = clientSecret;
47 | }
48 |
49 | @SuppressWarnings("WeakerAccess")
50 | public class GrantType {
51 | public static final String AUTH_CODE = "authorization_code";
52 | public static final String REFRESH_TOKEN = "refresh_token";
53 | public static final String PASSWORD = "password";
54 | public static final String CLIENT_CREDENTIALS = "client_credentials";
55 | public static final String OAUTH1_TOKEN = "oauth1_token";
56 | }
57 |
58 |
59 | private Interceptor buildInterceptor() {
60 | return (chain -> {
61 | Request originalRequest = chain.request();
62 | HttpUrl originalHttpUrl = originalRequest.url();
63 | HttpUrl newHttpUrl = originalHttpUrl.newBuilder()
64 | .setQueryParameter(CLIENT_ID, clientId)
65 | .build();
66 | Request newRequest = originalRequest.newBuilder()
67 | .url(newHttpUrl).build();
68 | return chain.proceed(newRequest);
69 | });
70 | }
71 |
72 | private Retrofit buildRetrofit() {
73 | OkHttpClient okHttpClient = new OkHttpClient.Builder()
74 | .addInterceptor(buildInterceptor())
75 | .build();
76 | return new Retrofit.Builder()
77 | .baseUrl("https://api.soundcloud.com/")
78 | .addConverterFactory(GsonConverterFactory.create())
79 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
80 | .client(okHttpClient)
81 | .build();
82 | }
83 |
84 | public SoundCloudAuth addRedirectUri(String redirectUri) {
85 | this.redirectUri = redirectUri;
86 | return this;
87 | }
88 |
89 | private AuthService createService() {
90 | return buildRetrofit().create(AuthService.class);
91 | }
92 |
93 | public Single tokenWithAuthCode(String code) {
94 | if (TextUtils.isEmpty(code)) {
95 | throw new IllegalArgumentException("auth is empty");
96 | }
97 | AuthService service = createService();
98 | Map fieldMap = new HashMap<>();
99 | fieldMap.put(CLIENT_ID, clientId);
100 | fieldMap.put(CLIENT_SECRET, clientSecret);
101 | fieldMap.put(GRANT_TYPE, GrantType.AUTH_CODE);
102 | fieldMap.put(CODE, code);
103 | if (redirectUri != null) {
104 | fieldMap.put(REDIRECT_URI, redirectUri);
105 | }
106 | return service.requestToken(fieldMap);
107 | }
108 |
109 | public Single tokenWithCredentials(String username, String password) {
110 | if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
111 | throw new IllegalArgumentException("username or password is null");
112 | }
113 | AuthService service = createService();
114 | Map fieldMap = new HashMap<>();
115 | fieldMap.put(CLIENT_ID, clientId);
116 | fieldMap.put(CLIENT_SECRET, clientSecret);
117 | fieldMap.put(GRANT_TYPE, GrantType.PASSWORD);
118 | fieldMap.put(USERNAME, username);
119 | fieldMap.put(PASSWORD, password);
120 | if (redirectUri != null) {
121 | fieldMap.put(REDIRECT_URI, redirectUri);
122 | }
123 | return service.requestToken(fieldMap);
124 | }
125 |
126 | public Single refreshToken(Token token) {
127 | if (token == null || TextUtils.isEmpty(token.refresh)) {
128 | throw new IllegalArgumentException("Wrong token");
129 | }
130 | AuthService service = createService();
131 | Map fieldMap = new HashMap<>();
132 | fieldMap.put(CLIENT_ID, clientId);
133 | fieldMap.put(CLIENT_SECRET, clientSecret);
134 | fieldMap.put(GrantType.REFRESH_TOKEN, token.refresh);
135 | fieldMap.put(GRANT_TYPE, GrantType.REFRESH_TOKEN);
136 | if (redirectUri != null) {
137 | fieldMap.put(REDIRECT_URI, redirectUri);
138 | }
139 | return service.requestToken(fieldMap);
140 | }
141 |
142 | public void loginWithActivity(Activity activity, String redirectUri, int requestCode) {
143 | if (TextUtils.isEmpty(redirectUri)) {
144 | throw new IllegalArgumentException("redirectUri is empty");
145 | }
146 | String url = "https://www.soundcloud.com/connect?" +
147 | "client_id=" + clientId +
148 | "&redirect_uri=" + redirectUri +
149 | "&response_type=" + "code" +
150 | "&scope=" + "non-expiring" +
151 | "&display=" + "popup" +
152 | "&state=" + "asdf";
153 | Connect connect = new Connect(url, redirectUri);
154 | Intent intent = new Intent(activity, LoginActivity.class);
155 | intent.putExtra(LoginActivity.EXTRA_CONNECT_DATA, connect);
156 | activity.startActivityForResult(intent, requestCode);
157 | }
158 |
159 | public static SoundCloudAuth create(String clientId, String clientSecret) {
160 | return new SoundCloudAuth(clientId, clientSecret);
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/AppEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class AppEntity {
4 | public String id;
5 | public String kind;
6 | public String name;
7 | public String uri;
8 | public String permalink_url;
9 | public String external_url;
10 | public String creator;
11 |
12 | public AppEntity() {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/CommentEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class CommentEntity {
4 | public String id;
5 | public String uri;
6 | public String created_at;
7 | public String body;
8 | public String timestamp;
9 | public String user_id;
10 | public MiniUserEntity user;
11 | public String track_id;
12 |
13 | public CommentEntity() {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/ConnectionEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class ConnectionEntity {
4 | public String created_at;
5 | public String display_name;
6 | public String id;
7 | public String post_favorite;
8 | public String post_publish;
9 | public String service;
10 | public String type;
11 | public String uri;
12 |
13 | public ConnectionEntity() {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/MeEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | public class MeEntity extends UserEntity {
6 |
7 | public String plan;
8 | public long private_tracks_count;
9 | public long private_playlists_count;
10 | @SerializedName("primary_email_confirmed")
11 | public boolean isEmailConfirmed;
12 |
13 | public MeEntity() {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/MiniUserEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class MiniUserEntity {
4 | public String avatar_url;
5 | public String id;
6 | public String kind;
7 | public String last_modified;
8 | public String permalink;
9 | public String permalink_url;
10 | public String uri;
11 | public String username;
12 |
13 | public MiniUserEntity() {
14 | }
15 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/MyActivityEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class MyActivityEntity {
4 |
5 | public String type;
6 | public String created_at;
7 | public String tags;
8 |
9 | public Origin origin;
10 |
11 | public MyActivityEntity() {
12 | }
13 |
14 | public class Origin {
15 | public TrackEntity track;
16 | public CommentEntity comment;
17 | public MiniUserEntity miniUser;
18 |
19 | public Origin() {
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/Page.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | import android.net.Uri;
4 | import android.text.TextUtils;
5 |
6 | import com.vpaliy.soundcloud.utils.Adapter;
7 |
8 | import java.util.List;
9 |
10 | import com.google.gson.annotations.SerializedName;
11 |
12 | public class Page implements Adapter.PostProcessable {
13 |
14 | public List collection;
15 |
16 | @SerializedName("next_href")
17 | private String next_href;
18 |
19 | public boolean isLast;
20 | public int futureOffset;
21 | public String query;
22 |
23 | public Page() {
24 | }
25 |
26 | @Override
27 | public void postProcess() {
28 | isLast = next_href == null || TextUtils.isEmpty(next_href);
29 | futureOffset = toNumber("offset=");
30 | query();
31 |
32 | }
33 |
34 | private void query() {
35 | if (next_href != null) {
36 | int index = next_href.indexOf("&q=") + 3;
37 | if (next_href.length() > index) {
38 | int lastIndex = next_href.indexOf("&", index);
39 | query = next_href.substring(index, lastIndex != -1 ? lastIndex : next_href.length());
40 | query = Uri.decode(query);
41 | }
42 | }
43 | }
44 |
45 | private int toNumber(String pattern) {
46 | if (next_href != null) {
47 | int index = next_href.indexOf(pattern);
48 | int number = 0;
49 | while (true) {
50 | int tempIndex = index + pattern.length();
51 | if (next_href.length() <= tempIndex || !Character.isDigit(next_href.charAt(tempIndex))) {
52 | break;
53 | }
54 | number *= 10;
55 | number += next_href.charAt(tempIndex) - '0';
56 | index++;
57 | }
58 | return number;
59 | }
60 | return -1;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/PlaylistEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | @SuppressWarnings("unused")
10 | public class PlaylistEntity {
11 | public String id;
12 | public String created_at;
13 | public String user_id;
14 | public String duration;
15 | public String sharing;
16 | public String tag_list;
17 | public String permalink;
18 | public String track_count;
19 |
20 | @SerializedName("streamable")
21 | public boolean is_streamable;
22 |
23 | @SerializedName("downloadable")
24 | public boolean is_downloadable;
25 | public String embeddable_by;
26 | public String purchase_url;
27 | public String type;
28 | public String label;
29 | public String label_id;
30 | public String label_name;
31 | public String playlist_type;
32 | public String ean;
33 | public String description;
34 | public String genre;
35 | public String release;
36 | public String purchase_title;
37 | public String title;
38 | public String release_year;
39 | public String release_month;
40 | public String release_day;
41 | public String license;
42 | public String uri;
43 | public String permalink_url;
44 | public String artwork_url;
45 | public MiniUserEntity user;
46 | public List tracks;
47 |
48 | public PlaylistEntity() {
49 | }
50 |
51 | public static class PlaylistType {
52 | public static final String EP_SINGLE = "ep single";
53 | public static final String ALBUM = "album";
54 | public static final String COMPILATION = "compilation";
55 | public static final String PROJECT_FILES = "project files";
56 | public static final String ARCHIVE = "archive";
57 | public static final String SHOWCASE = "showcase";
58 | public static final String DEMO = "demo";
59 | public static final String SAMPLE_PACK = "sample pack";
60 | public static final String OTHER = "other";
61 | }
62 |
63 | public static class EmbeddableBy {
64 | public static final String ALL = "all";
65 | public static final String ME = "me";
66 | public static final String NONE = "none";
67 | }
68 |
69 | public enum Representation {
70 | ID("id"),
71 | COMPACT("compact");
72 | public String name;
73 |
74 | Representation(String name) {
75 | this.name = name;
76 | }
77 | }
78 |
79 | @SuppressWarnings({"unused", "WeakerAccess"})
80 | public static class Filter {
81 |
82 | private Map options;
83 | private Page> page;
84 |
85 | public Filter() {
86 | options = new HashMap<>();
87 | }
88 |
89 | public Filter byRepresentation(Representation representation) {
90 | options.put("representation", representation.name);
91 | return this;
92 | }
93 |
94 | public Filter byName(String name) {
95 | options.put("q", name);
96 | return this;
97 | }
98 |
99 | public Filter nextPage(Page> page) {
100 | this.page = page;
101 | return this;
102 | }
103 |
104 | public Filter limit(int limit) {
105 | options.put("limit", limit);
106 | return this;
107 | }
108 |
109 | public Filter withPagination() {
110 | options.put("linked_partitioning", -1);
111 | return this;
112 | }
113 |
114 | public Filter offset(int offset) {
115 | options.put("offset", offset);
116 | return this;
117 | }
118 |
119 | public static Filter start() {
120 | return new Filter();
121 | }
122 |
123 | public Map createOptions() {
124 | if (page != null) {
125 | if (!page.isLast) {
126 | withPagination();
127 | options.put("offset", page.futureOffset);
128 | options.put("q", page.query);
129 | }
130 | }
131 | page = null;
132 | return options;
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/SecretToken.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class SecretToken {
4 | public String kind;
5 | public String token;
6 | public String uri;
7 | public String resource_uri;
8 |
9 | public SecretToken() {
10 | }
11 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/Token.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 |
4 | import com.google.gson.annotations.SerializedName;
5 |
6 | import java.util.Date;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | /**
11 | * Represents an OAuth2 access/refresh token pair.
12 | */
13 | @SuppressWarnings("WeakerAccess")
14 | public class Token {
15 |
16 | public static final String SCOPE_DEFAULT = "*";
17 |
18 | public static final String SCOPE_SIGNUP = "signup";
19 | public static final String SCOPE_PLAYCOUNT = "playcount";
20 | public static final String SCOPE_NON_EXPIRING = "non-expiring";
21 |
22 | private static final String ACCESS_TOKEN = "access_token";
23 | private static final String REFRESH_TOKEN = "refresh_token";
24 | private static final String SCOPE = "scope";
25 | private static final String EXPIRES_IN = "expires_in";
26 |
27 | @SerializedName("access_token")
28 | public String access;
29 | public String refresh;
30 | public String scope;
31 | public long expiresIn;
32 |
33 | public Token() {
34 | }
35 |
36 | public final Map customParameters = new HashMap();
37 |
38 | /**
39 | * Invalidates the access token
40 | */
41 | public void invalidate() {
42 | this.access = null;
43 | }
44 |
45 | /**
46 | * @return null or the date of expiration of this token
47 | */
48 | public Date getExpiresIn() {
49 | return expiresIn == 0 ? null : new Date(expiresIn);
50 | }
51 |
52 | public boolean defaultScoped() {
53 | return scoped(SCOPE_DEFAULT);
54 | }
55 |
56 | /**
57 | * @return has token the signup scope ("signup")
58 | */
59 | public boolean signupScoped() {
60 | return scoped(SCOPE_SIGNUP);
61 | }
62 |
63 | public boolean scoped(String scope) {
64 | if (this.scope != null) {
65 | for (String s : this.scope.split(" "))
66 | if (scope.equals(s)) return true;
67 | }
68 | return false;
69 | }
70 |
71 | public boolean valid() {
72 | return access != null && (scoped(SCOPE_NON_EXPIRING) || refresh != null);
73 | }
74 |
75 | @Override
76 | public String toString() {
77 | return "Token{" +
78 | "access='" + access + '\'' +
79 | ", refresh='" + refresh + '\'' +
80 | ", scope='" + scope + '\'' +
81 | ", expires=" + getExpiresIn() +
82 | '}';
83 | }
84 |
85 | @Override
86 | public boolean equals(Object o) {
87 | if (this == o) return true;
88 | if (o == null) return false;
89 |
90 | if (o instanceof Token) {
91 | Token token = (Token) o;
92 | if (access != null ? !access.equals(token.access) : token.access != null) return false;
93 | if (refresh != null ? !refresh.equals(token.refresh) : token.refresh != null)
94 | return false;
95 | if (scope != null ? !scope.equals(token.scope) : token.scope != null) return false;
96 | return true;
97 | } else {
98 | return super.equals(o);
99 | }
100 | }
101 |
102 | @Override
103 | public int hashCode() {
104 | int result = access != null ? access.hashCode() : 0;
105 | result = 31 * result + (refresh != null ? refresh.hashCode() : 0);
106 | result = 31 * result + (scope != null ? scope.hashCode() : 0);
107 | return result;
108 | }
109 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/TrackEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | import java.util.Arrays;
4 | import java.util.Date;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import com.google.gson.annotations.SerializedName;
9 |
10 | @SuppressWarnings("unused")
11 | public class TrackEntity {
12 |
13 | public String id;
14 | public String created_at;
15 | public String user_id;
16 | public MiniUserEntity user;
17 | public String title;
18 | public String permalink;
19 | public String permalink_url;
20 | public String uri;
21 | public String sharing;
22 | public String embeddable_by;
23 | public String purchase_url;
24 | public String artwork_url;
25 | public String description;
26 | public String duration;
27 | public String genre;
28 | public String tag_list;
29 | public String label_id;
30 | public String label_name;
31 | public String release;
32 | public String release_day;
33 | public String release_month;
34 | public String release_year;
35 | @SerializedName("streamable")
36 | public boolean is_streamable;
37 | @SerializedName("downloadable")
38 | public boolean is_downloadable;
39 | public String state;
40 | public String license;
41 | public String track_type;
42 | public String waveform_url;
43 | public String download_url;
44 | public String stream_url;
45 | public String video_url;
46 | public String bpm;
47 | public boolean commentable;
48 | public String isrc;
49 | public String key_signature;
50 | public String comment_count;
51 | public String download_count;
52 | public String playback_count;
53 | public String favoritings_count;
54 | public String original_format;
55 | public String original_content_size;
56 | public String asset_data;
57 | public String artwork_data;
58 | public boolean user_favorite;
59 |
60 | public TrackEntity() {
61 | }
62 |
63 | public enum License {
64 |
65 | ALL_RIGHTS_RESERVED("all-rights-reserved"),
66 | NO_RIGHTS_RESERVED("no-rights-reserved"),
67 | CC_ATTRIBUTION("cc-by"),
68 | CC_ATTRIBUTION_NONCOMMERCIAL("cc-by-nc"),
69 | CC_ATTRIBUTION_NO_DERIVATIVES("cc-by-nd"),
70 | CC_ATTRIBUTION_SHARE_ALIKE("cc-by-sa"),
71 | CC_ATTRIBUTION_NONCOMMERCIAL_NO_DERIVATES("cc-by-nc-nd"),
72 | CC_ATTRIBUTION_NONCOMMERCIAL_SHARE_ALIKE("cc-by-nc-sa");
73 |
74 | private final String license;
75 |
76 | License(String license) {
77 | this.license = license;
78 | }
79 |
80 | @Override
81 | public String toString() {
82 | return license;
83 | }
84 | }
85 |
86 | public enum Type {
87 |
88 | ORIGINAL("original"),
89 | REMIX("remix"),
90 | LIVE("live"),
91 | RECORDING("recording"),
92 | SPOKEN("spoken"),
93 | PODCAST("podcast"),
94 | DEMO("demo"),
95 | IN_PROGRESS("in progress"),
96 | STEM("stem"),
97 | LOOP("loop"),
98 | SOUND_EFFECT("sound effect"),
99 | SAMPLE("sample"),
100 | OTHER("other");
101 |
102 | private final String type;
103 |
104 | Type(String type) {
105 | this.type = type;
106 | }
107 |
108 | @Override
109 | public String toString() {
110 | return type;
111 | }
112 | }
113 |
114 |
115 | public enum State {
116 |
117 | PROCESSING("processing"),
118 | FAILED("failed"),
119 | FINISHED("finished");
120 |
121 | private final String state;
122 |
123 | State(String state) {
124 | this.state = state;
125 | }
126 |
127 | @Override
128 | public String toString() {
129 | return state;
130 | }
131 | }
132 |
133 | public enum Visibility {
134 | ALL("all"),
135 | PUBLIC("public"),
136 | PRIVATE("private");
137 |
138 | private final String filter;
139 |
140 | Visibility(String filter) {
141 | this.filter = filter;
142 | }
143 |
144 | @Override
145 | public String toString() {
146 | return filter;
147 | }
148 | }
149 |
150 |
151 | public enum EmbeddableBy {
152 | ALL("all"),
153 | ME("me"),
154 | NONE("none");
155 |
156 | private final String embeddableBy;
157 |
158 | EmbeddableBy(String embeddableBy) {
159 | this.embeddableBy = embeddableBy;
160 | }
161 |
162 | @Override
163 | public String toString() {
164 | return embeddableBy;
165 | }
166 | }
167 |
168 | @SuppressWarnings({"WeakerAccess"})
169 | public static class Filter {
170 |
171 | private Map options;
172 | private Page> page;
173 |
174 | public Filter() {
175 | options = new HashMap<>();
176 | }
177 |
178 | public Filter byName(String name) {
179 | options.put("q", name);
180 | return this;
181 | }
182 |
183 | public Filter limit(int limit) {
184 | options.put("limit", limit);
185 | return this;
186 | }
187 |
188 | public Filter nextPage(Page> page) {
189 | this.page = page;
190 | return this;
191 | }
192 |
193 |
194 | public Filter offset(int offset) {
195 | options.put("offset", offset);
196 | return this;
197 | }
198 |
199 | public Filter byGenres(String... genres) {
200 | if (genres != null) {
201 | options.put("genres", convert(genres));
202 | }
203 | return this;
204 | }
205 |
206 | public Filter byTags(String... tags) {
207 | if (tags != null) {
208 | options.put("tags", convert(tags));
209 | }
210 | return this;
211 | }
212 |
213 | public Filter byLicense(License license) {
214 | if (license != null) {
215 | options.put("license", license.license);
216 | }
217 | return this;
218 | }
219 |
220 | public Filter byTypes(Type... types) {
221 | if (types != null) {
222 | options.put("types", convert(types));
223 | }
224 | return this;
225 | }
226 |
227 | public Filter byBPM(int from, int to) {
228 | options.put("bpm[from]", from);
229 | options.put("bpm[to]", to);
230 | return this;
231 | }
232 |
233 | public Filter withPagination() {
234 | options.put("linked_partitioning", -1);
235 | return this;
236 | }
237 |
238 | public Filter byVisibility(Visibility visibility) {
239 | if (visibility != null) {
240 | options.put("filter", visibility.filter);
241 | }
242 | return this;
243 | }
244 |
245 | @SuppressWarnings("all")
246 | private String convert(T... strings) {
247 | return Arrays.toString(strings)
248 | .replaceAll("[\\[.\\].\\s+]", "");
249 | }
250 |
251 | public Filter byTime(Date from, Date to) {
252 | options.put("created_at[from]", from);
253 | options.put("created_at[to]", to);
254 | return this;
255 | }
256 |
257 | public Filter byDuration(int fromMillis, int toMillis) {
258 | options.put("duration[from]", fromMillis);
259 | options.put("duration[to]", toMillis);
260 | return this;
261 | }
262 |
263 | public Filter byIds(String... ids) {
264 | options.put("ids", convert(ids));
265 | return this;
266 | }
267 |
268 | public Map createOptions() {
269 | if (page != null) {
270 | if (!page.isLast) {
271 | withPagination();
272 | options.put("offset", page.futureOffset);
273 | options.put("q", page.query);
274 | }
275 | }
276 | page = null;
277 | return options;
278 | }
279 |
280 | public static Filter start() {
281 | return new Filter();
282 | }
283 | }
284 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/UserEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | public class UserEntity {
9 | public String id;
10 | public String permalink;
11 | public String username;
12 | public String uri;
13 | public String permalink_url;
14 | public String avatar_url;
15 | public String country;
16 | public String full_name;
17 | public String city;
18 | public String description;
19 |
20 | @SerializedName("discogs-name")
21 | public String discogs_name;
22 |
23 | @SerializedName("myspace-name")
24 | public String myspace_name;
25 | public String website;
26 |
27 | @SerializedName("website-tile")
28 | public String website_title;
29 |
30 | @SerializedName("online")
31 | public boolean is_online;
32 | public String track_count;
33 | public String playlist_count;
34 | public String followers_count;
35 | public String followings_count;
36 | public String public_favorites_count;
37 | public String avatar_data;
38 |
39 | public UserEntity() {
40 | }
41 |
42 | @SuppressWarnings({"unused", "WeakerAccess"})
43 | public static class Filter {
44 |
45 | private Map options;
46 | private Page> page;
47 |
48 | public Filter() {
49 | options = new HashMap<>();
50 | }
51 |
52 | public Filter byName(String name) {
53 | options.put("q", name);
54 | return this;
55 | }
56 |
57 | public Filter nextPage(Page> page) {
58 | this.page = page;
59 | return this;
60 | }
61 |
62 | public Filter withPagination() {
63 | options.put("linked_partitioning", -1);
64 | return this;
65 | }
66 |
67 | public Filter limit(int limit) {
68 | options.put("limit", limit);
69 | return this;
70 | }
71 |
72 | public Filter offset(int offset) {
73 | options.put("offset", offset);
74 | return this;
75 | }
76 |
77 | public Filter invalidateAll() {
78 | options.clear();
79 | return this;
80 | }
81 |
82 | public Map createOptions() {
83 | if (page != null) {
84 | if (!page.isLast) {
85 | withPagination();
86 | options.put("offset", page.futureOffset);
87 | options.put("q", page.query);
88 | }
89 | }
90 | page = null;
91 | return options;
92 | }
93 |
94 | public static Filter start() {
95 | return new Filter();
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/model/WebProfileEntity.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.model;
2 |
3 | public class WebProfileEntity {
4 | private String kind;
5 | private String id;
6 | private String service;
7 | private String title;
8 | private String url;
9 | private String username;
10 | private String created_at;
11 |
12 | public WebProfileEntity() {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/java/com/vpaliy/soundcloud/utils/Adapter.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud.utils;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.TypeAdapter;
5 | import com.google.gson.TypeAdapterFactory;
6 | import com.google.gson.reflect.TypeToken;
7 | import com.google.gson.stream.JsonReader;
8 | import com.google.gson.stream.JsonWriter;
9 |
10 | import java.io.IOException;
11 |
12 | public class Adapter implements TypeAdapterFactory {
13 |
14 | public interface PostProcessable {
15 | void postProcess();
16 | }
17 |
18 | public TypeAdapter create(Gson gson, TypeToken type) {
19 | final TypeAdapter delegate = gson.getDelegateAdapter(this, type);
20 |
21 | return new TypeAdapter() {
22 | public void write(JsonWriter out, T value) throws IOException {
23 | delegate.write(out, value);
24 | }
25 |
26 | public T read(JsonReader in) throws IOException {
27 | T obj = delegate.read(in);
28 | if (obj instanceof PostProcessable) {
29 | ((PostProcessable) obj).postProcess();
30 | }
31 | return obj;
32 | }
33 | };
34 | }
35 | }
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/res/layout/activity_web.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | soundcloud-api
3 |
4 |
--------------------------------------------------------------------------------
/soundcloud-api@/src/test/java/com/vpaliy/soundcloud/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.vpaliy.soundcloud;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------