Skip to content

Commit a08e0b7

Browse files
committed
Merge pull request #100 from kaaes/demo_app
Add sample app that uses search
2 parents 78370f1 + 187a418 commit a08e0b7

31 files changed

+1008
-4
lines changed

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
language: android
22
android:
33
components:
4-
- build-tools-22.0.1
4+
- tools
5+
- build-tools-23.0.2
6+
- android-23
7+
- extra-android-m2repository
58

69
env:
710
global:

sample-search/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

sample-search/build.gradle

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
apply plugin: 'com.android.application'
2+
3+
android {
4+
compileSdkVersion 23
5+
buildToolsVersion "23.0.2"
6+
7+
defaultConfig {
8+
applicationId "kaaes.spotify.webapi.samplesearch"
9+
minSdkVersion 15
10+
targetSdkVersion 23
11+
versionCode 1
12+
versionName "1.0"
13+
}
14+
15+
lintOptions {
16+
disable 'InvalidPackage' // for okio-1.2.0
17+
}
18+
19+
buildTypes {
20+
release {
21+
minifyEnabled false
22+
}
23+
}
24+
}
25+
26+
repositories {
27+
mavenCentral()
28+
flatDir {
29+
dirs 'libs'
30+
}
31+
}
32+
33+
dependencies {
34+
compile fileTree(include: ['*.jar'], dir: 'libs')
35+
compile 'com.android.support:appcompat-v7:23.1.1'
36+
compile 'com.android.support:recyclerview-v7:23.1.1'
37+
compile 'com.squareup.picasso:picasso:2.5.2'
38+
compile 'com.google.guava:guava:18.0'
39+
compile project(':spotify-api')
40+
compile(name: 'spotify-auth-1.0.0-beta12', ext: 'aar')
41+
}
32.5 KB
Binary file not shown.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest
3+
package="kaaes.spotify.webapi.samplesearch"
4+
xmlns:android="http://schemas.android.com/apk/res/android">
5+
6+
<uses-permission android:name="android.permission.INTERNET"/>
7+
8+
<application
9+
android:allowBackup="true"
10+
android:icon="@mipmap/ic_launcher"
11+
android:label="@string/app_name"
12+
android:supportsRtl="true"
13+
android:theme="@style/AppTheme">
14+
15+
<activity android:name=".MainActivity">
16+
17+
</activity>
18+
19+
<activity android:name=".LoginActivity">
20+
<intent-filter>
21+
<action android:name="android.intent.action.MAIN"/>
22+
<category android:name="android.intent.category.LAUNCHER"/>
23+
</intent-filter>
24+
</activity>
25+
26+
<activity
27+
android:name="com.spotify.sdk.android.authentication.LoginActivity"
28+
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
29+
30+
<service android:name=".PlayerService"/>
31+
32+
</application>
33+
34+
</manifest>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package kaaes.spotify.webapi.samplesearch;
2+
3+
import android.content.Context;
4+
import android.content.SharedPreferences;
5+
6+
import java.util.concurrent.TimeUnit;
7+
8+
public class CredentialsHandler {
9+
10+
private static final String ACCESS_TOKEN_NAME = "webapi.credentials.access_token";
11+
private static final String ACCESS_TOKEN = "access_token";
12+
private static final String EXPIRES_AT = "expires_at";
13+
14+
public static void setToken(Context context, String token, long expiresIn, TimeUnit unit) {
15+
Context appContext = context.getApplicationContext();
16+
17+
long now = System.currentTimeMillis();
18+
long expiresAt = now + unit.toMillis(expiresIn);
19+
20+
SharedPreferences sharedPref = getSharedPreferences(appContext);
21+
SharedPreferences.Editor editor = sharedPref.edit();
22+
editor.putString(ACCESS_TOKEN, token);
23+
editor.putLong(EXPIRES_AT, expiresAt);
24+
editor.apply();
25+
}
26+
27+
private static SharedPreferences getSharedPreferences(Context appContext) {
28+
return appContext.getSharedPreferences(ACCESS_TOKEN_NAME, Context.MODE_PRIVATE);
29+
}
30+
31+
public static String getToken(Context context) {
32+
Context appContext = context.getApplicationContext();
33+
SharedPreferences sharedPref = getSharedPreferences(appContext);
34+
35+
String token = sharedPref.getString(ACCESS_TOKEN, null);
36+
long expiresAt = sharedPref.getLong(EXPIRES_AT, 0L);
37+
38+
if (token == null || expiresAt < System.currentTimeMillis()) {
39+
return null;
40+
}
41+
42+
return token;
43+
}
44+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package kaaes.spotify.webapi.samplesearch;
2+
3+
import android.app.Activity;
4+
import android.content.Intent;
5+
import android.os.Bundle;
6+
import android.util.Log;
7+
import android.view.View;
8+
import android.widget.Toast;
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+
import java.util.concurrent.TimeUnit;
15+
16+
public class LoginActivity extends Activity {
17+
18+
private static final String TAG = LoginActivity.class.getSimpleName();
19+
20+
@SuppressWarnings("SpellCheckingInspection")
21+
private static final String CLIENT_ID = "8a91678afa49446c9aff1beaabe9c807";
22+
@SuppressWarnings("SpellCheckingInspection")
23+
private static final String REDIRECT_URI = "testschema://callback";
24+
25+
private static final int REQUEST_CODE = 1337;
26+
27+
@Override
28+
protected void onCreate(Bundle savedInstanceState) {
29+
super.onCreate(savedInstanceState);
30+
31+
String token = CredentialsHandler.getToken(this);
32+
if (token == null) {
33+
setContentView(R.layout.activity_login);
34+
} else {
35+
startMainActivity(token);
36+
}
37+
}
38+
39+
public void onLoginButtonClicked(View view) {
40+
final AuthenticationRequest request = new AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI)
41+
.setScopes(new String[]{"playlist-read"})
42+
.build();
43+
44+
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request);
45+
}
46+
47+
@Override
48+
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
49+
super.onActivityResult(requestCode, resultCode, intent);
50+
51+
// Check if result comes from the correct activity
52+
if (requestCode == REQUEST_CODE) {
53+
AuthenticationResponse response = AuthenticationClient.getResponse(resultCode, intent);
54+
switch (response.getType()) {
55+
// Response was successful and contains auth token
56+
case TOKEN:
57+
logMessage("Got token: " + response.getAccessToken());
58+
CredentialsHandler.setToken(this, response.getAccessToken(), response.getExpiresIn(), TimeUnit.SECONDS);
59+
startMainActivity(response.getAccessToken());
60+
break;
61+
62+
// Auth flow returned an error
63+
case ERROR:
64+
logError("Auth error: " + response.getError());
65+
break;
66+
67+
// Most likely auth flow was cancelled
68+
default:
69+
logError("Auth result: " + response.getType());
70+
}
71+
}
72+
}
73+
74+
private void startMainActivity(String token) {
75+
Intent intent = MainActivity.createIntent(this);
76+
intent.putExtra(MainActivity.EXTRA_TOKEN, token);
77+
startActivity(intent);
78+
finish();
79+
}
80+
81+
private void logError(String msg) {
82+
Toast.makeText(this, "Error: " + msg, Toast.LENGTH_SHORT).show();
83+
Log.e(TAG, msg);
84+
}
85+
86+
private void logMessage(String msg) {
87+
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
88+
Log.d(TAG, msg);
89+
}
90+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package kaaes.spotify.webapi.samplesearch;
2+
3+
import android.content.Context;
4+
import android.content.Intent;
5+
import android.os.Bundle;
6+
import android.support.v7.app.AppCompatActivity;
7+
import android.support.v7.widget.LinearLayoutManager;
8+
import android.support.v7.widget.RecyclerView;
9+
import android.view.View;
10+
import android.widget.SearchView;
11+
12+
import java.util.List;
13+
14+
import kaaes.spotify.webapi.android.models.Track;
15+
16+
public class MainActivity extends AppCompatActivity implements Search.View {
17+
18+
static final String EXTRA_TOKEN = "EXTRA_TOKEN";
19+
private static final String KEY_CURRENT_QUERY = "CURRENT_QUERY";
20+
21+
private Search.ActionListener mActionListener;
22+
23+
private LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
24+
private ScrollListener mScrollListener = new ScrollListener(mLayoutManager);
25+
private SearchResultsAdapter mAdapter;
26+
27+
28+
private class ScrollListener extends ResultListScrollListener {
29+
30+
public ScrollListener(LinearLayoutManager layoutManager) {
31+
super(layoutManager);
32+
}
33+
34+
@Override
35+
public void onLoadMore() {
36+
mActionListener.loadMoreResults();
37+
}
38+
}
39+
40+
public static Intent createIntent(Context context) {
41+
return new Intent(context, MainActivity.class);
42+
}
43+
44+
@Override
45+
protected void onCreate(Bundle savedInstanceState) {
46+
super.onCreate(savedInstanceState);
47+
setContentView(R.layout.activity_main);
48+
49+
Intent intent = getIntent();
50+
String token = intent.getStringExtra(EXTRA_TOKEN);
51+
52+
mActionListener = new SearchPresenter(this, this);
53+
mActionListener.init(token);
54+
55+
// Setup search field
56+
final SearchView searchView = (SearchView) findViewById(R.id.search_view);
57+
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
58+
@Override
59+
public boolean onQueryTextSubmit(String query) {
60+
mActionListener.search(query);
61+
searchView.clearFocus();
62+
return true;
63+
}
64+
65+
@Override
66+
public boolean onQueryTextChange(String newText) {
67+
return false;
68+
}
69+
});
70+
71+
72+
// Setup search results list
73+
mAdapter = new SearchResultsAdapter(this, new SearchResultsAdapter.ItemSelectedListener() {
74+
@Override
75+
public void onItemSelected(View itemView, Track item) {
76+
mActionListener.selectTrack(item);
77+
}
78+
});
79+
80+
RecyclerView resultsList = (RecyclerView) findViewById(R.id.search_results);
81+
resultsList.setHasFixedSize(true);
82+
resultsList.setLayoutManager(mLayoutManager);
83+
resultsList.setAdapter(mAdapter);
84+
resultsList.addOnScrollListener(mScrollListener);
85+
86+
// If Activity was recreated wit active search restore it
87+
if (savedInstanceState != null) {
88+
String currentQuery = savedInstanceState.getString(KEY_CURRENT_QUERY);
89+
mActionListener.search(currentQuery);
90+
}
91+
}
92+
93+
@Override
94+
public void reset() {
95+
mScrollListener.reset();
96+
mAdapter.clearData();
97+
}
98+
99+
@Override
100+
public void addData(List<Track> items) {
101+
mAdapter.addData(items);
102+
}
103+
104+
@Override
105+
protected void onPause() {
106+
super.onPause();
107+
mActionListener.pause();
108+
}
109+
110+
@Override
111+
protected void onResume() {
112+
super.onResume();
113+
mActionListener.resume();
114+
}
115+
116+
@Override
117+
protected void onSaveInstanceState(Bundle outState) {
118+
super.onSaveInstanceState(outState);
119+
if (mActionListener.getCurrentQuery() != null) {
120+
outState.putString(KEY_CURRENT_QUERY, mActionListener.getCurrentQuery());
121+
}
122+
}
123+
124+
@Override
125+
protected void onDestroy() {
126+
mActionListener.destroy();
127+
super.onDestroy();
128+
}
129+
130+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kaaes.spotify.webapi.samplesearch;
2+
3+
import android.support.annotation.Nullable;
4+
5+
public interface Player {
6+
7+
void play(String url);
8+
9+
void pause();
10+
11+
void resume();
12+
13+
boolean isPlaying();
14+
15+
@Nullable
16+
String getCurrentTrack();
17+
18+
void release();
19+
}

0 commit comments

Comments
 (0)