├── settings.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── auth-sample ├── keystore │ └── example.keystore ├── src │ └── main │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── spotify │ │ └── sdk │ │ └── android │ │ └── auth │ │ └── sample │ │ └── MainActivity.java └── build.gradle ├── catalog-info.yaml ├── config ├── lint.xml ├── findbugs-filter.xml └── checkstyle.xml ├── auth-lib ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── spotify │ │ │ └── sdk │ │ │ └── android │ │ │ └── auth │ │ │ ├── app │ │ │ └── FakeSha1HashUtil.java │ │ │ ├── LoginActivityTest.java │ │ │ ├── PKCEInformationFactoryTest.java │ │ │ ├── TokenExchangeRequestTest.java │ │ │ ├── AuthorizationResponseTest.java │ │ │ ├── TokenExchangeResponseTest.java │ │ │ └── AuthorizationClientTest.java │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── spotify │ │ │ │ └── sdk │ │ │ │ └── android │ │ │ │ └── auth │ │ │ │ ├── AccountsQueryParameters.java │ │ │ │ ├── IntentExtras.java │ │ │ │ ├── app │ │ │ │ ├── Sha1HashUtil.java │ │ │ │ ├── SpotifyAuthHandler.java │ │ │ │ └── SpotifyNativeAuthUtil.java │ │ │ │ ├── AuthorizationHandler.java │ │ │ │ ├── PKCEInformationFactory.java │ │ │ │ ├── PKCEInformation.java │ │ │ │ ├── TokenExchangeResponse.java │ │ │ │ ├── TokenExchangeRequest.java │ │ │ │ ├── AuthorizationResponse.java │ │ │ │ └── AuthorizationRequest.java │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── layout │ │ │ │ ├── com_spotify_sdk_login_activity.xml │ │ │ │ └── com_spotify_sdk_login_dialog.xml │ │ └── AndroidManifest.xml │ ├── auth │ │ ├── java │ │ │ └── com │ │ │ │ └── spotify │ │ │ │ └── sdk │ │ │ │ └── android │ │ │ │ └── auth │ │ │ │ ├── browser │ │ │ │ ├── RedirectUriReceiverActivity.java │ │ │ │ ├── CustomTabsSupportChecker.java │ │ │ │ └── BrowserAuthHandler.java │ │ │ │ └── FallbackHandlerProvider.java │ │ └── AndroidManifest.xml │ ├── store │ │ └── java │ │ │ └── com │ │ │ └── spotify │ │ │ └── sdk │ │ │ └── android │ │ │ └── auth │ │ │ ├── FallbackHandlerProvider.java │ │ │ └── store │ │ │ └── PlayStoreHandler.java │ ├── testStore │ │ └── java │ │ │ └── com │ │ │ └── spotify │ │ │ └── sdk │ │ │ └── android │ │ │ └── auth │ │ │ └── LoginActivityStoreTest.java │ └── testAuth │ │ └── java │ │ └── com │ │ └── spotify │ │ └── sdk │ │ └── android │ │ └── auth │ │ └── LoginActivityAuthTest.java └── build.gradle ├── .editorconfig ├── gradle.properties ├── .travis.yml ├── gradlew.bat ├── CHANGELOG.md ├── README.md ├── .github └── workflows │ └── publish-javadoc.yml ├── gradlew └── LICENSE /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':auth-lib', ':auth-sample' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea/ 5 | .DS_Store 6 | build/ 7 | doc/ 8 | doc-store/ 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /auth-sample/keystore/example.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/keystore/example.keystore -------------------------------------------------------------------------------- /auth-sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /auth-sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /auth-sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /auth-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /auth-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/android-auth/HEAD/auth-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: android-auth 5 | spec: 6 | type: library 7 | owner: opx-partner-activation 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /config/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /auth-sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Spotify Android Auth 3 | You need to request an access token first 4 | Code: %1$s 5 | Access Token: %1$s 6 | Get User Profile 7 | Request Code 8 | Request Token 9 | 10 | -------------------------------------------------------------------------------- /config/findbugs-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /auth-lib/src/test/java/com/spotify/sdk/android/auth/app/FakeSha1HashUtil.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth.app; 2 | 3 | import android.util.Pair; 4 | 5 | import java.util.Map; 6 | 7 | public class FakeSha1HashUtil implements Sha1HashUtil { 8 | 9 | private final Map mSignatureHashMap; 10 | 11 | public FakeSha1HashUtil(Map signatureHashMap) { 12 | mSignatureHashMap = signatureHashMap; 13 | } 14 | 15 | @Override 16 | public String sha1Hash(String signature) { 17 | return mSignatureHashMap.get(signature); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | # 5 | # Install IntelliJ plugin: https://github.com/editorconfig/editorconfig-jetbrains#readme 6 | 7 | root = true 8 | 9 | [*] 10 | 11 | indent_style = space 12 | indent_size = 4 13 | continuation_indent_size = 8 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.xml] 20 | continuation_indent_size = 4 21 | 22 | [*.{cpp,h,hpp}] 23 | indent_style = space 24 | indent_size = 2 25 | continuation_indent_size = 4 26 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/AccountsQueryParameters.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth; 2 | 3 | public interface AccountsQueryParameters { 4 | String CLIENT_ID = "client_id"; 5 | String RESPONSE_TYPE = "response_type"; 6 | String REDIRECT_URI = "redirect_uri"; 7 | String STATE = "state"; 8 | String SCOPE = "scope"; 9 | String SHOW_DIALOG = "show_dialog"; 10 | String UTM_SOURCE = "utm_source"; 11 | String UTM_MEDIUM = "utm_medium"; 12 | String UTM_CAMPAIGN = "utm_campaign"; 13 | String ERROR = "error"; 14 | String CODE = "code"; 15 | String ACCESS_TOKEN = "access_token"; 16 | String EXPIRES_IN = "expires_in"; 17 | String CODE_CHALLENGE = "code_challenge"; 18 | String CODE_CHALLENGE_METHOD = "code_challenge_method"; 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | signingEnabled=false 16 | 17 | # When configured, Gradle will run in incubating parallel mode. 18 | # This option should only be used with decoupled projects. More details, visit 19 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 20 | # org.gradle.parallel=true 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | android: 7 | components: 8 | - tools 9 | - tools 10 | - platform-tools 11 | # Note that the tools section appears twice on purpose as it’s required 12 | # to get the newest Android SDK tools. Source: Travis CI docs. 13 | - build-tools-30.0.2 14 | - extra-android-m2repository 15 | 16 | before_install: 17 | - echo yes | sdkmanager "platforms;android-30" 18 | - echo yes | sdkmanager "build-tools;30.0.2" 19 | - mkdir "$ANDROID_HOME/licenses" || true 20 | - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 21 | - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" 22 | 23 | script: 24 | - ./gradlew clean build 25 | 26 | before_cache: 27 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 28 | cache: 29 | directories: 30 | - $HOME/.gradle/caches/ 31 | - $HOME/.gradle/wrapper/ 32 | -------------------------------------------------------------------------------- /auth-lib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | Loading… 24 | 25 | -------------------------------------------------------------------------------- /auth-lib/src/auth/java/com/spotify/sdk/android/auth/browser/RedirectUriReceiverActivity.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth.browser; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import com.spotify.sdk.android.auth.LoginActivity; 10 | 11 | /** 12 | * Activity that receives the auth response sent by the browser's Custom Tab via deeplink. 13 | * The sole purpose of this activity is to forward the response back to {@link LoginActivity}. 14 | * This activity is used only during browser based auth flow - when the Spotify app is not 15 | * installed on the device. 16 | */ 17 | public class RedirectUriReceiverActivity extends Activity { 18 | 19 | @Override 20 | protected void onCreate(@Nullable Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | Intent intent = new Intent(this, LoginActivity.class); 23 | intent.setData(getIntent().getData()); 24 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); 25 | startActivity(intent); 26 | finish(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /auth-sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | #3F51B5 24 | #303F9F 25 | #FF4081 26 | 27 | -------------------------------------------------------------------------------- /auth-lib/src/main/res/layout/com_spotify_sdk_login_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /auth-lib/src/main/res/layout/com_spotify_sdk_login_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 27 | -------------------------------------------------------------------------------- /auth-sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 21 | 22 | 27 | 28 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/IntentExtras.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth; 2 | 3 | /* 4 | * Constants below have their counterparts in Spotify app where 5 | * they're used to parse messages received from SDK. 6 | * If any of these values needs to be changed, a new protocol version needs to be created on both 7 | * sides (auth-lib and Spotify app) and bumped accordingly. 8 | */ 9 | public interface IntentExtras { 10 | String KEY_CLIENT_ID = "CLIENT_ID"; 11 | String KEY_REQUESTED_SCOPES = "SCOPES"; 12 | String KEY_STATE = "STATE"; 13 | String KEY_UTM_SOURCE = "UTM_SOURCE"; 14 | String KEY_UTM_MEDIUM = "UTM_MEDIUM"; 15 | String KEY_UTM_CAMPAIGN = "UTM_CAMPAIGN"; 16 | String KEY_REDIRECT_URI = "REDIRECT_URI"; 17 | String KEY_RESPONSE_TYPE = "RESPONSE_TYPE"; 18 | String KEY_ACCESS_TOKEN = "ACCESS_TOKEN"; 19 | String KEY_AUTHORIZATION_CODE = "AUTHORIZATION_CODE"; 20 | String KEY_EXPIRES_IN = "EXPIRES_IN"; 21 | String KEY_CODE_CHALLENGE = "CODE_CHALLENGE"; 22 | String KEY_CODE_CHALLENGE_METHOD = "CODE_CHALLENGE_METHOD"; 23 | /* 24 | * This is used to pass information about the protocol version 25 | * to the AuthorizationActivity. 26 | * DO NOT CHANGE THIS. 27 | */ 28 | String KEY_VERSION = "VERSION"; 29 | } 30 | -------------------------------------------------------------------------------- /auth-lib/src/auth/java/com/spotify/sdk/android/auth/FallbackHandlerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import androidx.annotation.NonNull; 25 | import com.spotify.sdk.android.auth.browser.BrowserAuthHandler; 26 | 27 | /** 28 | * Provides an AuthorizationHandler that opens a browser when the Spotify application is not installed 29 | */ 30 | public class FallbackHandlerProvider { 31 | 32 | @NonNull 33 | public AuthorizationHandler provideFallback() { 34 | return new BrowserAuthHandler(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /auth-lib/src/store/java/com/spotify/sdk/android/auth/FallbackHandlerProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import androidx.annotation.NonNull; 25 | import com.spotify.sdk.android.auth.store.PlayStoreHandler; 26 | 27 | /** 28 | * Provides an AuthorizationHandler that opens the play store when the Spotify application is not installed 29 | */ 30 | public class FallbackHandlerProvider { 31 | 32 | @NonNull 33 | public AuthorizationHandler provideFallback() { 34 | return new PlayStoreHandler(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/app/Sha1HashUtil.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth.app; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | import java.security.MessageDigest; 8 | import java.security.NoSuchAlgorithmException; 9 | 10 | 11 | interface Sha1HashUtil { 12 | @Nullable 13 | String sha1Hash(@NonNull String toHash); 14 | } 15 | final class Sha1HashUtilImpl implements Sha1HashUtil { 16 | 17 | @Override 18 | @Nullable 19 | public String sha1Hash(@NonNull String toHash) { 20 | String hash = null; 21 | try { 22 | MessageDigest digest = MessageDigest.getInstance("SHA-1"); 23 | byte[] bytes = toHash.getBytes("UTF-8"); 24 | digest.update(bytes, 0, bytes.length); 25 | bytes = digest.digest(); 26 | 27 | hash = bytesToHex(bytes); 28 | } catch (NoSuchAlgorithmException ignored) { 29 | } catch (UnsupportedEncodingException ignored) { 30 | } 31 | return hash; 32 | } 33 | 34 | private final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); 35 | 36 | @NonNull 37 | private String bytesToHex(@NonNull byte[] bytes) { 38 | char[] hexChars = new char[bytes.length * 2]; 39 | for (int j = 0; j < bytes.length; j++) { 40 | int v = bytes[j] & 0xFF; 41 | hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 42 | hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 43 | } 44 | return new String(hexChars); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/AuthorizationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import android.app.Activity; 25 | 26 | import androidx.annotation.NonNull; 27 | import androidx.annotation.Nullable; 28 | 29 | public interface AuthorizationHandler { 30 | 31 | interface OnCompleteListener { 32 | void onComplete(@NonNull AuthorizationResponse response); 33 | 34 | void onCancel(); 35 | 36 | void onError(@NonNull Throwable error); 37 | 38 | } 39 | 40 | boolean start(@NonNull Activity contextActivity, @NonNull AuthorizationRequest request); 41 | 42 | void stop(); 43 | 44 | void setOnCompleteListener(@Nullable OnCompleteListener listener); 45 | 46 | boolean isAuthInProgress(); 47 | } 48 | -------------------------------------------------------------------------------- /auth-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /auth-lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /auth-lib/src/auth/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /auth-lib/src/test/java/com/spotify/sdk/android/auth/LoginActivityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import android.app.Activity; 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.robolectric.Robolectric; 31 | import org.robolectric.RobolectricTestRunner; 32 | import org.robolectric.android.controller.ActivityController; 33 | import org.robolectric.shadows.ShadowActivity; 34 | 35 | import static org.junit.Assert.assertEquals; 36 | import static org.junit.Assert.assertFalse; 37 | import static org.junit.Assert.assertTrue; 38 | import static org.robolectric.Robolectric.buildActivity; 39 | import static org.robolectric.Shadows.shadowOf; 40 | 41 | @RunWith(RobolectricTestRunner.class) 42 | public class LoginActivityTest { 43 | 44 | @Test 45 | public void shouldFinishLoginActivityIfNoAuthRequest() { 46 | Activity context = buildActivity(Activity.class).create().get(); 47 | Intent intent = new Intent(context, LoginActivity.class); 48 | 49 | Activity activity = buildActivity(LoginActivity.class, intent).create().get(); 50 | 51 | assertTrue(activity.isFinishing()); 52 | assertEquals(Activity.RESULT_CANCELED, shadowOf(activity).getResultCode()); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/app/SpotifyAuthHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth.app; 23 | 24 | import android.app.Activity; 25 | 26 | import androidx.annotation.Nullable; 27 | 28 | import com.spotify.sdk.android.auth.AuthorizationHandler; 29 | import com.spotify.sdk.android.auth.AuthorizationRequest; 30 | 31 | public class SpotifyAuthHandler implements AuthorizationHandler { 32 | 33 | private SpotifyNativeAuthUtil mSpotifyNativeAuthUtil; 34 | 35 | @Override 36 | public boolean start(Activity contextActivity, AuthorizationRequest request) { 37 | mSpotifyNativeAuthUtil = new SpotifyNativeAuthUtil( 38 | contextActivity, 39 | request, 40 | new Sha1HashUtilImpl() 41 | ); 42 | return mSpotifyNativeAuthUtil.startAuthActivity(); 43 | } 44 | 45 | @Override 46 | public void stop() { 47 | if (mSpotifyNativeAuthUtil != null) { 48 | mSpotifyNativeAuthUtil.stopAuthActivity(); 49 | } 50 | } 51 | 52 | @Override 53 | public void setOnCompleteListener(@Nullable OnCompleteListener listener) { 54 | // no-op 55 | } 56 | 57 | @Override 58 | public boolean isAuthInProgress() { 59 | // not supported, always return false 60 | return false; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /auth-sample/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | apply plugin: 'com.android.application' 23 | 24 | android { 25 | compileSdk 33 26 | buildToolsVersion = "33.0.0" 27 | 28 | signingConfigs { 29 | debug { 30 | storeFile file('keystore/example.keystore') 31 | storePassword 'example' 32 | keyAlias 'example_alias' 33 | keyPassword 'example' 34 | } 35 | } 36 | 37 | defaultConfig { 38 | applicationId "com.spotify.sdk.android.authentication.sample" 39 | minSdkVersion 16 40 | targetSdkVersion 33 41 | versionCode 1 42 | versionName "1.0" 43 | 44 | manifestPlaceholders = [ 45 | redirectSchemeName: "spotify-sdk", 46 | redirectHostName: "auth", 47 | redirectPathPattern: ".*" 48 | ] 49 | 50 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 51 | } 52 | buildTypes { 53 | debug { 54 | debuggable true 55 | signingConfig signingConfigs.debug 56 | } 57 | release { 58 | minifyEnabled false 59 | proguardFiles getDefaultProguardFile('proguard-android.txt') 60 | } 61 | } 62 | 63 | lintOptions { 64 | lintConfig file("${project.rootDir}/config/lint.xml") 65 | quiet false 66 | warningsAsErrors false 67 | textReport true 68 | textOutput 'stdout' 69 | xmlReport false 70 | } 71 | namespace 'com.spotify.sdk.android.authentication.sample' 72 | } 73 | 74 | dependencies { 75 | implementation files('../auth-lib/build/outputs/aar/auth-release.aar') 76 | // implementation 'com.spotify.android:auth:2.1.1' 77 | 78 | implementation 'androidx.browser:browser:1.4.0' 79 | implementation 'androidx.appcompat:appcompat:1.1.0' 80 | implementation 'com.google.android.material:material:1.0.0' 81 | implementation 'com.squareup.okhttp3:okhttp:4.9.3' 82 | } 83 | -------------------------------------------------------------------------------- /auth-lib/src/main/java/com/spotify/sdk/android/auth/PKCEInformationFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import android.util.Base64; 25 | 26 | import androidx.annotation.NonNull; 27 | 28 | import java.security.MessageDigest; 29 | import java.security.NoSuchAlgorithmException; 30 | import java.security.SecureRandom; 31 | 32 | public class PKCEInformationFactory { 33 | 34 | private static final int CODE_VERIFIER_LENGTH = 128; 35 | private static final String CODE_VERIFIER_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"; 36 | 37 | @NonNull 38 | public static PKCEInformation create() throws NoSuchAlgorithmException { 39 | String codeVerifier = generateCodeVerifier(); 40 | String codeChallenge = generateCodeChallenge(codeVerifier); 41 | return PKCEInformation.sha256(codeVerifier, codeChallenge); 42 | } 43 | 44 | @NonNull 45 | private static String generateCodeVerifier() { 46 | SecureRandom secureRandom = new SecureRandom(); 47 | StringBuilder codeVerifier = new StringBuilder(); 48 | 49 | for (int i = 0; i < CODE_VERIFIER_LENGTH; i++) { 50 | int randomIndex = secureRandom.nextInt(CODE_VERIFIER_CHARSET.length()); 51 | codeVerifier.append(CODE_VERIFIER_CHARSET.charAt(randomIndex)); 52 | } 53 | 54 | return codeVerifier.toString(); 55 | } 56 | 57 | @NonNull 58 | private static String generateCodeChallenge(@NonNull String codeVerifier) throws NoSuchAlgorithmException { 59 | try { 60 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 61 | byte[] hash = digest.digest(codeVerifier.getBytes("US-ASCII")); 62 | return Base64.encodeToString(hash, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); 63 | } catch (final java.io.UnsupportedEncodingException e) { 64 | // US-ASCII is guaranteed to be supported on all platforms 65 | throw new RuntimeException("US-ASCII encoding not supported", e); 66 | } 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /auth-lib/src/test/java/com/spotify/sdk/android/auth/PKCEInformationFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth; 23 | 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.robolectric.RobolectricTestRunner; 27 | 28 | import java.security.NoSuchAlgorithmException; 29 | 30 | import static org.junit.Assert.assertEquals; 31 | import static org.junit.Assert.assertNotNull; 32 | import static org.junit.Assert.assertTrue; 33 | 34 | @RunWith(RobolectricTestRunner.class) 35 | public class PKCEInformationFactoryTest { 36 | 37 | @Test 38 | public void shouldCreatePKCEInformation() throws NoSuchAlgorithmException { 39 | PKCEInformation pkceInfo = PKCEInformationFactory.create(); 40 | 41 | assertNotNull(pkceInfo); 42 | assertNotNull(pkceInfo.getVerifier()); 43 | assertNotNull(pkceInfo.getChallenge()); 44 | assertEquals("S256", pkceInfo.getCodeChallengeMethod()); 45 | } 46 | 47 | @Test 48 | public void shouldGenerateCodeVerifierWithCorrectLength() throws NoSuchAlgorithmException { 49 | PKCEInformation pkceInfo = PKCEInformationFactory.create(); 50 | 51 | assertEquals(128, pkceInfo.getVerifier().length()); 52 | } 53 | 54 | @Test 55 | public void shouldGenerateUniqueCodeVerifiers() throws NoSuchAlgorithmException { 56 | PKCEInformation pkceInfo1 = PKCEInformationFactory.create(); 57 | PKCEInformation pkceInfo2 = PKCEInformationFactory.create(); 58 | 59 | assertTrue(!pkceInfo1.getVerifier().equals(pkceInfo2.getVerifier())); 60 | assertTrue(!pkceInfo1.getChallenge().equals(pkceInfo2.getChallenge())); 61 | } 62 | 63 | @Test 64 | public void shouldGenerateValidBase64UrlEncodedChallenge() throws NoSuchAlgorithmException { 65 | PKCEInformation pkceInfo = PKCEInformationFactory.create(); 66 | 67 | String challenge = pkceInfo.getChallenge(); 68 | assertTrue(challenge.matches("^[A-Za-z0-9_-]+$")); 69 | assertTrue(!challenge.contains("=")); 70 | assertTrue(!challenge.contains("+")); 71 | assertTrue(!challenge.contains("/")); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /auth-lib/src/testStore/java/com/spotify/sdk/android/auth/LoginActivityStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.sdk.android.auth;/* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | import static org.junit.Assert.assertEquals; 23 | import static org.junit.Assert.assertNotNull; 24 | import static org.junit.Assert.assertTrue; 25 | import static org.robolectric.Robolectric.buildActivity; 26 | import static org.robolectric.Shadows.shadowOf; 27 | 28 | import android.app.Activity; 29 | import android.content.Intent; 30 | import android.os.Bundle; 31 | 32 | import org.junit.Test; 33 | import org.junit.runner.RunWith; 34 | import org.robolectric.Robolectric; 35 | import org.robolectric.RobolectricTestRunner; 36 | import org.robolectric.android.controller.ActivityController; 37 | import org.robolectric.shadows.ShadowActivity; 38 | 39 | @RunWith(RobolectricTestRunner.class) 40 | public class LoginActivityStoreTest { 41 | 42 | @Test 43 | public void shouldFinishLoginActivityWithErrorIfPlayStoreIsNotInstalled() { 44 | Activity context = Robolectric 45 | .buildActivity(Activity.class) 46 | .create() 47 | .get(); 48 | 49 | AuthorizationRequest request = new AuthorizationRequest.Builder("test", AuthorizationResponse.Type.TOKEN, "test://test").build(); 50 | 51 | Bundle bundle = new Bundle(); 52 | bundle.putParcelable(LoginActivity.REQUEST_KEY, request); 53 | 54 | Intent intent = new Intent(context, LoginActivity.class); 55 | intent.putExtra(LoginActivity.EXTRA_AUTH_REQUEST, bundle); 56 | 57 | ActivityController loginActivityActivityController = buildActivity(LoginActivity.class, intent); 58 | 59 | final LoginActivity loginActivity = loginActivityActivityController.get(); 60 | 61 | final ShadowActivity shadowLoginActivity = shadowOf(loginActivity); 62 | shadowLoginActivity.setCallingActivity(context.getComponentName()); 63 | 64 | loginActivityActivityController.create(); 65 | 66 | assertTrue(loginActivity.isFinishing()); 67 | assertEquals(Activity.RESULT_OK, shadowLoginActivity.getResultCode()); 68 | AuthorizationResponse response = LoginActivity.getResponseFromIntent(shadowLoginActivity.getResultIntent()); 69 | assertNotNull(response); 70 | assertEquals(AuthorizationResponse.Type.ERROR, response.getType()); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## Version 3.0.0 5 | - Add mandatory redirect path pattern. 6 | 7 | You can now define the path pattern in your app's build.gradle file as follows: 8 | ```gradle 9 | defaultConfig { 10 | manifestPlaceholders = [ 11 | redirectSchemeName: "your-redirect-scheme", 12 | redirectHostName: "your-redirect-host", 13 | redirectPathPattern: "your/redirect/path/pattern" // New mandatory field 14 | ] 15 | ... 16 | } 17 | ``` 18 | 19 | If you want to retain the previous behavior (accepting any path), use .* as the path pattern. 20 | For more details, see the [Google documentation](https://developer.android.com/guide/topics/manifest/data-element#path). 21 | 22 | ## Version 2.2.0 23 | - Requesting token now uses PKCE when: 24 | - There is an installed Spotify app that supports PKCE. 25 | - When web fallback is used. 26 | - Token requests may return a refresh token if PKCE was used. 27 | 28 | ## Version 2.1.2 29 | * Propagate tracking parameters when opening the native login flow 30 | 31 | ## Version 2.1.1 32 | * Introduced RedirectUriReceiverActivity for web based auth flow 33 | * Restructured web based flow, removed LoginDialog class 34 | * Made AuthorizationClient final 35 | * Bumped to Gradle 7.5, AGP 7.4.2 36 | * Bumped targetSdkVersion to 33 37 | * Bumped androidx.browser to 1.5.0 and test dependencies 38 | 39 | ## Version 2.1.0 40 | * Introduce a new flavour store to generate a library called auth-store that default to the play store instead of CustomTabsIntent 41 | * Expose method to check if the Spotify application is installed 42 | 43 | ## Version 2.0.2 44 | * Fixed StackOverflowError at com.spotify.sdk.android.auth.browser.LoginDialog.onServiceDisconnected 45 | * Fixed NullPointerException when creating CustomTabsIntent in LoginDialog 46 | * Set LoginActivity launch mode to singleTask to avoid launching CustomTabs in a separate task 47 | 48 | ## Version 2.0.1 49 | * Removed unused code related to WebView 50 | 51 | ## Version 2.0.0 52 | * Replaced WebView usage with Custom Tabs since Google and Facebook Login no longer support WebViews for authenticating users. 53 | * Removed AuthorizationClient#clearCookies method from the API. Custom Tabs use the cookies from the browser. 54 | * Bumped targetSdkVersion to 31 55 | 56 | ## Version 1.2.6 57 | * Fixes an issue related to package visibility changes in API 30 58 | 59 | ## Version 1.2.5 60 | * Updated targetSdkVersion (API 30), buildTools and Gradle plugin 61 | * Removed unused jcenter repository 62 | * Conform to package visibility restrictions when targeting Android 11 (API 30) 63 | 64 | ## Version 1.2.3 65 | * Fixed a few issues with the webview based redirect 66 | 67 | ## Version 1.2.2 68 | * Remove custom-tabs handling due to issues 69 | 70 | ## Version 1.2.1 71 | * Fixes an issue that produced a redirect error when the redirect uri contains CAPS. 72 | 73 | ## Version 1.2.0 74 | 75 | * Breaking changes: Rename classes from AuthenticationClassName to AuthorizationClassName 76 | * Pass state parameter in AuthorizationResponse 77 | 78 | 2019-08-12 79 | 80 | * Add method to clear Spotify and Facebook cookies to AuthenticationClient 81 | * Upgrade buildToolsVersion to 27.0.3 82 | * Replace deprecated compile keyword in gradle files 83 | 84 | ## Version 1.1.0 85 | 86 | _2018-02-28_ 87 | 88 | * Upgrade target and compile SDKs to 27 89 | * Upgrade android support libraries to 27.0.2 90 | -------------------------------------------------------------------------------- /auth-lib/src/store/java/com/spotify/sdk/android/auth/store/PlayStoreHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2016 Spotify AB 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.sdk.android.auth.store; 23 | 24 | import android.app.Activity; 25 | import android.content.ComponentName; 26 | import android.content.Intent; 27 | import android.net.Uri; 28 | import android.util.Log; 29 | 30 | import androidx.annotation.Nullable; 31 | 32 | import com.spotify.sdk.android.auth.AuthorizationHandler; 33 | import com.spotify.sdk.android.auth.AuthorizationRequest; 34 | 35 | /** 36 | * An AuthorizationHandler that opens the play store to download the main Spotify application 37 | */ 38 | public class PlayStoreHandler implements AuthorizationHandler { 39 | 40 | private static final String TAG = PlayStoreHandler.class.getSimpleName(); 41 | private static final String APP_PACKAGE_NAME = "com.spotify.music"; 42 | 43 | @Nullable 44 | private OnCompleteListener mListener; 45 | 46 | @Override 47 | public boolean start(Activity contextActivity, AuthorizationRequest request) { 48 | Log.d(TAG, "start"); 49 | Intent intent = new Intent(Intent.ACTION_VIEW); 50 | intent.setData(Uri.parse( 51 | "https://play.google.com/store/apps/details?id=" + APP_PACKAGE_NAME 52 | )); 53 | intent.setPackage("com.android.vending"); 54 | 55 | ComponentName componentName = intent.resolveActivity(contextActivity.getPackageManager()); 56 | 57 | OnCompleteListener listener = mListener; 58 | if (componentName == null) { 59 | if (listener != null) { 60 | listener.onError( 61 | new ClassNotFoundException("Couldn't find an activity to handle a play store link") 62 | ); 63 | } 64 | return false; 65 | } 66 | 67 | contextActivity.startActivity(intent); 68 | 69 | if (listener != null) { 70 | listener.onCancel(); 71 | } 72 | return true; 73 | } 74 | 75 | @Override 76 | public void stop() { 77 | Log.d(TAG, "stop"); 78 | } 79 | 80 | /** 81 | * {@link OnCompleteListener#onError(Throwable)} will be called if no play store application is installed 82 | * {@link OnCompleteListener#onCancel()} will be called if the play store is launched 83 | */ 84 | @Override 85 | public void setOnCompleteListener(@Nullable OnCompleteListener listener) { 86 | mListener = listener; 87 | } 88 | 89 | @Override 90 | public boolean isAuthInProgress() { 91 | // not supported, always return false 92 | return false; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /auth-sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 21 | 27 | 28 | 32 | 33 | 39 | 40 |