├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── .project ├── .settings └── org.eclipse.buildship.core.prefs ├── LICENSE ├── README.md ├── app ├── .classpath ├── .gitignore ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── build.gradle ├── libs │ ├── spotify-android-auth-1.0.0.aar │ ├── spotify-player-24-noconnect-2.20b.aar │ └── spotify-web-api-android-0.4.1.aar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sodastudio │ │ └── jun │ │ └── spotify_demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── sodastudio │ │ │ └── jun │ │ │ └── spotify_demo │ │ │ ├── MainActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── SpotifyLoginActivity.java │ │ │ ├── TrackDetailActivity.java │ │ │ ├── manager │ │ │ ├── ListManager.java │ │ │ ├── PlaybackManager.java │ │ │ └── SearchPager.java │ │ │ ├── model │ │ │ ├── AlbumNew.java │ │ │ ├── ArtistSearch.java │ │ │ ├── Music.java │ │ │ ├── SimplePlaylist.java │ │ │ ├── TopArtist.java │ │ │ └── TopTrack.java │ │ │ └── ui │ │ │ ├── BrowseFragment.java │ │ │ ├── HomeFragment.java │ │ │ ├── LibraryFragment.java │ │ │ ├── MainFragment.java │ │ │ ├── RadioFragment.java │ │ │ ├── SearchFragment.java │ │ │ └── SearchResultFragment.java │ └── res │ │ ├── anim │ │ ├── alpha.xml │ │ ├── fragment_slide_left_enter.xml │ │ ├── fragment_slide_right_exit.xml │ │ ├── hold.xml │ │ ├── nav_click.xml │ │ ├── pull_up_from_bottom.xml │ │ ├── push_out_to_bottom.xml │ │ └── translate.xml │ │ ├── drawable-v24 │ │ ├── ic_home_white_24dp.xml │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── create_account_button.xml │ │ ├── create_account_normal.xml │ │ ├── create_account_pressed.xml │ │ ├── follow_rect.xml │ │ ├── ic_add_black_24dp.xml │ │ ├── ic_arrow_back_black_24dp.xml │ │ ├── ic_arrow_back_white_24dp.xml │ │ ├── ic_close_black_24dp.xml │ │ ├── ic_file_upload_black_24dp.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_library_music_black_24dp.xml │ │ ├── ic_more_horiz_black_24dp.xml │ │ ├── ic_open_in_browser_black_24dp.xml │ │ ├── ic_playlist_add_black_24dp.xml │ │ ├── ic_radio_black_24dp.xml │ │ ├── ic_search_black_24dp.xml │ │ ├── ic_surround_sound_black_24dp.xml │ │ ├── login_button.xml │ │ ├── login_normal.xml │ │ ├── login_pressed.xml │ │ ├── main_logo_white.png │ │ ├── maroon_sample_album.jpeg │ │ ├── round_rect.xml │ │ └── sodastudio_logo.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_splashscreen.xml │ │ ├── activity_spotify_login.xml │ │ ├── activity_track_detail.xml │ │ ├── album_view.xml │ │ ├── artist_search.xml │ │ ├── artist_view.xml │ │ ├── fragment_browse.xml │ │ ├── fragment_home.xml │ │ ├── fragment_library.xml │ │ ├── fragment_main.xml │ │ ├── fragment_radio.xml │ │ ├── fragment_search.xml │ │ ├── fragment_search_result.xml │ │ ├── icon_navbar.xml │ │ ├── music.xml │ │ ├── new_album_view.xml │ │ └── track_view.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ └── sodastudio │ └── jun │ └── spotify_demo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sample.json ├── screenshot ├── demo-first.gif └── screens.png └── settings.gradle /.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 | 11 | # Built application files 12 | *.apk 13 | *.ap_ 14 | 15 | # Files for the ART/Dalvik VM 16 | *.dex 17 | 18 | # Java class files 19 | *.class 20 | 21 | # Generated files 22 | bin/ 23 | gen/ 24 | out/ 25 | 26 | # Gradle files 27 | .gradle/ 28 | build/ 29 | 30 | # Local configuration file (sdk path, etc) 31 | local.properties 32 | 33 | # Proguard folder generated by Eclipse 34 | proguard/ 35 | 36 | # Log Files 37 | *.log 38 | 39 | # Android Studio Navigation editor temp files 40 | .navigation/ 41 | 42 | # Android Studio captures folder 43 | captures/ 44 | 45 | # Intellij 46 | *.iml 47 | .idea/workspace.xml 48 | .idea/tasks.xml 49 | .idea/gradle.xml 50 | .idea/dictionaries 51 | .idea/libraries 52 | 53 | # Keystore files 54 | *.jks 55 | 56 | # External native build folder generated in Android Studio 2.2 and later 57 | .externalNativeBuild 58 | 59 | # Google Services (e.g. APIs or Firebase) 60 | google-services.json 61 | 62 | # Freeline 63 | freeline.py 64 | freeline/ 65 | freeline_project_description.json -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | spotify-demo 4 | Project spotify-demo created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | #Tue Dec 26 18:09:44 CST 2017 2 | connection.project.dir= 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **This project is paused!!** 🙏 2 | 3 | Some components may not work and some packages/dependencies are deprecated. 4 | 5 | # spotify-demo 6 | Music player app using the Spotify Android SDK and Spotify Web API. 7 | 8 | [![Screenshot](screenshot/screens.png)](https://github.com/junlee91/android-spotify-demo/blob/master/screenshot/screens.png) 9 | 10 | 11 | ## Getting Started 12 | - Clone this repo: 13 | ```sh 14 | https://github.com/junlee91/android-spotify-demo.git 15 | ``` 16 | - Open the project in Android Studio 17 | - Make sure to have `buildToolsVersion "26.0.2"` 18 | 19 | ## Installing Spotify Android SDK 20 | You can follow the [Spotify Android SDK Tutorial](https://developer.spotify.com/technologies/spotify-android-sdk/tutorial/) to start the set up. 21 | 22 | ### Quick step 23 | - Download the [Spotify Android playback library zip](https://github.com/spotify/android-sdk/) and [Spotify Android auth library zip](https://github.com/spotify/android-auth/) from GitHub and unzip them. 24 | - In a file explorer (not Android Studio), drag the unzipped spotify-auth-version.aar and spotify-player-version.aar files into the /app/libs directory in your project’s root directory. 25 | - Playback library can be found when you unzip the zipfile. And simply copy this .aar into app/libs 26 | - Auth library needs some more steps. 27 | 28 | ### Build .aar for auth library 29 | - Download and unzip 30 | - Remove the auth-sample directory since it is not needed to create .aar file 31 | - In the `\android-auth-1.0\settings.gradle` file, include `':app'` 32 | - Create `\android-auth-1.0\local.properties` and add `sdk.dir=/Users/%YOUR_PC_USERNAME%/Library/Android/sdk` for path to the Android SDK 33 | - Run `./gradlew build` and grab an artifact from `auth-lib/build/outputs/aar/`. 34 | 35 | ## Generate SHA1 36 | This step is needed for registering application fingerprint 37 | ```sh 38 | echo -n password | shasum -a 1 | awk '{print $1}' 39 | ``` 40 | 41 | ## Get data from Spotify Web API 42 | - [Spotify Web API for Android](https://github.com/kaaes/spotify-web-api-android) 43 | - [Documentation](http://kaaes.github.io/spotify-web-api-android/) 44 | 45 | ## ScalingLayout 46 | - Follow the instructions from [here](https://github.com/iammert/ScalingLayout) 47 | 48 | ## Dependency 49 | If you use Android Studio as recommended, the following dependencies will automatically be installed by Gradle. 50 | ```sh 51 | android{ 52 | vectorDrawables.useSupportLibrary = true 53 | } 54 | 55 | repositories { 56 | mavenCentral() 57 | flatDir { 58 | dirs 'libs' 59 | } 60 | maven { url "https://jitpack.io" } 61 | } 62 | 63 | dependencies { 64 | ... 65 | compile 'com.github.iammert:ScalingLayout:1.1' 66 | compile 'com.android.support:recyclerview-v7:26.1.0' 67 | compile 'com.squareup.picasso:picasso:2.5.2' 68 | compile 'de.hdodenhof:circleimageview:2.2.0' 69 | 70 | compile 'com.spotify.sdk:spotify-android-auth-1.0.0@aar' 71 | compile 'com.spotify.sdk:spotify-player-24-noconnect-2.20b@aar' 72 | compile 'com.github.kaaes:spotify-web-api-android:0.4.1' 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | #Tue Dec 26 18:09:45 CST 2017 2 | connection.project.dir=.. 3 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.2" 6 | defaultConfig { 7 | applicationId "com.sodastudio.jun.spotify_demo" 8 | minSdkVersion 23 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | debug { 21 | } 22 | } 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | flatDir { 28 | dirs 'libs' 29 | } 30 | maven { url "https://jitpack.io" } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(include: ['*.jar'], dir: 'libs') 35 | implementation 'com.android.support:appcompat-v7:26.1.0' 36 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 39 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 40 | 41 | implementation 'com.github.iammert:ScalingLayout:1.1' 42 | implementation 'com.android.support:recyclerview-v7:26.1.0' 43 | implementation 'com.squareup.picasso:picasso:2.5.2' 44 | implementation 'de.hdodenhof:circleimageview:2.2.0' 45 | 46 | implementation 'com.spotify.sdk:spotify-android-auth-1.0.0@aar' 47 | implementation 'com.spotify.sdk:spotify-player-24-noconnect-2.20b@aar' 48 | implementation 'com.github.kaaes:spotify-web-api-android:0.4.1' 49 | } 50 | -------------------------------------------------------------------------------- /app/libs/spotify-android-auth-1.0.0.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/libs/spotify-android-auth-1.0.0.aar -------------------------------------------------------------------------------- /app/libs/spotify-player-24-noconnect-2.20b.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/libs/spotify-player-24-noconnect-2.20b.aar -------------------------------------------------------------------------------- /app/libs/spotify-web-api-android-0.4.1.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/libs/spotify-web-api-android-0.4.1.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sodastudio/jun/spotify_demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.sodastudio.jun.spotify_demo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo; 2 | 3 | import android.app.FragmentManager; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.widget.Toast; 8 | 9 | import com.sodastudio.jun.spotify_demo.manager.PlaybackManager; 10 | import com.sodastudio.jun.spotify_demo.manager.SearchPager; 11 | import com.sodastudio.jun.spotify_demo.ui.MainFragment; 12 | import com.spotify.sdk.android.player.Config; 13 | import com.spotify.sdk.android.player.ConnectionStateCallback; 14 | import com.spotify.sdk.android.player.Error; 15 | import com.spotify.sdk.android.player.PlaybackState; 16 | import com.spotify.sdk.android.player.Spotify; 17 | import com.spotify.sdk.android.player.SpotifyPlayer; 18 | 19 | import kaaes.spotify.webapi.android.SpotifyApi; 20 | import kaaes.spotify.webapi.android.SpotifyService; 21 | public class MainActivity extends AppCompatActivity 22 | implements ConnectionStateCallback 23 | { 24 | 25 | private static final String TAG = "Spotify MainActivity"; 26 | 27 | // _____ _ _ _ 28 | // | ___(_) ___| | __| |___ 29 | // | |_ | |/ _ \ |/ _` / __| 30 | // | _| | | __/ | (_| \__ \ 31 | // |_| |_|\___|_|\__,_|___/ 32 | // 33 | 34 | public static SpotifyPlayer mPlayer; 35 | public static PlaybackState mCurrentPlaybackState; 36 | 37 | private Toast mToast; 38 | 39 | private String AUTH_TOKEN; 40 | 41 | public static SpotifyService spotifyService; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_main); 47 | 48 | FragmentManager manager = getFragmentManager(); 49 | manager.beginTransaction().replace(R.id.fragment_container, new MainFragment()).commit(); 50 | 51 | AUTH_TOKEN = getIntent().getStringExtra(SpotifyLoginActivity.AUTH_TOKEN); 52 | 53 | onAuthenticationComplete(AUTH_TOKEN); 54 | 55 | } 56 | 57 | private void onAuthenticationComplete(final String auth_token) { 58 | 59 | Log.d(TAG,"Got authentication token"); 60 | 61 | if(mPlayer == null) 62 | { 63 | Config playerConfig = new Config(this, auth_token, SpotifyLoginActivity.CLIENT_ID); 64 | 65 | Spotify.getPlayer(playerConfig, this, new SpotifyPlayer.InitializationObserver() { 66 | @Override 67 | public void onInitialized(SpotifyPlayer spotifyPlayer) { 68 | Log.d(TAG,"-- Player initialized --"); 69 | mPlayer = spotifyPlayer; 70 | mPlayer.addConnectionStateCallback(MainActivity.this); 71 | //mPlayer.addNotificationCallback(MainActivity.this); 72 | 73 | Log.d(TAG, "AccessToken: " + auth_token); 74 | 75 | // Set API 76 | setServiceAPI(); 77 | } 78 | 79 | @Override 80 | public void onError(Throwable throwable) { 81 | Log.e(TAG, "Could not initialize player: " + throwable.getMessage()); 82 | } 83 | }); 84 | } else { 85 | mPlayer.login(auth_token); 86 | } 87 | 88 | } 89 | 90 | private void setServiceAPI(){ 91 | Log.d(TAG, "Setting Spotify API Service"); 92 | SpotifyApi api = new SpotifyApi(); 93 | api.setAccessToken(AUTH_TOKEN); 94 | 95 | spotifyService = api.getService(); 96 | } 97 | 98 | // _ _ ___ _____ _ 99 | // | | | |_ _| | ____|_ _____ _ __ | |_ ___ 100 | // | | | || | | _| \ \ / / _ \ '_ \| __/ __| 101 | // | |_| || | | |___ \ V / __/ | | | |_\__ \ 102 | // \___/|___| |_____| \_/ \___|_| |_|\__|___/ 103 | // 104 | 105 | 106 | private boolean isLoggedIn() { 107 | return mPlayer != null && mPlayer.isLoggedIn(); 108 | } 109 | 110 | 111 | private void showToast(String text) { 112 | if (mToast != null) { 113 | mToast.cancel(); 114 | } 115 | mToast = Toast.makeText(this, text, Toast.LENGTH_SHORT); 116 | mToast.show(); 117 | } 118 | 119 | // ____ _ _ _ _ __ __ _ _ _ 120 | // / ___|__ _| | | |__ __ _ ___| | __ | \/ | ___| |_| |__ ___ __| |___ 121 | // | | / _` | | | '_ \ / _` |/ __| |/ / | |\/| |/ _ \ __| '_ \ / _ \ / _` / __| 122 | // | |__| (_| | | | |_) | (_| | (__| < | | | | __/ |_| | | | (_) | (_| \__ \ 123 | // \____\__,_|_|_|_.__/ \__,_|\___|_|\_\ |_| |_|\___|\__|_| |_|\___/ \__,_|___/ 124 | // 125 | 126 | @Override 127 | public void onLoggedIn() { 128 | Log.d(TAG, "User logged in"); 129 | //showToast("Login Success!"); 130 | 131 | SearchPager.getInstance(this).getNewRelease(null); 132 | SearchPager.getInstance(this).getMyTopTracks(null); 133 | SearchPager.getInstance(this).getFeatured(); 134 | } 135 | 136 | @Override 137 | public void onLoggedOut() { 138 | Log.d(TAG, "User logged out"); 139 | } 140 | 141 | @Override 142 | public void onLoginFailed(Error error) { 143 | Log.d(TAG, "Login failed"); 144 | showToast("Login failed. You need Spotify Premium to use the app."); 145 | } 146 | 147 | @Override 148 | public void onTemporaryError() { 149 | Log.d(TAG, "Temporary error occurred"); 150 | } 151 | 152 | @Override 153 | public void onConnectionMessage(String message) { 154 | Log.d(TAG, "Received connection message: " + message); 155 | } 156 | 157 | // ____ _ _ _ 158 | // | _ \ ___ ___| |_ _ __ _ _ ___| |_(_) ___ _ __ 159 | // | | | |/ _ \/ __| __| '__| | | |/ __| __| |/ _ \| '_ \ 160 | // | |_| | __/\__ \ |_| | | |_| | (__| |_| | (_) | | | | 161 | // |____/ \___||___/\__|_| \__,_|\___|\__|_|\___/|_| |_| 162 | // 163 | 164 | @Override 165 | protected void onPause() { 166 | super.onPause(); 167 | 168 | if (mPlayer != null) { 169 | //mPlayer.removeNotificationCallback(MainActivity.this); 170 | mPlayer.removeConnectionStateCallback(MainActivity.this); 171 | } 172 | } 173 | 174 | @Override 175 | protected void onDestroy() { 176 | Log.d(TAG, "onDestroy"); 177 | Spotify.destroyPlayer(this); 178 | super.onDestroy(); 179 | } 180 | 181 | 182 | @Override 183 | public void onBackPressed() { 184 | super.onBackPressed(); 185 | 186 | PlaybackManager playbackManager = PlaybackManager.getInstance(); 187 | playbackManager.setSearchResultFragmentAdded(false); 188 | } 189 | } 190 | 191 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo; 2 | 3 | import android.content.Intent; 4 | import android.graphics.PixelFormat; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.Window; 8 | import android.view.animation.Animation; 9 | import android.view.animation.AnimationUtils; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | 13 | /** 14 | * Created by jun on 12/27/17. 15 | */ 16 | 17 | public class SplashActivity extends AppCompatActivity { 18 | 19 | public void onAttachedToWindow(){ 20 | super.onAttachedToWindow(); 21 | Window window = getWindow(); 22 | window.setFormat(PixelFormat.RGBA_8888); 23 | } 24 | 25 | 26 | @Override 27 | public void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_splashscreen); 30 | StartAnimations(); 31 | } 32 | 33 | private void StartAnimations(){ 34 | Animation anim = AnimationUtils.loadAnimation(this, R.anim.alpha); 35 | anim.reset(); 36 | LinearLayout l = (LinearLayout)findViewById(R.id.lin_lay); 37 | l.clearAnimation(); 38 | l.startAnimation(anim); 39 | 40 | anim = AnimationUtils.loadAnimation(this, R.anim.translate); 41 | anim.reset(); 42 | ImageView iv = (ImageView)findViewById(R.id.splash); 43 | iv.clearAnimation(); 44 | iv.startAnimation(anim); 45 | 46 | Thread splashTread = new Thread() { 47 | @Override 48 | public void run() { 49 | try { 50 | int waited = 0; 51 | // Splash screen pause time 52 | while (waited < 2500) { 53 | sleep(100); 54 | waited += 100; 55 | } 56 | 57 | Intent intent = new Intent(SplashActivity.this, 58 | SpotifyLoginActivity.class); 59 | intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 60 | 61 | startActivity(intent); 62 | overridePendingTransition(R.anim.pull_up_from_bottom, R.anim.hold); 63 | 64 | SplashActivity.this.finish(); 65 | } catch (InterruptedException e) { 66 | // do nothing 67 | } finally { 68 | SplashActivity.this.finish(); 69 | } 70 | 71 | } 72 | }; 73 | splashTread.start(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/SpotifyLoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.Button; 9 | 10 | import com.spotify.sdk.android.authentication.AuthenticationClient; 11 | import com.spotify.sdk.android.authentication.AuthenticationRequest; 12 | import com.spotify.sdk.android.authentication.AuthenticationResponse; 13 | 14 | public class SpotifyLoginActivity extends AppCompatActivity { 15 | 16 | // ____ _ _ 17 | // / ___|___ _ __ ___| |_ __ _ _ __ | |_ ___ 18 | // | | / _ \| '_ \/ __| __/ _` | '_ \| __/ __| 19 | // | |__| (_) | | | \__ \ || (_| | | | | |_\__ \ 20 | // \____\___/|_| |_|___/\__\__,_|_| |_|\__|___/ 21 | // 22 | 23 | @SuppressWarnings("SpellCheckingInspection") 24 | public static final String CLIENT_ID = "5de6930c8a744270851a5064c7ff6333"; 25 | @SuppressWarnings("SpellCheckingInspection") 26 | private static final String REDIRECT_URI = "http://localhost:8888/callback"; 27 | 28 | private static final String TAG = "Spotify " + SpotifyLoginActivity.class.getSimpleName(); 29 | 30 | /** 31 | * Request code that will be passed together with authentication result to the onAuthenticationResult 32 | */ 33 | private static final int REQUEST_CODE = 1337; 34 | 35 | public static final String AUTH_TOKEN = "AUTH_TOKEN"; 36 | 37 | // ___ _ _ _ _ _ _ _ 38 | // |_ _|_ __ (_) |_(_) __ _| (_)______ _| |_(_) ___ _ __ 39 | // | || '_ \| | __| |/ _` | | |_ / _` | __| |/ _ \| '_ \ 40 | // | || | | | | |_| | (_| | | |/ / (_| | |_| | (_) | | | | 41 | // |___|_| |_|_|\__|_|\__,_|_|_/___\__,_|\__|_|\___/|_| |_| 42 | // 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.activity_spotify_login); 48 | 49 | Button mLoginButton = (Button)findViewById(R.id.login_button); 50 | mLoginButton.setOnClickListener(mListener); 51 | 52 | } 53 | 54 | // _ _ _ _ _ _ _ 55 | // / \ _ _| |_| |__ ___ _ __ | |_(_) ___ __ _| |_(_) ___ _ __ 56 | // / _ \| | | | __| '_ \ / _ \ '_ \| __| |/ __/ _` | __| |/ _ \| '_ \ 57 | // / ___ \ |_| | |_| | | | __/ | | | |_| | (_| (_| | |_| | (_) | | | | 58 | // /_/ \_\__,_|\__|_| |_|\___|_| |_|\__|_|\___\__,_|\__|_|\___/|_| |_| 59 | // 60 | 61 | 62 | private void openLoginWindow() { 63 | 64 | AuthenticationRequest.Builder builder = new AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN,REDIRECT_URI); 65 | 66 | builder.setScopes(new String[]{"user-read-private", "streaming", "user-top-read", "user-read-recently-played"}); 67 | AuthenticationRequest request = builder.build(); 68 | 69 | AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request); 70 | } 71 | 72 | 73 | @Override 74 | protected void onActivityResult(int requestCode, final int resultCode, Intent data) { 75 | super.onActivityResult(requestCode, resultCode, data); 76 | 77 | if(requestCode == REQUEST_CODE) 78 | { 79 | final AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, data); 80 | 81 | switch (response.getType()) { 82 | // Response was successful and contains auth token 83 | case TOKEN: 84 | 85 | Intent intent = new Intent(SpotifyLoginActivity.this, 86 | MainActivity.class); 87 | 88 | intent.putExtra(AUTH_TOKEN, response.getAccessToken()); 89 | 90 | startActivity(intent); 91 | 92 | destroy(); 93 | 94 | break; 95 | 96 | // Auth flow returned an error 97 | case ERROR: 98 | Log.e(TAG,"Auth error: " + response.getError()); 99 | break; 100 | 101 | // Most likely auth flow was cancelled 102 | default: 103 | Log.d(TAG,"Auth result: " + response.getType()); 104 | } 105 | } 106 | } 107 | 108 | View.OnClickListener mListener = new View.OnClickListener(){ 109 | 110 | @Override 111 | public void onClick(View view) { 112 | switch (view.getId()){ 113 | case R.id.login_button: 114 | openLoginWindow(); 115 | break; 116 | } 117 | } 118 | }; 119 | 120 | public void destroy(){ 121 | SpotifyLoginActivity.this.finish(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/TrackDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo; 2 | 3 | import android.graphics.Bitmap; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import com.sodastudio.jun.spotify_demo.model.Music; 12 | import com.sodastudio.jun.spotify_demo.ui.SearchResultFragment; 13 | import com.squareup.picasso.Picasso; 14 | import com.squareup.picasso.Transformation; 15 | 16 | public class TrackDetailActivity extends AppCompatActivity { 17 | 18 | private static final String TAG = "Spotify TrackDetail"; 19 | 20 | private Music detailMusic; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_track_detail); 26 | 27 | detailMusic = getIntent().getParcelableExtra(SearchResultFragment.DETAIL_MUSIC); 28 | 29 | final ImageView albumImage = findViewById(R.id.detail_album_image_field); 30 | final TextView titleView = findViewById(R.id.detail_track_title_field); 31 | final TextView artistView = findViewById(R.id.detail_artist_field); 32 | final TextView albumView = findViewById(R.id.detail_album_field); 33 | 34 | final TextView cancelView =findViewById(R.id.detail_cancel_button); 35 | cancelView.setOnClickListener(new View.OnClickListener(){ 36 | 37 | @Override 38 | public void onClick(View view) { 39 | TrackDetailActivity.this.finish(); 40 | } 41 | }); 42 | 43 | Picasso.with(this) 44 | .load(detailMusic.getAlbum_image()) 45 | .transform(new Transformation() { 46 | @Override 47 | public Bitmap transform(Bitmap source) { 48 | // really ugly darkening trick 49 | final Bitmap copy = source.copy(source.getConfig(), true); 50 | source.recycle(); 51 | return copy; 52 | } 53 | 54 | @Override 55 | public String key() { 56 | return "darken"; 57 | } 58 | }) 59 | .into(albumImage); 60 | 61 | String title = detailMusic.getTitle(); 62 | String album = detailMusic.getAlbum(); 63 | String artist = detailMusic.getArtist(); 64 | 65 | if(title.length() > 40){ 66 | title = title.substring(0, 40); 67 | title += "..."; 68 | } 69 | 70 | if(album.length() > 40){ 71 | album = album.substring(0,40); 72 | album += "..."; 73 | } 74 | 75 | if(artist.length() > 40){ 76 | artist = artist.substring(0,40); 77 | artist += "..."; 78 | } 79 | 80 | titleView.setText(title); 81 | artistView.setText(artist); 82 | albumView.setText(album); 83 | 84 | Log.d(TAG, detailMusic.getTitle()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/manager/ListManager.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.manager; 2 | 3 | import com.sodastudio.jun.spotify_demo.model.AlbumNew; 4 | import com.sodastudio.jun.spotify_demo.model.ArtistSearch; 5 | import com.sodastudio.jun.spotify_demo.model.Music; 6 | import com.sodastudio.jun.spotify_demo.model.SimplePlaylist; 7 | import com.sodastudio.jun.spotify_demo.model.TopArtist; 8 | import com.sodastudio.jun.spotify_demo.model.TopTrack; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * Created by jun on 12/30/17. 14 | */ 15 | 16 | public class ListManager { 17 | 18 | private static ListManager listManager; 19 | 20 | private ArrayList trackLists; 21 | private ArrayList artistSearches; 22 | private ArrayList albumNewArrayList; 23 | private ArrayList topArtists; 24 | private ArrayList topTracks; 25 | private ArrayList simplePlaylists; 26 | 27 | private String current_playing_title; 28 | private String current_playing_album; 29 | 30 | public static ListManager getInstance(){ 31 | if(listManager == null){ 32 | listManager = new ListManager(); 33 | } 34 | 35 | return listManager; 36 | } 37 | 38 | private ListManager(){ 39 | trackLists = new ArrayList<>(); 40 | artistSearches = new ArrayList<>(); 41 | albumNewArrayList = new ArrayList<>(); 42 | topArtists = new ArrayList<>(); 43 | topTracks = new ArrayList<>(); 44 | simplePlaylists = new ArrayList<>(); 45 | 46 | current_playing_title = null; 47 | current_playing_album = null; 48 | } 49 | 50 | public ArrayList getSimplePlaylists(){ 51 | return simplePlaylists; 52 | } 53 | 54 | public void addSimpleList(SimplePlaylist simple){ 55 | simplePlaylists.add(simple); 56 | } 57 | 58 | public ArrayList getTopTracks(){ 59 | return topTracks; 60 | } 61 | 62 | public void addTopTrack(TopTrack track){ 63 | 64 | topTracks.add(track); 65 | } 66 | 67 | public void addTopArtist(TopArtist artist){ 68 | topArtists.add(artist); 69 | } 70 | 71 | public ArrayList getTopArtists(){ 72 | return topArtists; 73 | } 74 | 75 | public void addNewAlbum(AlbumNew albumNew){ 76 | albumNewArrayList.add(albumNew); 77 | } 78 | 79 | public ArrayList getAlbumNewArrayList(){ 80 | return albumNewArrayList; 81 | } 82 | 83 | public ArrayList getArtists() { 84 | return artistSearches; 85 | } 86 | 87 | public void addArtist(ArtistSearch search){ 88 | 89 | ArtistSearch found = null; 90 | 91 | for(ArtistSearch artistSearch : artistSearches) 92 | { 93 | if(artistSearch.getName().equals(search.getName())) 94 | { 95 | found = artistSearch; 96 | } 97 | } 98 | 99 | if(found != null) 100 | artistSearches.remove(found); 101 | 102 | artistSearches.add(0, search); 103 | } 104 | 105 | public ArrayList getTrackLists() { 106 | return trackLists; 107 | } 108 | 109 | public void addTrack(Music music){ 110 | trackLists.add(music); 111 | } 112 | 113 | public void clearList(){ 114 | trackLists.clear(); 115 | } 116 | 117 | public Music findCurrentMusic(String title, String album){ 118 | for(Music m : trackLists){ 119 | if(m.getTitle().equals(title) && m.getAlbum().equals(album)){ 120 | return m; 121 | } 122 | } 123 | 124 | return null; 125 | } 126 | 127 | public String getCurrent_playing_title() { 128 | return current_playing_title; 129 | } 130 | 131 | public void setCurrent_playing_title(String current_playing_title) { 132 | this.current_playing_title = current_playing_title; 133 | } 134 | 135 | public String getCurrent_playing_album() { 136 | return current_playing_album; 137 | } 138 | 139 | public void setCurrent_playing_album(String current_playing_album) { 140 | this.current_playing_album = current_playing_album; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/manager/PlaybackManager.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.manager; 2 | 3 | import android.os.Parcelable; 4 | 5 | import com.sodastudio.jun.spotify_demo.model.Music; 6 | 7 | /** 8 | * Created by jun on 12/31/17. 9 | */ 10 | 11 | public class PlaybackManager { 12 | 13 | private static PlaybackManager manager; 14 | 15 | private Music music; 16 | private Parcelable state; 17 | private boolean SearchResultFragmentAdded = false; 18 | 19 | public static PlaybackManager getInstance(){ 20 | if(manager == null){ 21 | manager = new PlaybackManager(); 22 | } 23 | 24 | return manager; 25 | } 26 | 27 | public Music getMusic() { 28 | return music; 29 | } 30 | 31 | public void setMusic(Music music) { 32 | this.music = music; 33 | } 34 | 35 | public Parcelable getState() { 36 | return state; 37 | } 38 | 39 | public void setState(Parcelable state) { 40 | this.state = state; 41 | } 42 | 43 | public boolean isSearchResultFragmentAdded() { 44 | return SearchResultFragmentAdded; 45 | } 46 | 47 | public void setSearchResultFragmentAdded(boolean searchResultFragmentAdded) { 48 | SearchResultFragmentAdded = searchResultFragmentAdded; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/manager/SearchPager.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.manager; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.sodastudio.jun.spotify_demo.MainActivity; 7 | import com.sodastudio.jun.spotify_demo.model.AlbumNew; 8 | import com.sodastudio.jun.spotify_demo.model.SimplePlaylist; 9 | import com.sodastudio.jun.spotify_demo.model.TopArtist; 10 | import com.sodastudio.jun.spotify_demo.model.TopTrack; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import kaaes.spotify.webapi.android.SpotifyCallback; 18 | import kaaes.spotify.webapi.android.SpotifyError; 19 | import kaaes.spotify.webapi.android.SpotifyService; 20 | import kaaes.spotify.webapi.android.models.Album; 21 | import kaaes.spotify.webapi.android.models.AlbumSimple; 22 | import kaaes.spotify.webapi.android.models.Artist; 23 | import kaaes.spotify.webapi.android.models.ArtistSimple; 24 | import kaaes.spotify.webapi.android.models.FeaturedPlaylists; 25 | import kaaes.spotify.webapi.android.models.NewReleases; 26 | import kaaes.spotify.webapi.android.models.Pager; 27 | import kaaes.spotify.webapi.android.models.PlaylistSimple; 28 | import kaaes.spotify.webapi.android.models.Track; 29 | import kaaes.spotify.webapi.android.models.TracksPager; 30 | import retrofit.client.Response; 31 | 32 | /** 33 | * Created by jun on 12/29/17. 34 | */ 35 | 36 | public class SearchPager { 37 | 38 | private static SearchPager searchPager; 39 | 40 | private SpotifyService spotifyService = MainActivity.spotifyService; 41 | 42 | public interface CompleteListener { 43 | void onComplete(List items); 44 | void onError(Throwable error); 45 | } 46 | public interface ArtistListener{ 47 | void onComplete(String url); 48 | void onError(Throwable error); 49 | } 50 | public interface onCompleteListener { 51 | void onComplete(); 52 | void onError(Throwable error); 53 | } 54 | public interface onCompleteTopArtistListener { 55 | void onComplete(); 56 | void onError(Throwable error); 57 | } 58 | public interface onCompleteTopTrackListener { 59 | void onComplete(); 60 | void onError(Throwable error); 61 | } 62 | 63 | public static SearchPager getInstance(Context context){ 64 | if(searchPager == null){ 65 | searchPager = new SearchPager(); 66 | } 67 | return searchPager; 68 | } 69 | 70 | public void getTracksFromSearch(String query, CompleteListener listener){ 71 | getData(query, listener); 72 | } 73 | 74 | private void getData(String query, final CompleteListener listener){ 75 | 76 | spotifyService.searchTracks(query, new SpotifyCallback() { 77 | @Override 78 | public void failure(SpotifyError spotifyError) { 79 | listener.onError(spotifyError); 80 | } 81 | 82 | @Override 83 | public void success(TracksPager tracksPager, Response response) { 84 | listener.onComplete(tracksPager.tracks.items); 85 | } 86 | }); 87 | } 88 | 89 | public void getArtist(String id, final ArtistListener listener){ 90 | 91 | spotifyService.getArtist(id, new SpotifyCallback() { 92 | @Override 93 | public void failure(SpotifyError spotifyError) { 94 | Log.d("SearchPager", spotifyError.toString()); 95 | listener.onError(spotifyError); 96 | } 97 | 98 | @Override 99 | public void success(Artist artist, Response response) { 100 | //Log.d("SearchPager CHECK", artist.images.get(0).url); // img url 101 | listener.onComplete(artist.images.get(1).url); 102 | } 103 | }); 104 | } 105 | 106 | public void getMyTopArtist(final onCompleteTopArtistListener listener){ 107 | 108 | Map options = new HashMap<>(); 109 | options.put(SpotifyService.LIMIT, 10); 110 | 111 | final ListManager listManager = ListManager.getInstance(); 112 | 113 | spotifyService.getTopArtists(options, new SpotifyCallback>() { 114 | @Override 115 | public void failure(SpotifyError spotifyError) { 116 | Log.d("SearchPager", spotifyError.toString()); 117 | 118 | if(listener != null) 119 | listener.onError(spotifyError); 120 | } 121 | 122 | @Override 123 | public void success(Pager artistPager, Response response) { 124 | List mList = artistPager.items; 125 | 126 | for(Artist art : mList){ 127 | Log.d("SearchPager", art.name); 128 | Log.d("SearchPager", art.images.get(1).url); 129 | 130 | listManager.addTopArtist(new TopArtist(art.name, art.images.get(1).url)); 131 | } 132 | 133 | if(listener != null) 134 | listener.onComplete(); 135 | else{ 136 | Log.d("SearchPager", "What is happening?"); 137 | } 138 | } 139 | }); 140 | } 141 | 142 | public void getMyTopTracks(final onCompleteTopTrackListener listener){ 143 | Map options = new HashMap<>(); 144 | options.put(SpotifyService.LIMIT, 10); 145 | 146 | final ListManager listManager = ListManager.getInstance(); 147 | 148 | spotifyService.getTopTracks(options, new SpotifyCallback>() { 149 | @Override 150 | public void failure(SpotifyError spotifyError) { 151 | Log.d("SearchPager", spotifyError.toString()); 152 | 153 | if(listener != null) 154 | listener.onError(spotifyError); 155 | } 156 | 157 | @Override 158 | public void success(Pager trackPager, Response response) { 159 | List tracks = trackPager.items; 160 | 161 | for(Track track : tracks){ 162 | Log.d("SearchPager", track.album.name); 163 | Log.d("SearchPager", track.album.images.get(1).url); 164 | 165 | listManager.addTopTrack(new TopTrack(track.album.name, track.album.images.get(1).url)); 166 | 167 | } 168 | 169 | if(listener != null) 170 | listener.onComplete(); 171 | } 172 | }); 173 | } 174 | 175 | public void getNewRelease(final onCompleteListener listener){ 176 | Map options = new HashMap<>(); 177 | options.put(SpotifyService.OFFSET, 0); // 0 ~ 5 6 ~ 10 178 | options.put(SpotifyService.LIMIT, 10); 179 | 180 | final ListManager listManager = ListManager.getInstance(); 181 | 182 | spotifyService.getNewReleases(options, new SpotifyCallback() { 183 | @Override 184 | public void failure(SpotifyError spotifyError) { 185 | Log.d("SearchPager", spotifyError.toString()); 186 | 187 | if(listener != null) 188 | listener.onError(spotifyError); 189 | } 190 | 191 | @Override 192 | public void success(NewReleases newReleases, Response response) { 193 | List albums = newReleases.albums.items; 194 | 195 | for(final AlbumSimple albumSimple : albums){ 196 | //Log.d("SearchPage", albumSimple.name); 197 | //Log.d("SearchPage", albumSimple.id); 198 | //Log.d("SearchPage", albumSimple.uri); 199 | //Log.d("SearchPage", albumSimple.href); 200 | //Log.d("SearchPage", albumSimple.images.get(1).url); 201 | 202 | spotifyService.getAlbum(albumSimple.id, new SpotifyCallback() { 203 | @Override 204 | public void failure(SpotifyError spotifyError) { 205 | Log.d("SearchPage Followup", spotifyError.toString()); 206 | 207 | if(listener != null) 208 | listener.onError(spotifyError); 209 | } 210 | 211 | @Override 212 | public void success(Album album, Response response) { 213 | 214 | Log.d("SearchPage Followup", albumSimple.name); 215 | Log.d("SearchPage Followup", albumSimple.id); 216 | Log.d("SearchPage Followup", albumSimple.images.get(1).url); 217 | 218 | List list = album.artists; 219 | List artists = new ArrayList<>(); 220 | 221 | for(ArtistSimple simple : list){ 222 | Log.d("SearchPage Followup", simple.name); 223 | artists.add(simple.name); 224 | } 225 | 226 | AlbumNew albumNew = new AlbumNew(albumSimple.name, albumSimple.images.get(1).url, artists); 227 | 228 | listManager.addNewAlbum(albumNew); 229 | 230 | } 231 | 232 | }); 233 | } 234 | 235 | if(listener != null) 236 | listener.onComplete(); 237 | } 238 | }); 239 | } 240 | 241 | public void getMyPlayList(){ 242 | Map options = new HashMap<>(); 243 | options.put(SpotifyService.LIMIT, 30); 244 | 245 | spotifyService.getMyPlaylists(options, new SpotifyCallback>() { 246 | @Override 247 | public void failure(SpotifyError spotifyError) { 248 | Log.d("SearchPager", spotifyError.toString()); 249 | } 250 | 251 | @Override 252 | public void success(Pager playlistSimplePager, Response response) { 253 | List simples = playlistSimplePager.items; 254 | 255 | for(PlaylistSimple simple : simples){ 256 | Log.d("SearchPager", simple.name); 257 | Log.d("SearchPager", simple.images.get(1).url); 258 | } 259 | 260 | } 261 | }); 262 | } 263 | 264 | public void getFeatured(){ 265 | 266 | spotifyService.getFeaturedPlaylists(new SpotifyCallback() { 267 | @Override 268 | public void failure(SpotifyError spotifyError) { 269 | Log.d("SearchPager", spotifyError.toString()); 270 | } 271 | 272 | @Override 273 | public void success(FeaturedPlaylists featuredPlaylists, Response response) { 274 | List mlist = featuredPlaylists.playlists.items; 275 | 276 | for(PlaylistSimple simple : mlist) 277 | { 278 | Log.d("SearchPager Simple", simple.name); 279 | Log.d("SearchPager Simple", simple.images.get(0).url); 280 | 281 | ListManager.getInstance().addSimpleList(new SimplePlaylist(simple.name, simple.images.get(0).url)); 282 | } 283 | } 284 | }); 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/AlbumNew.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by jun on 1/2/18. 7 | */ 8 | 9 | public class AlbumNew { 10 | 11 | private String title; 12 | private String img_url; 13 | private List artists; 14 | 15 | public AlbumNew(String title, String img_url, List artists) { 16 | this.title = title; 17 | this.img_url = img_url; 18 | this.artists = artists; 19 | } 20 | 21 | public String getTitle() { 22 | return title; 23 | } 24 | 25 | public String getImg_url() { 26 | return img_url; 27 | } 28 | 29 | public List getArtists() { 30 | return artists; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/ArtistSearch.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | /** 6 | * Created by jun on 1/2/18. 7 | */ 8 | 9 | public class ArtistSearch { 10 | 11 | private String name; 12 | private Bitmap image; 13 | 14 | public ArtistSearch(String name, Bitmap img) { 15 | this.name = name; 16 | this.image = img; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public Bitmap getImage(){ 24 | return image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/Music.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | import android.net.Uri; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import java.net.URL; 8 | 9 | /** 10 | * Created by jun on 12/29/17. 11 | */ 12 | 13 | public class Music implements Parcelable 14 | { 15 | private String id; 16 | private String uri; 17 | private String title; 18 | private String album; 19 | private String album_image; 20 | private long duration; 21 | private String artist; 22 | private String artist_id; 23 | private boolean playing; 24 | 25 | public Music(String i, String u, String t, String a, String a_img, long dura, String art, String art_id){ 26 | id = i; 27 | uri = u; 28 | title = t; 29 | album = a; 30 | album_image = a_img; 31 | duration = dura; 32 | artist = art; 33 | artist_id = art_id; 34 | playing = false; 35 | } 36 | 37 | private Music(Parcel in) { 38 | uri = in.readString(); 39 | title = in.readString(); 40 | album = in.readString(); 41 | album_image = in.readString(); 42 | duration = in.readLong(); 43 | artist = in.readString(); 44 | } 45 | 46 | public static final Creator CREATOR = new Creator() { 47 | @Override 48 | public Music createFromParcel(Parcel in) { 49 | return new Music(in); 50 | } 51 | 52 | @Override 53 | public Music[] newArray(int size) { 54 | return new Music[size]; 55 | } 56 | }; 57 | 58 | public String getArtist_id() { 59 | return artist_id; 60 | } 61 | 62 | public String getId() { return id; } 63 | 64 | public String getUri() { 65 | return uri; 66 | } 67 | 68 | public String getTitle() { 69 | return title; 70 | } 71 | 72 | public String getAlbum() { 73 | return album; 74 | } 75 | 76 | public String getAlbum_image() { 77 | return album_image; 78 | } 79 | 80 | public long getDuration() { 81 | return duration; 82 | } 83 | 84 | public String getArtist() { 85 | return artist; 86 | } 87 | 88 | public boolean isPlaying() { 89 | return playing; 90 | } 91 | 92 | public void setPlaying(boolean playing) { 93 | this.playing = playing; 94 | } 95 | 96 | @Override 97 | public int describeContents() { 98 | return 0; 99 | } 100 | 101 | @Override 102 | public void writeToParcel(Parcel parcel, int i) { 103 | parcel.writeString(uri); 104 | parcel.writeString(title); 105 | parcel.writeString(album); 106 | parcel.writeString(album_image); 107 | parcel.writeLong(duration); 108 | parcel.writeString(artist); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/SimplePlaylist.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | /** 4 | * Created by jun on 1/4/18. 5 | */ 6 | 7 | public class SimplePlaylist { 8 | private String name; 9 | private String img_url; 10 | 11 | public SimplePlaylist(String name, String img_url) { 12 | this.name = name; 13 | this.img_url = img_url; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public String getImg_url() { 21 | return img_url; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/TopArtist.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | /** 4 | * Created by jun on 1/3/18. 5 | */ 6 | 7 | public class TopArtist { 8 | private String name; 9 | private String img_url; 10 | 11 | public TopArtist(String name, String img_url) { 12 | this.name = name; 13 | this.img_url = img_url; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public String getImg_url() { 21 | return img_url; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/model/TopTrack.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.model; 2 | 3 | /** 4 | * Created by jun on 1/3/18. 5 | */ 6 | 7 | public class TopTrack { 8 | private String name; 9 | private String img_url; 10 | 11 | public TopTrack(String name, String img_url) { 12 | this.name = name; 13 | this.img_url = img_url; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public String getImg_url() { 21 | return img_url; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/BrowseFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.app.Fragment; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.widget.GridLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.Log; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | 16 | import com.sodastudio.jun.spotify_demo.R; 17 | import com.sodastudio.jun.spotify_demo.manager.ListManager; 18 | import com.sodastudio.jun.spotify_demo.manager.SearchPager; 19 | import com.sodastudio.jun.spotify_demo.model.AlbumNew; 20 | import com.squareup.picasso.Picasso; 21 | import com.squareup.picasso.Transformation; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * Created by jun on 12/28/17. 27 | */ 28 | 29 | public class BrowseFragment extends Fragment { 30 | 31 | public static final String TAG = "Sptify Browsefragment"; 32 | 33 | private RecyclerView newAlbumsRecyclerView; 34 | private newAlbumAdapter mAdapter; 35 | 36 | private ListManager listManager; 37 | private SearchPager.onCompleteListener listener; 38 | 39 | @Override 40 | public void onCreate(@Nullable Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | 43 | listManager = ListManager.getInstance(); 44 | } 45 | 46 | @Nullable 47 | @Override 48 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 49 | View view = inflater.inflate(R.layout.fragment_browse, container, false); 50 | 51 | newAlbumsRecyclerView = view.findViewById(R.id.new_albums_RecyclerView); 52 | newAlbumsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); 53 | 54 | updateView(); 55 | 56 | return view; 57 | } 58 | 59 | private void updateView(){ 60 | 61 | List albumNewList = listManager.getAlbumNewArrayList(); 62 | 63 | if(albumNewList.size() == 0){ 64 | 65 | listener = new SearchPager.onCompleteListener() { 66 | @Override 67 | public void onComplete() { 68 | Log.d(TAG, "onComplete!"); 69 | mAdapter.notifyDataSetChanged(); 70 | updateView(); 71 | } 72 | 73 | @Override 74 | public void onError(Throwable error) { 75 | 76 | } 77 | }; 78 | 79 | SearchPager.getInstance(getContext()).getNewRelease(listener); 80 | } 81 | 82 | if(mAdapter == null) 83 | mAdapter = new newAlbumAdapter(albumNewList); 84 | 85 | newAlbumsRecyclerView.setAdapter(mAdapter); 86 | 87 | } 88 | 89 | private class newAlbumHolder extends RecyclerView.ViewHolder{ 90 | 91 | private ImageView album_image; 92 | private TextView album_title; 93 | private TextView album_artists; 94 | 95 | private newAlbumHolder(View itemView) { 96 | super(itemView); 97 | 98 | album_image = itemView.findViewById(R.id.new_album_image); 99 | album_title = itemView.findViewById(R.id.new_album_title); 100 | album_artists = itemView.findViewById(R.id.new_artist_name); 101 | } 102 | 103 | private void bindAlbum(AlbumNew albumNew){ 104 | final String title = albumNew.getTitle(); 105 | String img_url = albumNew.getImg_url(); 106 | 107 | StringBuilder sb = new StringBuilder(); 108 | 109 | List artists = albumNew.getArtists(); 110 | for(String s : artists){ 111 | sb.append(s + ", "); 112 | } 113 | 114 | String artist = sb.toString(); 115 | 116 | int index = artist.lastIndexOf(","); 117 | artist = artist.substring(0, index); 118 | 119 | if(artist.length() > 30){ 120 | artist = artist.substring(0, 30); 121 | artist += "..."; 122 | } 123 | 124 | album_title.setText(title); 125 | album_artists.setText(artist); 126 | 127 | Picasso.with(getContext()) 128 | .load(img_url) 129 | .transform(new Transformation() { 130 | @Override 131 | public Bitmap transform(Bitmap source) { 132 | final Bitmap copy = source.copy(source.getConfig(), true); 133 | source.recycle(); 134 | 135 | return copy; 136 | } 137 | 138 | @Override 139 | public String key() { 140 | return title; 141 | } 142 | }) 143 | .into(album_image); 144 | } 145 | } 146 | 147 | private class newAlbumAdapter extends RecyclerView.Adapter{ 148 | 149 | private List albumNewsList; 150 | 151 | private newAlbumAdapter(List list) { 152 | albumNewsList = list; 153 | } 154 | 155 | @Override 156 | public newAlbumHolder onCreateViewHolder(ViewGroup parent, int viewType) { 157 | 158 | LayoutInflater inflater = LayoutInflater.from(getActivity()); 159 | 160 | View view = inflater.inflate(R.layout.new_album_view, parent, false); 161 | 162 | return new newAlbumHolder(view); 163 | } 164 | 165 | @Override 166 | public void onBindViewHolder(newAlbumHolder holder, int position) { 167 | holder.bindAlbum(albumNewsList.get(position)); 168 | } 169 | 170 | @Override 171 | public int getItemCount() { 172 | return albumNewsList.size(); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.app.Fragment; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.util.Log; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | 16 | import com.sodastudio.jun.spotify_demo.R; 17 | import com.sodastudio.jun.spotify_demo.manager.ListManager; 18 | import com.sodastudio.jun.spotify_demo.manager.SearchPager; 19 | import com.sodastudio.jun.spotify_demo.model.SimplePlaylist; 20 | import com.sodastudio.jun.spotify_demo.model.TopArtist; 21 | import com.sodastudio.jun.spotify_demo.model.TopTrack; 22 | import com.squareup.picasso.Picasso; 23 | import com.squareup.picasso.Transformation; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import kaaes.spotify.webapi.android.models.PlaylistSimple; 29 | 30 | /** 31 | * Created by jun on 12/28/17. 32 | */ 33 | 34 | public class HomeFragment extends Fragment{ 35 | 36 | public static final String TAG = "Spotify HomeFragment"; 37 | 38 | private RecyclerView mMadeForYouRecyclerView; 39 | private RecyclerView mTopArtistRecyclerView; 40 | private RecyclerView mTopTrackRecyclerView; 41 | 42 | private HorizontalSimpleListAdapter simpleListAdapter; 43 | private HorizontalArtistAdapter artistAdapter; 44 | private HorizontalTrackAdapter trackAdapter; 45 | 46 | private SearchPager.onCompleteTopArtistListener artistListener; 47 | private SearchPager.onCompleteTopTrackListener trackListener; 48 | 49 | public void onCreate(@Nullable Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | 52 | } 53 | 54 | @Nullable 55 | @Override 56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 57 | 58 | View view = inflater.inflate(R.layout.fragment_home, container, false); 59 | 60 | mMadeForYouRecyclerView = view.findViewById(R.id.made_for_you_RecyclerView); 61 | mTopArtistRecyclerView = view.findViewById(R.id.top_artist_RecyclerView); 62 | mTopTrackRecyclerView = view.findViewById(R.id.top_tracks_RecyclerView); 63 | 64 | LinearLayoutManager horizontal_home_layout = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); 65 | LinearLayoutManager horizontal_artist_layout = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); 66 | LinearLayoutManager horizontal_track_layout = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); 67 | 68 | mMadeForYouRecyclerView.setLayoutManager(horizontal_home_layout); 69 | mTopArtistRecyclerView.setLayoutManager(horizontal_artist_layout); 70 | mTopTrackRecyclerView.setLayoutManager(horizontal_track_layout); 71 | 72 | updateUI(); 73 | 74 | return view; 75 | } 76 | 77 | private void updateUI(){ 78 | 79 | setMadeForYou(); 80 | 81 | setTopArtists(); 82 | 83 | setTopTracks(); 84 | } 85 | 86 | private void setMadeForYou(){ 87 | 88 | ArrayList mlist = ListManager.getInstance().getSimplePlaylists(); 89 | 90 | if (mlist == null || mlist.isEmpty()) { 91 | Log.d(TAG, "List is empty"); 92 | return; 93 | } 94 | 95 | if(simpleListAdapter == null) 96 | simpleListAdapter = new HorizontalSimpleListAdapter(mlist.subList(0,10)); 97 | 98 | mMadeForYouRecyclerView.setAdapter(simpleListAdapter); 99 | } 100 | 101 | private void setTopArtists(){ 102 | List artists = ListManager.getInstance().getTopArtists(); 103 | 104 | if(artists.size() == 0){ 105 | 106 | artistListener = new SearchPager.onCompleteTopArtistListener() { 107 | @Override 108 | public void onComplete() { 109 | Log.d(TAG, "onComplete!"); 110 | artistAdapter.notifyDataSetChanged(); 111 | setTopArtists(); 112 | } 113 | 114 | @Override 115 | public void onError(Throwable error) { 116 | 117 | } 118 | }; 119 | 120 | SearchPager.getInstance(getContext()).getMyTopArtist(artistListener); 121 | } 122 | 123 | if(artistAdapter == null) 124 | artistAdapter = new HorizontalArtistAdapter(artists); 125 | 126 | mTopArtistRecyclerView.setAdapter(artistAdapter); 127 | } 128 | 129 | private void setTopTracks(){ 130 | List tracks = ListManager.getInstance().getTopTracks(); 131 | 132 | if(tracks.size() == 0){ 133 | trackListener = new SearchPager.onCompleteTopTrackListener() { 134 | @Override 135 | public void onComplete() { 136 | trackAdapter.notifyDataSetChanged(); 137 | setTopTracks(); 138 | } 139 | 140 | @Override 141 | public void onError(Throwable error) { 142 | 143 | } 144 | 145 | }; 146 | 147 | SearchPager.getInstance(getContext()).getMyTopTracks(trackListener); 148 | } 149 | 150 | if(trackAdapter == null) 151 | trackAdapter = new HorizontalTrackAdapter(tracks); 152 | 153 | mTopTrackRecyclerView.setAdapter(trackAdapter); 154 | 155 | } 156 | 157 | private class HorizontalTrackHolder extends RecyclerView.ViewHolder{ 158 | 159 | private ImageView album_image_field; 160 | private TextView album_name_view; 161 | 162 | private HorizontalTrackHolder(View itemView) { 163 | super(itemView); 164 | 165 | album_image_field = itemView.findViewById(R.id.track_image_field); 166 | album_name_view = itemView.findViewById(R.id.track_name); 167 | } 168 | 169 | private void bindTrack(final TopTrack track){ 170 | 171 | album_name_view.setText(track.getName()); 172 | 173 | Picasso.with(getContext()) 174 | .load(track.getImg_url()) 175 | .transform(new Transformation() { 176 | @Override 177 | public Bitmap transform(Bitmap source) { 178 | final Bitmap copy = source.copy(source.getConfig(), true); 179 | source.recycle(); 180 | 181 | return copy; 182 | } 183 | 184 | @Override 185 | public String key() { 186 | return track.getName(); 187 | } 188 | }) 189 | .into(album_image_field); 190 | 191 | } 192 | 193 | } 194 | 195 | private class HorizontalTrackAdapter extends RecyclerView.Adapter{ 196 | 197 | private List tracks; 198 | 199 | private HorizontalTrackAdapter(List list){ 200 | tracks = list; 201 | } 202 | 203 | @Override 204 | public HorizontalTrackHolder onCreateViewHolder(ViewGroup parent, int viewType) { 205 | 206 | LayoutInflater inflater = LayoutInflater.from(getContext()); 207 | 208 | View view = inflater.inflate(R.layout.track_view, parent, false); 209 | 210 | return new HorizontalTrackHolder(view); 211 | } 212 | 213 | @Override 214 | public void onBindViewHolder(HorizontalTrackHolder holder, int position) { 215 | holder.bindTrack(tracks.get(position)); 216 | } 217 | 218 | @Override 219 | public int getItemCount() { 220 | return tracks.size(); 221 | } 222 | } 223 | 224 | 225 | private class HorizontalArtistHolder extends RecyclerView.ViewHolder{ 226 | 227 | private TopArtist artist; 228 | private TextView artist_name_view; 229 | private ImageView artist_image_field; 230 | 231 | private HorizontalArtistHolder(View itemView) { 232 | super(itemView); 233 | 234 | artist_name_view = itemView.findViewById(R.id.artist_name); 235 | artist_image_field = itemView.findViewById(R.id.artist_image_field); 236 | } 237 | 238 | private void bindArtist(final TopArtist artist){ 239 | this.artist = artist; 240 | 241 | artist_name_view.setText(artist.getName()); 242 | 243 | Picasso.with(getContext()) 244 | .load(artist.getImg_url()) 245 | .transform(new Transformation() { 246 | @Override 247 | public Bitmap transform(Bitmap source) { 248 | final Bitmap copy = source.copy(source.getConfig(), true); 249 | source.recycle(); 250 | 251 | return copy; 252 | } 253 | 254 | @Override 255 | public String key() { 256 | return artist.getName(); 257 | } 258 | }) 259 | .into(artist_image_field); 260 | 261 | 262 | } 263 | } 264 | 265 | private class HorizontalArtistAdapter extends RecyclerView.Adapter{ 266 | 267 | private List artists; 268 | 269 | private HorizontalArtistAdapter(List list){ 270 | this.artists = list; 271 | } 272 | 273 | @Override 274 | public HorizontalArtistHolder onCreateViewHolder(ViewGroup parent, int viewType) { 275 | 276 | LayoutInflater inflater = LayoutInflater.from(getContext()); 277 | 278 | View view = inflater.inflate(R.layout.artist_view, parent, false); 279 | 280 | return new HorizontalArtistHolder(view); 281 | } 282 | 283 | @Override 284 | public void onBindViewHolder(HorizontalArtistHolder holder, int position) { 285 | holder.bindArtist(artists.get(position)); 286 | } 287 | 288 | @Override 289 | public int getItemCount() { 290 | return artists.size(); 291 | } 292 | } 293 | 294 | 295 | 296 | private class HorizontalSimpleListHolder extends RecyclerView.ViewHolder{ 297 | 298 | private ImageView imageView; 299 | private TextView titleView; 300 | 301 | private HorizontalSimpleListHolder(View itemView) { 302 | super(itemView); 303 | 304 | imageView = itemView.findViewById(R.id.playlist_album_image); 305 | titleView = itemView.findViewById(R.id.playlist_album_title); 306 | } 307 | 308 | private void bindItem(final SimplePlaylist simple){ 309 | titleView.setText(simple.getName()); 310 | 311 | Picasso.with(getContext()) 312 | .load(simple.getImg_url()) 313 | .transform(new Transformation() { 314 | @Override 315 | public Bitmap transform(Bitmap source) { 316 | final Bitmap copy = source.copy(source.getConfig(), true); 317 | source.recycle(); 318 | 319 | return copy; 320 | } 321 | 322 | @Override 323 | public String key() { 324 | return simple.getName(); 325 | } 326 | }) 327 | .into(imageView); 328 | } 329 | } 330 | 331 | private class HorizontalSimpleListAdapter extends RecyclerView.Adapter{ 332 | 333 | private List testList; 334 | 335 | private HorizontalSimpleListAdapter(List list){ 336 | testList = list; 337 | } 338 | 339 | @Override 340 | public HorizontalSimpleListHolder onCreateViewHolder(ViewGroup parent, int viewType) { 341 | 342 | LayoutInflater inflater = LayoutInflater.from(getContext()); 343 | 344 | View view = inflater.inflate(R.layout.album_view, parent, false); 345 | 346 | return new HorizontalSimpleListHolder(view); 347 | } 348 | 349 | @Override 350 | public void onBindViewHolder(HorizontalSimpleListHolder holder, int position) { 351 | 352 | holder.bindItem(testList.get(position)); 353 | } 354 | 355 | @Override 356 | public int getItemCount() { 357 | return testList.size(); 358 | } 359 | } 360 | 361 | 362 | } 363 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/LibraryFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.sodastudio.jun.spotify_demo.R; 11 | 12 | /** 13 | * Created by jun on 12/28/17. 14 | */ 15 | 16 | public class LibraryFragment extends Fragment { 17 | 18 | @Override 19 | public void onCreate(@Nullable Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 26 | View view = inflater.inflate(R.layout.fragment_library, container, false); 27 | 28 | return view; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/MainFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | 4 | import android.app.FragmentManager; 5 | import android.content.Context; 6 | import android.graphics.Typeface; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Bundle; 9 | import android.support.annotation.Nullable; 10 | import android.app.Fragment; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.animation.AnimationUtils; 16 | import android.widget.Button; 17 | import android.widget.TextView; 18 | 19 | import com.sodastudio.jun.spotify_demo.R; 20 | 21 | /** 22 | * Created by jun on 12/28/17. 23 | */ 24 | 25 | public class MainFragment extends Fragment { 26 | 27 | private static final String TAG = "Spotify MainFragment"; 28 | 29 | private FragmentManager manager; 30 | 31 | private TextView homeText; 32 | private TextView browseText; 33 | private TextView searchText; 34 | private TextView radioText; 35 | private TextView libraryText; 36 | 37 | @Override 38 | public void onCreate(@Nullable Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | 41 | manager = getFragmentManager(); 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 47 | 48 | 49 | View view = inflater.inflate(R.layout.fragment_main, container, false); 50 | 51 | Button homeLayout = view.findViewById(R.id.nav_home); 52 | Button browseLayout = view.findViewById(R.id.nav_browse); 53 | Button searchLayout = view.findViewById(R.id.nav_search); 54 | Button radioLayout = view.findViewById(R.id.nav_radio); 55 | Button libraryLayout = view.findViewById(R.id.nav_library); 56 | 57 | homeText = view.findViewById(R.id.nav_home_text); 58 | browseText = view.findViewById(R.id.nav_browse_text); 59 | searchText = view.findViewById(R.id.nav_search_text); 60 | radioText = view.findViewById(R.id.nav_radio_text); 61 | libraryText = view.findViewById(R.id.nav_library_text); 62 | 63 | homeLayout.setOnClickListener(mListener); 64 | browseLayout.setOnClickListener(mListener); 65 | searchLayout.setOnClickListener(mListener); 66 | radioLayout.setOnClickListener(mListener); 67 | libraryLayout.setOnClickListener(mListener); 68 | 69 | //homeLayout.callOnClick(); 70 | 71 | return view; 72 | } 73 | 74 | Drawable home; 75 | Drawable browse; 76 | Drawable search; 77 | Drawable radio; 78 | Drawable library; 79 | int focusMode; 80 | int defocusMode; 81 | 82 | int prev_clicked_id = -1; 83 | View prev_view; 84 | 85 | @Override 86 | public void onAttach(Context context) { 87 | super.onAttach(context); 88 | 89 | home = getResources().getDrawable(R.drawable.ic_home_black_24dp, null); 90 | browse = getResources().getDrawable(R.drawable.ic_open_in_browser_black_24dp, null); 91 | search = getResources().getDrawable(R.drawable.ic_search_black_24dp, null); 92 | radio = getResources().getDrawable(R.drawable.ic_radio_black_24dp, null); 93 | library = getResources().getDrawable(R.drawable.ic_library_music_black_24dp, null); 94 | 95 | focusMode = getResources().getColor(R.color.colorWhite, null);; 96 | defocusMode = getResources().getColor(R.color.colorNavIcon, null); 97 | } 98 | 99 | View.OnClickListener mListener = new View.OnClickListener() { 100 | @Override 101 | public void onClick(View view) { 102 | 103 | view.startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.nav_click)); 104 | 105 | switch (view.getId()) 106 | { 107 | case R.id.nav_home: 108 | Log.d(TAG, "HOME"); 109 | 110 | if(view.isActivated()) break; 111 | 112 | manager.beginTransaction().replace(R.id.fragment, new HomeFragment()).commit(); 113 | 114 | setFocus(R.id.nav_home, view); 115 | setDeFocus(prev_clicked_id, prev_view); 116 | 117 | prev_clicked_id = R.id.nav_home; 118 | prev_view = view; 119 | break; 120 | case R.id.nav_browse: 121 | Log.d(TAG, "BROWSE"); 122 | 123 | if(view.isActivated()) break; 124 | 125 | manager.beginTransaction().replace(R.id.fragment, new BrowseFragment()).commit(); 126 | 127 | setFocus(R.id.nav_browse, view); 128 | setDeFocus(prev_clicked_id, prev_view); 129 | 130 | prev_clicked_id = R.id.nav_browse; 131 | prev_view = view; 132 | break; 133 | case R.id.nav_search: 134 | Log.d(TAG, "SEARCH"); 135 | 136 | if(view.isActivated()) break; 137 | 138 | SearchFragment.getFragmentInstance(manager, "SearchFragment"); 139 | 140 | setFocus(R.id.nav_search, view); 141 | setDeFocus(prev_clicked_id, prev_view); 142 | 143 | prev_clicked_id = R.id.nav_search; 144 | prev_view = view; 145 | break; 146 | case R.id.nav_radio: 147 | Log.d(TAG, "RADIO"); 148 | 149 | if(view.isActivated()) break; 150 | 151 | manager.beginTransaction().replace(R.id.fragment, new RadioFragment()).commit(); 152 | 153 | setFocus(R.id.nav_radio, view); 154 | setDeFocus(prev_clicked_id, prev_view); 155 | 156 | prev_clicked_id = R.id.nav_radio; 157 | prev_view = view; 158 | break; 159 | case R.id.nav_library: 160 | Log.d(TAG, "LIBRARY"); 161 | 162 | if(view.isActivated()) break; 163 | 164 | manager.beginTransaction().replace(R.id.fragment, new LibraryFragment()).commit(); 165 | 166 | setFocus(R.id.nav_library, view); 167 | setDeFocus(prev_clicked_id, prev_view); 168 | 169 | prev_clicked_id = R.id.nav_library; 170 | prev_view = view; 171 | break; 172 | } 173 | } 174 | }; 175 | 176 | private void setFocus(int res_id, View view){ 177 | switch (res_id) 178 | { 179 | case R.id.nav_home: 180 | home.setTint(focusMode); 181 | view.setBackground(home); 182 | homeText.setTextColor(focusMode); 183 | homeText.setTypeface(Typeface.DEFAULT_BOLD); 184 | view.setActivated(true); 185 | break; 186 | case R.id.nav_browse: 187 | browse.setTint(focusMode); 188 | view.setBackground(browse); 189 | browseText.setTextColor(focusMode); 190 | browseText.setTypeface(Typeface.DEFAULT_BOLD); 191 | view.setActivated(true); 192 | break; 193 | case R.id.nav_search: 194 | search.setTint(focusMode); 195 | view.setBackground(search); 196 | searchText.setTextColor(focusMode); 197 | searchText.setTypeface(Typeface.DEFAULT_BOLD); 198 | view.setActivated(true); 199 | break; 200 | case R.id.nav_radio: 201 | radio.setTint(focusMode); 202 | view.setBackground(radio); 203 | radioText.setTextColor(focusMode); 204 | radioText.setTypeface(Typeface.DEFAULT_BOLD); 205 | view.setActivated(true); 206 | break; 207 | case R.id.nav_library: 208 | library.setTint(focusMode); 209 | view.setBackground(library); 210 | libraryText.setTextColor(focusMode); 211 | libraryText.setTypeface(Typeface.DEFAULT_BOLD); 212 | view.setActivated(true); 213 | break; 214 | } 215 | } 216 | private void setDeFocus(int res_id, View view){ 217 | switch (res_id) 218 | { 219 | case R.id.nav_home: 220 | home.setTint(defocusMode); 221 | view.setBackground(home); 222 | homeText.setTextColor(defocusMode); 223 | homeText.setTypeface(Typeface.DEFAULT); 224 | view.setActivated(false); 225 | break; 226 | case R.id.nav_browse: 227 | browse.setTint(defocusMode); 228 | view.setBackground(browse); 229 | browseText.setTextColor(defocusMode); 230 | browseText.setTypeface(Typeface.DEFAULT); 231 | view.setActivated(false); 232 | break; 233 | case R.id.nav_search: 234 | search.setTint(defocusMode); 235 | view.setBackground(search); 236 | searchText.setTextColor(defocusMode); 237 | searchText.setTypeface(Typeface.DEFAULT); 238 | view.setActivated(false); 239 | break; 240 | case R.id.nav_radio: 241 | radio.setTint(defocusMode); 242 | view.setBackground(radio); 243 | radioText.setTextColor(defocusMode); 244 | radioText.setTypeface(Typeface.DEFAULT); 245 | view.setActivated(false); 246 | break; 247 | case R.id.nav_library: 248 | library.setTint(defocusMode); 249 | view.setBackground(library); 250 | libraryText.setTextColor(defocusMode); 251 | libraryText.setTypeface(Typeface.DEFAULT); 252 | view.setActivated(false); 253 | break; 254 | case -1: 255 | break; 256 | default: 257 | break; 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/RadioFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import com.sodastudio.jun.spotify_demo.R; 11 | 12 | /** 13 | * Created by jun on 12/28/17. 14 | */ 15 | 16 | public class RadioFragment extends Fragment { 17 | @Override 18 | public void onCreate(@Nullable Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | } 21 | 22 | @Nullable 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 25 | 26 | View view = inflater.inflate(R.layout.fragment_radio, container, false); 27 | 28 | return view; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/SearchFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Fragment; 5 | import android.app.FragmentManager; 6 | import android.app.FragmentTransaction; 7 | import android.os.Bundle; 8 | import android.support.annotation.Nullable; 9 | import android.support.v4.view.ViewCompat; 10 | import android.support.v4.view.ViewPropertyAnimatorListener; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.EditText; 18 | import android.widget.ImageButton; 19 | import android.widget.ImageView; 20 | import android.widget.RelativeLayout; 21 | import android.widget.TextView; 22 | 23 | import com.sodastudio.jun.spotify_demo.R; 24 | import com.sodastudio.jun.spotify_demo.manager.PlaybackManager; 25 | import com.sodastudio.jun.spotify_demo.manager.ListManager; 26 | import com.sodastudio.jun.spotify_demo.model.ArtistSearch; 27 | 28 | import java.util.List; 29 | 30 | import iammert.com.view.scalinglib.ScalingLayout; 31 | import iammert.com.view.scalinglib.ScalingLayoutListener; 32 | import iammert.com.view.scalinglib.State; 33 | 34 | /** 35 | * Created by jun on 12/28/17. 36 | */ 37 | 38 | public class SearchFragment extends Fragment{ 39 | 40 | private static final String TAG = "Spotify SearchFragment"; 41 | 42 | private TextView textViewSearch; 43 | private EditText editTextSearch; 44 | private ScalingLayout scalingLayout; 45 | private RecyclerView searchListView; 46 | private ArtistListAdapter mAdapter; 47 | 48 | FragmentManager fragmentManager; 49 | 50 | @SuppressLint("ResourceType") 51 | public static SearchFragment getFragmentInstance(FragmentManager fm, String tag){ 52 | SearchFragment fragment = (SearchFragment)fm.findFragmentByTag(tag); 53 | 54 | if(fragment == null){ 55 | fragment = new SearchFragment(); 56 | 57 | FragmentTransaction ft = fm.beginTransaction(); 58 | ft.replace(R.id.fragment, fragment, tag).commitAllowingStateLoss(); 59 | } 60 | return fragment; 61 | } 62 | 63 | @Override 64 | public void onCreate(@Nullable Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | } 67 | 68 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 69 | 70 | // check if the previous state is SearchResultFragment 71 | PlaybackManager manager = PlaybackManager.getInstance(); 72 | if(manager.isSearchResultFragmentAdded()) 73 | { 74 | fragmentManager = getFragmentManager(); 75 | FragmentTransaction ft = fragmentManager.beginTransaction(); 76 | ft.add(R.id.fragment, SearchResultFragment.newInstance("empty")) 77 | .addToBackStack(TAG) 78 | .commit(); 79 | } 80 | 81 | final View view = inflater.inflate(R.layout.fragment_search, container, false); 82 | 83 | textViewSearch = view.findViewById(R.id.textViewSearch); 84 | final RelativeLayout searchLayout = view.findViewById(R.id.searchLayout); 85 | final ImageButton searchButton = view.findViewById(R.id.search_text_button); 86 | 87 | editTextSearch = view.findViewById(R.id.editTextSearch); 88 | searchButton.setOnClickListener(mListener); 89 | scalingLayout = view.findViewById(R.id.scalingLayout); 90 | searchListView = view.findViewById(R.id.Artist_search_list); 91 | 92 | searchListView.setLayoutManager(new LinearLayoutManager(getContext())); 93 | 94 | mAdapter = new ArtistListAdapter(ListManager.getInstance().getArtists()); 95 | searchListView.setAdapter(mAdapter); 96 | 97 | scalingLayout.setListener(new ScalingLayoutListener() { 98 | @Override 99 | public void onCollapsed() { 100 | ViewCompat.animate(textViewSearch).alpha(1).setDuration(150).start(); 101 | ViewCompat.animate(searchLayout).alpha(0).setDuration(150).setListener(new ViewPropertyAnimatorListener() { 102 | @Override 103 | public void onAnimationStart(View view) { 104 | textViewSearch.setVisibility(View.VISIBLE); 105 | } 106 | 107 | @Override 108 | public void onAnimationEnd(View view) { 109 | searchLayout.setVisibility(View.INVISIBLE); 110 | } 111 | 112 | @Override 113 | public void onAnimationCancel(View view) { 114 | 115 | } 116 | }).start(); 117 | } 118 | 119 | @Override 120 | public void onExpanded() { 121 | ViewCompat.animate(textViewSearch).alpha(0).setDuration(200).start(); 122 | ViewCompat.animate(searchLayout).alpha(1).setDuration(200).setListener(new ViewPropertyAnimatorListener() { 123 | @Override 124 | public void onAnimationStart(View view) { 125 | searchLayout.setVisibility(View.VISIBLE); 126 | } 127 | 128 | @Override 129 | public void onAnimationEnd(View view) { 130 | textViewSearch.setVisibility(View.INVISIBLE); 131 | } 132 | 133 | @Override 134 | public void onAnimationCancel(View view) { 135 | 136 | } 137 | }).start(); 138 | } 139 | 140 | @Override 141 | public void onProgress(float progress) { 142 | 143 | } 144 | }); 145 | 146 | scalingLayout.setOnClickListener(new View.OnClickListener() { 147 | @Override 148 | public void onClick(View view) { 149 | if (scalingLayout.getState() == State.COLLAPSED) { 150 | scalingLayout.expand(); 151 | } 152 | } 153 | }); 154 | 155 | view.findViewById(R.id.rootLayout).setOnClickListener(new View.OnClickListener() { 156 | @Override 157 | public void onClick(View view) { 158 | if (scalingLayout.getState() == State.EXPANDED) { 159 | scalingLayout.collapse(); 160 | 161 | if(editTextSearch.getText().toString().equals("")) 162 | textViewSearch.setText("Search"); 163 | } 164 | } 165 | }); 166 | 167 | return view; 168 | } 169 | 170 | View.OnClickListener mListener = new View.OnClickListener() { 171 | @Override 172 | public void onClick(View view) { 173 | switch (view.getId()){ 174 | case R.id.search_text_button: 175 | String query = editTextSearch.getText().toString(); 176 | 177 | Log.d(TAG, "Query: " + query); 178 | 179 | scalingLayout.collapse(); 180 | 181 | if(query.equals("")){ 182 | textViewSearch.setText("Search"); 183 | } else { 184 | 185 | fragmentManager = getFragmentManager(); 186 | FragmentTransaction ft = fragmentManager.beginTransaction(); 187 | ft.add(R.id.fragment, SearchResultFragment.newInstance(query)) 188 | .addToBackStack(TAG) 189 | .commit(); 190 | 191 | 192 | 193 | textViewSearch.setText(query); 194 | } 195 | 196 | break; 197 | } 198 | } 199 | }; 200 | 201 | public void refresh(){ 202 | mAdapter.notifyDataSetChanged(); 203 | } 204 | 205 | private class ArtistListHolder extends RecyclerView.ViewHolder 206 | { 207 | private TextView artistName; 208 | private ImageView artistImage; 209 | private ArtistSearch artistSearch; 210 | 211 | private ArtistListHolder(View itemView) { 212 | super(itemView); 213 | 214 | artistImage = itemView.findViewById(R.id.search_artist_image_field); 215 | artistName = itemView.findViewById(R.id.search_artist_name); 216 | } 217 | 218 | private void bindArtist(ArtistSearch search) 219 | { 220 | artistSearch = search; 221 | 222 | artistName.setText(artistSearch.getName()); 223 | artistImage.setImageBitmap(artistSearch.getImage()); 224 | } 225 | } 226 | 227 | private class ArtistListAdapter extends RecyclerView.Adapter 228 | { 229 | private List artistSearchList; 230 | 231 | private ArtistListAdapter(List list){ 232 | artistSearchList = list; 233 | } 234 | 235 | @Override 236 | public ArtistListHolder onCreateViewHolder(ViewGroup parent, int viewType) { 237 | 238 | LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); 239 | 240 | View view = layoutInflater.inflate(R.layout.artist_search, parent, false); 241 | 242 | return new ArtistListHolder(view); 243 | } 244 | 245 | @Override 246 | public void onBindViewHolder(ArtistListHolder holder, int position) { 247 | holder.bindArtist(artistSearchList.get(position)); 248 | } 249 | 250 | @Override 251 | public int getItemCount() { 252 | return artistSearchList.size(); 253 | } 254 | } 255 | } 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /app/src/main/java/com/sodastudio/jun/spotify_demo/ui/SearchResultFragment.java: -------------------------------------------------------------------------------- 1 | package com.sodastudio.jun.spotify_demo.ui; 2 | 3 | import android.app.Fragment; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.os.Bundle; 7 | import android.os.Parcelable; 8 | import android.support.annotation.Nullable; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.support.v7.widget.Toolbar; 12 | import android.util.Log; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.ImageButton; 17 | import android.widget.ImageView; 18 | import android.widget.TextView; 19 | 20 | import com.sodastudio.jun.spotify_demo.MainActivity; 21 | import com.sodastudio.jun.spotify_demo.R; 22 | import com.sodastudio.jun.spotify_demo.TrackDetailActivity; 23 | import com.sodastudio.jun.spotify_demo.manager.PlaybackManager; 24 | import com.sodastudio.jun.spotify_demo.manager.SearchPager; 25 | import com.sodastudio.jun.spotify_demo.manager.ListManager; 26 | import com.sodastudio.jun.spotify_demo.model.ArtistSearch; 27 | import com.sodastudio.jun.spotify_demo.model.Music; 28 | import com.spotify.sdk.android.player.Error; 29 | import com.spotify.sdk.android.player.PlayerEvent; 30 | import com.spotify.sdk.android.player.SpotifyPlayer; 31 | import com.squareup.picasso.Picasso; 32 | import com.squareup.picasso.Transformation; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | import kaaes.spotify.webapi.android.models.Track; 38 | 39 | /** 40 | * Created by jun on 12/31/17. 41 | */ 42 | 43 | public class SearchResultFragment extends Fragment implements SpotifyPlayer.NotificationCallback{ 44 | 45 | public static final String QUERY = "QUERY"; 46 | public static final String TAG = "Spotify SearchResult"; 47 | public static final String DETAIL_MUSIC = "Detail Music"; 48 | 49 | private String query; 50 | 51 | private Toolbar toolbar; 52 | private ImageView background_album; 53 | 54 | private RecyclerView mRecyclerView; 55 | private TrackListAdapter mAdapter; 56 | 57 | private SearchPager mSearchPager; 58 | private SearchPager.CompleteListener mSearchListener; 59 | private SearchPager.ArtistListener mArtistListener; 60 | 61 | private ListManager listManager; 62 | private PlaybackManager playbackManager; 63 | 64 | private LinearLayoutManager layoutManager; 65 | 66 | private SpotifyPlayer mPlayer = MainActivity.mPlayer; 67 | 68 | public static SearchResultFragment newInstance(String query){ 69 | Bundle args = new Bundle(); 70 | args.putString(QUERY, query); 71 | 72 | SearchResultFragment searchResultFragment = new SearchResultFragment(); 73 | searchResultFragment.setArguments(args); 74 | 75 | return searchResultFragment; 76 | } 77 | 78 | @Override 79 | public void onCreate(@Nullable Bundle savedInstanceState) { 80 | super.onCreate(savedInstanceState); 81 | mSearchPager = SearchPager.getInstance(getContext()); 82 | setRetainInstance(true); 83 | listManager = ListManager.getInstance(); 84 | playbackManager = PlaybackManager.getInstance(); 85 | 86 | playbackManager.setSearchResultFragmentAdded(true); 87 | 88 | mPlayer.addNotificationCallback(SearchResultFragment.this); 89 | 90 | Log.d(TAG, "onCreate"); 91 | } 92 | 93 | @Nullable 94 | @Override 95 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 96 | 97 | query = getArguments().getString(QUERY); 98 | 99 | Log.d(TAG, "Query: " + query); 100 | 101 | View view = inflater.inflate(R.layout.fragment_search_result, container, false); 102 | 103 | playbackManager = PlaybackManager.getInstance(); 104 | state = playbackManager.getState(); 105 | 106 | layoutManager = new LinearLayoutManager(getActivity()); 107 | 108 | if(state != null){ 109 | layoutManager.onRestoreInstanceState(state); 110 | } 111 | 112 | mRecyclerView = view.findViewById(R.id.track_list_recycler_view); 113 | mRecyclerView.setLayoutManager(layoutManager); 114 | 115 | toolbar = view.findViewById(R.id.toolbar); 116 | 117 | toolbar.setNavigationIcon(getResources().getDrawable(R.drawable.ic_arrow_back_white_24dp, null)); 118 | toolbar.setNavigationOnClickListener(new View.OnClickListener() { 119 | 120 | @Override 121 | public void onClick(View view) { 122 | getFragmentManager().beginTransaction().detach(SearchResultFragment.this).commit(); 123 | 124 | PlaybackManager playbackManager = PlaybackManager.getInstance(); 125 | playbackManager.setSearchResultFragmentAdded(false); 126 | } 127 | }); 128 | 129 | background_album = view.findViewById(R.id.background_album_field); 130 | 131 | if(!query.equals("empty")) { 132 | queryData(); 133 | } else { 134 | updateView(); 135 | } 136 | 137 | return view; 138 | } 139 | 140 | private void queryData(){ 141 | 142 | mSearchListener = new SearchPager.CompleteListener() { 143 | @Override 144 | public void onComplete(List items) { 145 | 146 | listManager.clearList(); 147 | 148 | for(Track track : items){ 149 | //Log.d(TAG, "success! link: " + track.uri); // music link 150 | //Log.d(TAG, "success! title: " + track.name); // title 151 | //Log.d(TAG, "success! album: " + track.album.name); // album name 152 | //Log.d(TAG, "success! album img: " + track.album.images.get(0).url); // album image 153 | //Log.d(TAG, "success! duration: " + track.duration_ms); // song duration 154 | //Log.d(TAG, "success! artists: " + track.artists.get(0).name); // artists 155 | 156 | Music music = new Music( 157 | track.id, 158 | track.uri, 159 | track.name, 160 | track.album.name, 161 | track.album.images.get(0).url, 162 | track.duration_ms, 163 | track.artists.get(0).name, 164 | track.artists.get(0).id 165 | ); 166 | 167 | listManager.addTrack(music); 168 | } 169 | 170 | Log.d(TAG, "query finished! Updating view..."); 171 | updateView(); 172 | } 173 | 174 | @Override 175 | public void onError(Throwable error) { 176 | Log.d(TAG, error.getMessage()); 177 | } 178 | }; 179 | 180 | mSearchPager.getTracksFromSearch(query, mSearchListener); 181 | } 182 | 183 | private void updateView(){ 184 | 185 | List mList = listManager.getTrackLists(); 186 | 187 | if(mList.size() == 0) return; 188 | 189 | if(mAdapter == null) 190 | mAdapter = new TrackListAdapter(mList); 191 | 192 | mRecyclerView.setAdapter(mAdapter); 193 | 194 | final String artistName = mList.get(0).getArtist(); 195 | 196 | toolbar.setTitle(artistName); 197 | 198 | mArtistListener = new SearchPager.ArtistListener() { 199 | @Override 200 | public void onComplete(String img_url) { 201 | Picasso.with(getContext()) 202 | .load(img_url) 203 | .transform(new Transformation() { 204 | @Override 205 | public Bitmap transform(Bitmap source) { 206 | final Bitmap copy = source.copy(source.getConfig(), true); 207 | source.recycle(); 208 | 209 | listManager.addArtist(new ArtistSearch(artistName, copy)); 210 | 211 | return copy; 212 | } 213 | 214 | @Override 215 | public String key() { 216 | return query; 217 | } 218 | }) 219 | .into(background_album); 220 | } 221 | 222 | @Override 223 | public void onError(Throwable error) { 224 | 225 | } 226 | }; 227 | mSearchPager.getArtist(mList.get(0).getArtist_id(), mArtistListener); 228 | } 229 | 230 | 231 | @Override 232 | public void onPlaybackEvent(PlayerEvent playerEvent) { 233 | 234 | if(!playerEvent.name().contains("Metadata")) 235 | Log.d(TAG, "Playback event received: " + playerEvent.name()); 236 | 237 | switch (playerEvent.name()) { 238 | // Handle event type as necessary 239 | 240 | case "kSpPlaybackNotifyPlay": 241 | break; 242 | 243 | case "kSpPlaybackNotifyPause": 244 | Log.d(TAG, mPlayer.getMetadata().currentTrack.name); 245 | 246 | String title = mPlayer.getMetadata().currentTrack.name; 247 | String album = mPlayer.getMetadata().currentTrack.albumName; 248 | 249 | Music music = ListManager.getInstance().findCurrentMusic(title, album); 250 | 251 | if(music != null) 252 | music.setPlaying(false); 253 | if(mAdapter != null) 254 | mAdapter.notifyDataSetChanged(); 255 | 256 | break; 257 | 258 | case "kSpPlaybackNotifyTrackChanged": 259 | break; 260 | 261 | case "kSpPlaybackEventAudioFlush": 262 | break; 263 | 264 | default: 265 | break; 266 | } 267 | } 268 | 269 | @Override 270 | public void onPlaybackError(Error error) { 271 | 272 | } 273 | 274 | private class TrackListHolder extends RecyclerView.ViewHolder 275 | { 276 | private Music music; 277 | private TextView title_text; 278 | private TextView artist_text; 279 | private TextView album_text; 280 | private ImageButton more_button; 281 | 282 | private TrackListHolder(final View itemView){ 283 | super(itemView); 284 | 285 | title_text = itemView.findViewById(R.id.title_field); 286 | artist_text = itemView.findViewById(R.id.artist_field); 287 | album_text = itemView.findViewById(R.id.album_field); 288 | more_button = itemView.findViewById(R.id.more_horiz); 289 | 290 | more_button.setOnClickListener(new View.OnClickListener() { 291 | @Override 292 | public void onClick(View view) { 293 | 294 | Intent intent = new Intent(getContext(), TrackDetailActivity.class); 295 | 296 | Bundle args = new Bundle(); 297 | args.putParcelable(DETAIL_MUSIC, music); 298 | 299 | intent.putExtras(args); 300 | startActivity(intent); 301 | } 302 | }); 303 | 304 | title_text.setOnClickListener(new View.OnClickListener() { 305 | @Override 306 | public void onClick(View view) { 307 | 308 | if (mPlayer.getPlaybackState().isPlaying) { 309 | String album = mPlayer.getMetadata().currentTrack.albumName; 310 | String title = mPlayer.getMetadata().currentTrack.name; 311 | 312 | Music prevMusic = ListManager.getInstance().findCurrentMusic(title, album); 313 | 314 | if (prevMusic != null) { 315 | Log.d(TAG, "prev playing: " + prevMusic.getTitle()); 316 | prevMusic.setPlaying(false); 317 | } 318 | } 319 | 320 | // Check again 321 | String prevTitle = ListManager.getInstance().getCurrent_playing_title(); 322 | String prevAlbum = ListManager.getInstance().getCurrent_playing_album(); 323 | 324 | Music prevMusic = ListManager.getInstance().findCurrentMusic(prevTitle, prevAlbum); 325 | if (prevMusic != null) { 326 | prevMusic.setPlaying(false); 327 | } 328 | 329 | // Play music 330 | mPlayer.playUri(null, music.getUri(), 0, 0); 331 | Log.d(TAG, "now playing: " + music.getTitle()); 332 | music.setPlaying(true); 333 | 334 | ListManager.getInstance().setCurrent_playing_title(music.getTitle()); 335 | ListManager.getInstance().setCurrent_playing_album(music.getAlbum()); 336 | 337 | mAdapter.notifyDataSetChanged(); 338 | } 339 | }); 340 | } 341 | 342 | private void bindMusic(Music m) 343 | { 344 | music = m; 345 | 346 | String title = music.getTitle(); 347 | String album = music.getAlbum(); 348 | 349 | if(title.length() > 40){ 350 | title = title.substring(0, 40); 351 | title += "..."; 352 | } 353 | 354 | if(album.length() > 40){ 355 | album = album.substring(0,40); 356 | album += "..."; 357 | } 358 | 359 | title_text.setText(title); 360 | artist_text.setText(music.getArtist()); 361 | album_text.setText(album); 362 | 363 | if(music.isPlaying()) 364 | title_text.setTextColor(getResources().getColor(R.color.colorAccent, null)); 365 | else 366 | title_text.setTextColor(getResources().getColor(R.color.colorWhite, null)); 367 | 368 | 369 | } 370 | } 371 | 372 | private class TrackListAdapter extends RecyclerView.Adapter{ 373 | 374 | private List musicList; 375 | private TrackListAdapter(List list){ 376 | musicList = list; 377 | } 378 | 379 | @Override 380 | public TrackListHolder onCreateViewHolder(ViewGroup parent, int viewType) { 381 | 382 | LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); 383 | 384 | View view = layoutInflater.inflate(R.layout.music, parent, false); 385 | 386 | return new TrackListHolder(view); 387 | } 388 | 389 | @Override 390 | public void onBindViewHolder(TrackListHolder holder, int position) { 391 | Music music = musicList.get(position); 392 | 393 | holder.bindMusic(music); 394 | } 395 | 396 | @Override 397 | public int getItemCount() { 398 | return musicList.size(); 399 | } 400 | 401 | } 402 | 403 | @Override 404 | public void onResume() { 405 | super.onResume(); 406 | Log.d(TAG, "onResume"); 407 | } 408 | 409 | Parcelable state; 410 | 411 | @Override 412 | public void onPause() { 413 | super.onPause(); 414 | Log.d(TAG, "onPause"); 415 | 416 | state = layoutManager.onSaveInstanceState(); 417 | 418 | playbackManager = PlaybackManager.getInstance(); 419 | playbackManager.setState(state); 420 | 421 | 422 | Fragment fragment = getFragmentManager().findFragmentByTag("SearchFragment"); 423 | ((SearchFragment)fragment).refresh(); 424 | } 425 | 426 | @Override 427 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 428 | super.onViewCreated(view, savedInstanceState); 429 | Log.d(TAG, "onViewCreated"); 430 | } 431 | 432 | @Override 433 | public void onDestroyView() { 434 | super.onDestroyView(); 435 | Log.d(TAG, "onDestroyView"); 436 | 437 | 438 | } 439 | 440 | 441 | } 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /app/src/main/res/anim/alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_slide_left_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_slide_right_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/anim/hold.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/nav_click.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/pull_up_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/push_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/translate.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_home_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/create_account_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/create_account_normal.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/create_account_pressed.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/follow_rect.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_file_upload_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_library_music_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more_horiz_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_open_in_browser_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playlist_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_radio_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_surround_sound_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/login_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/login_normal.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/login_pressed.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/main_logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/src/main/res/drawable/main_logo_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/maroon_sample_album.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/src/main/res/drawable/maroon_sample_album.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_rect.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sodastudio_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlee91/android-spotify-demo/c7e88a7095fce7f67883e53d0cd586fa694f0a35/app/src/main/res/drawable/sodastudio_logo.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splashscreen.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_spotify_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 27 | 28 | 35 | 36 | 42 | 43 |