Merge lp:~karni/ubuntuone-android-music/playlist-ux-and-sync into lp:ubuntuone-android-music/v2

Proposed by Michał Karnicki
Status: Merged
Approved by: Michał Karnicki
Approved revision: 32
Merged at revision: 32
Proposed branch: lp:~karni/ubuntuone-android-music/playlist-ux-and-sync
Merge into: lp:ubuntuone-android-music/v2
Prerequisite: lp:~karni/ubuntuone-android-music/added-missing-dao
Diff against target: 1525 lines (+527/-267)
25 files modified
AndroidManifest.xml (+7/-0)
res/layout/activity_playlist.xml (+0/-76)
res/layout/activity_playlists.xml (+13/-0)
res/values/ids.xml (+1/-0)
res/values/strings.xml (+5/-0)
src/com/ubuntuone/android/music/adapter/U1PlaylistAdapter.java (+7/-2)
src/com/ubuntuone/android/music/model/Song.java (+1/-1)
src/com/ubuntuone/android/music/provider/MusicContract.java (+11/-3)
src/com/ubuntuone/android/music/provider/MusicDatabase.java (+1/-0)
src/com/ubuntuone/android/music/provider/MusicProvider.java (+6/-6)
src/com/ubuntuone/android/music/provider/MusicProviderUtils.java (+70/-3)
src/com/ubuntuone/android/music/service/MusicService.java (+16/-5)
src/com/ubuntuone/android/music/service/SyncService.java (+59/-10)
src/com/ubuntuone/android/music/ui/AlbumsFragment.java (+8/-1)
src/com/ubuntuone/android/music/ui/HomeActivity.java (+50/-4)
src/com/ubuntuone/android/music/ui/PlayerFragment.java (+1/-1)
src/com/ubuntuone/android/music/ui/PlaylistActivity.java (+0/-1)
src/com/ubuntuone/android/music/ui/PlaylistsActivity.java (+148/-6)
src/com/ubuntuone/android/music/ui/PlaylistsFragment.java (+33/-111)
src/com/ubuntuone/android/music/ui/SongsFragment.java (+58/-18)
src/com/ubuntuone/android/music/ui/dialog/InfoAlertDialog.java (+12/-6)
test/src/com/ubuntuone/android/music/provider/MusicProviderInsertTest.java (+2/-2)
test/src/com/ubuntuone/android/music/provider/MusicProviderQueryTest.java (+4/-4)
test/src/com/ubuntuone/android/music/provider/MusicProviderUtilsTest.java (+10/-7)
test/src/com/ubuntuone/android/music/ui/SongsPageTest.java (+4/-0)
To merge this branch: bzr merge lp:~karni/ubuntuone-android-music/playlist-ux-and-sync
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Review via email: mp+136731@code.launchpad.net

Commit message

Added playlist manipulation and sync.

Description of the change

This branch allows adding and removing items from playlists, as well as sync them with U1.
Create new playlist feature is pending minor rework after recent u1-server changes rolled out.

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'AndroidManifest.xml'
--- AndroidManifest.xml 2012-11-08 14:14:54 +0000
+++ AndroidManifest.xml 2012-11-28 17:49:20 +0000
@@ -63,6 +63,11 @@
63 android:theme="@style/Theme.Sherlock.Light" >63 android:theme="@style/Theme.Sherlock.Light" >
64 </activity>64 </activity>
65 <activity65 <activity
66 android:name=".ui.PlaylistsActivity"
67 android:label="U1 Playlists"
68 android:theme="@style/Theme.Sherlock.Light.Dialog" >
69 </activity>
70 <activity
66 android:name=".ui.PlayerActivity"71 android:name=".ui.PlayerActivity"
67 android:label="U1 Player"72 android:label="U1 Player"
68 android:screenOrientation="portrait" >73 android:screenOrientation="portrait" >
@@ -78,6 +83,7 @@
78 android:exported="false" >83 android:exported="false" >
79 <intent-filter>84 <intent-filter>
80 <action android:name="com.ubuntuone.android.music.ACTION_SYNC_MUSIC_METADATA" />85 <action android:name="com.ubuntuone.android.music.ACTION_SYNC_MUSIC_METADATA" />
86 <action android:name="com.ubuntuone.android.music.ACTION_SYNC_PLAYLISTS" />
81 </intent-filter>87 </intent-filter>
82 </service>88 </service>
83 <service89 <service
@@ -87,6 +93,7 @@
8793
88 <provider94 <provider
89 android:name=".provider.MusicProvider"95 android:name=".provider.MusicProvider"
96 android:exported="false"
90 android:authorities="com.ubuntuone.android.music"97 android:authorities="com.ubuntuone.android.music"
91 android:label="@string/app_name"98 android:label="@string/app_name"
92 android:syncable="true"99 android:syncable="true"
93100
=== removed file 'res/layout/activity_playlist.xml'
--- res/layout/activity_playlist.xml 2012-09-19 23:50:20 +0000
+++ res/layout/activity_playlist.xml 1970-01-01 00:00:00 +0000
@@ -1,76 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent" >
6
7 <ImageView
8 android:id="@+id/image"
9 android:layout_width="@dimen/header_image_size"
10 android:layout_height="@dimen/header_image_size"
11 android:layout_alignParentLeft="true"
12 android:layout_alignParentTop="true"
13 android:background="@drawable/ic_action_help"
14 android:minHeight="@dimen/header_image_size"
15 android:minWidth="@dimen/header_image_size" />
16
17 <TextView
18 android:id="@+id/text1"
19 android:layout_width="match_parent"
20 android:layout_height="wrap_content"
21 android:layout_alignTop="@id/image"
22 android:layout_toRightOf="@id/image"
23 android:gravity="top|left"
24 android:paddingLeft="4dp"
25 android:paddingTop="4dp"
26 android:singleLine="true"
27 android:textStyle="bold"
28 android:textAppearance="@android:style/TextAppearance.Medium"
29 android:textColor="@android:color/primary_text_light" />
30
31 <TextView
32 android:id="@+id/text2"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:layout_below="@id/text1"
36 android:layout_toRightOf="@id/image"
37 android:gravity="top|left"
38 android:paddingLeft="4dp"
39 android:singleLine="true"
40 android:textAppearance="@android:style/TextAppearance.Small"
41 android:textColor="@android:color/tertiary_text_light" />
42
43 <LinearLayout
44 android:layout_width="match_parent"
45 android:layout_height="wrap_content"
46 android:layout_alignBottom="@id/image"
47 android:layout_toRightOf="@id/image"
48 android:weightSum="2" >
49
50 <Button
51 android:id="@+id/shuffle_all"
52 android:layout_width="0dp"
53 android:layout_height="wrap_content"
54 android:layout_weight="1"
55 android:text="@string/shuffle_all"
56 android:textAppearance="@android:style/TextAppearance.Small"
57 android:onClick="shuffleAll" />
58
59 <Button
60 android:id="@+id/repeat_all"
61 android:layout_width="0dp"
62 android:layout_height="wrap_content"
63 android:layout_weight="1"
64 android:text="@string/repeat_all"
65 android:textAppearance="@android:style/TextAppearance.Small"
66 android:onClick="repeatAll" />
67 </LinearLayout>
68
69 <FrameLayout
70 android:id="@+id/songs"
71 android:layout_width="match_parent"
72 android:layout_height="match_parent"
73 android:layout_below="@id/image" >
74 </FrameLayout>
75
76</RelativeLayout>
77\ No newline at end of file0\ No newline at end of file
781
=== added file 'res/layout/activity_playlists.xml'
--- res/layout/activity_playlists.xml 1970-01-01 00:00:00 +0000
+++ res/layout/activity_playlists.xml 2012-11-28 17:49:20 +0000
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent" >
6
7 <fragment
8 android:id="@+id/playlists"
9 android:name="com.ubuntuone.android.music.ui.PlaylistsFragment"
10 android:layout_width="match_parent"
11 android:layout_height="match_parent" />
12
13</RelativeLayout>
014
=== modified file 'res/values/ids.xml'
--- res/values/ids.xml 2012-11-08 14:14:54 +0000
+++ res/values/ids.xml 2012-11-28 17:49:20 +0000
@@ -15,5 +15,6 @@
15 <item type="id" name="context_play" />15 <item type="id" name="context_play" />
16 <item type="id" name="context_star" />16 <item type="id" name="context_star" />
17 <item type="id" name="context_add_to_playlist" />17 <item type="id" name="context_add_to_playlist" />
18 <item type="id" name="context_remove_from_playlist" />
18 <item type="id" name="context_add_to_queue" />19 <item type="id" name="context_add_to_queue" />
19</resources>20</resources>
20\ No newline at end of file21\ No newline at end of file
2122
=== modified file 'res/values/strings.xml'
--- res/values/strings.xml 2012-11-08 14:14:54 +0000
+++ res/values/strings.xml 2012-11-28 17:49:20 +0000
@@ -31,7 +31,11 @@
31 <string name="playlist_stats">%1$s songs</string>31 <string name="playlist_stats">%1$s songs</string>
32 <string name="playlist_title">Playlist title</string>32 <string name="playlist_title">Playlist title</string>
33 <string name="creating_playlist">Creating playlist…</string>33 <string name="creating_playlist">Creating playlist…</string>
34 <string name="add_to">Add to…</string>
35 <string name="added_to_fmt">Added to %s.</string>
36 <string name="sync_pending">Sync pending</string>
34 <string name="creating_playlist_failed_title">Cound\'t create playlist</string>37 <string name="creating_playlist_failed_title">Cound\'t create playlist</string>
38 <string name="updating_playlist_failed_title">Cound\'t update playlist</string>
35 39
36 <string name="genres_title">genres</string>40 <string name="genres_title">genres</string>
37 <string name="genres_loading">Getting genres…</string>41 <string name="genres_loading">Getting genres…</string>
@@ -56,6 +60,7 @@
56 <string name="description_play">Play</string>60 <string name="description_play">Play</string>
57 <string name="description_star">Star</string>61 <string name="description_star">Star</string>
58 <string name="description_add_to_playlist">Add to playlist</string>62 <string name="description_add_to_playlist">Add to playlist</string>
63 <string name="description_remove_from_playlist">Remove from playlist</string>
59 <string name="description_add_to_queue">Add to queue</string>64 <string name="description_add_to_queue">Add to queue</string>
60 65
61 <string name="shuffle_all">Shuffle all</string>66 <string name="shuffle_all">Shuffle all</string>
6267
=== modified file 'src/com/ubuntuone/android/music/adapter/U1PlaylistAdapter.java'
--- src/com/ubuntuone/android/music/adapter/U1PlaylistAdapter.java 2012-11-22 10:23:21 +0000
+++ src/com/ubuntuone/android/music/adapter/U1PlaylistAdapter.java 2012-11-28 17:49:20 +0000
@@ -157,8 +157,13 @@
157 157
158 String name = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));158 String name = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));
159 int length = cursor.getInt(cursor.getColumnIndex(Playlists.PLAYLIST_SONG_COUNT));159 int length = cursor.getInt(cursor.getColumnIndex(Playlists.PLAYLIST_SONG_COUNT));
160160 int dirty = cursor.getInt(cursor.getColumnIndex(Playlists.DIRTY));
161 holder.playlistTitle.setText(name);161 if (dirty != 0) {
162 holder.playlistTitle.setText(String.format("%s [%s]",
163 name, context.getString(R.string.sync_pending)));
164 } else {
165 holder.playlistTitle.setText(name);
166 }
162 // TODO Get plural string resource for item(s).167 // TODO Get plural string resource for item(s).
163 if (length == 0) {168 if (length == 0) {
164 holder.playlistSongCount.setText(String.format("No songs", length));169 holder.playlistSongCount.setText(String.format("No songs", length));
165170
=== modified file 'src/com/ubuntuone/android/music/model/Song.java'
--- src/com/ubuntuone/android/music/model/Song.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/model/Song.java 2012-11-28 17:49:20 +0000
@@ -69,7 +69,7 @@
69 69
70 public Song(Context context, Uri uri) {70 public Song(Context context, Uri uri) {
71 ContentResolver resolver = context.getContentResolver();71 ContentResolver resolver = context.getContentResolver();
72 String[] projection = Songs.DEFAULT_PROJECTION;72 String[] projection = Songs.getDefaultProjection();
73 Cursor cursor = null;73 Cursor cursor = null;
74 try {74 try {
75 cursor = resolver.query(uri, projection, null, null, null);75 cursor = resolver.query(uri, projection, null, null, null);
7676
=== modified file 'src/com/ubuntuone/android/music/provider/MusicContract.java'
--- src/com/ubuntuone/android/music/provider/MusicContract.java 2012-11-21 15:03:12 +0000
+++ src/com/ubuntuone/android/music/provider/MusicContract.java 2012-11-28 17:49:20 +0000
@@ -153,6 +153,8 @@
153 String PLAYLIST_SONG_COUNT = "playlist_song_count";153 String PLAYLIST_SONG_COUNT = "playlist_song_count";
154 /** Full URL to this playlist online. */154 /** Full URL to this playlist online. */
155 String PLAYLIST_URL = "playlist_url";155 String PLAYLIST_URL = "playlist_url";
156 /** Flag indicating playlist should be sync'ed up. */
157 String DIRTY = "dirty";
156 }158 }
157 159
158 interface PlaylistSongsColumns extends SongsColumns160 interface PlaylistSongsColumns extends SongsColumns
@@ -376,7 +378,7 @@
376 .buildUpon().appendPath(PATH_CACHED).build();378 .buildUpon().appendPath(PATH_CACHED).build();
377 379
378 /** Default query projection. */380 /** Default query projection. */
379 public static final String[] DEFAULT_PROJECTION = new String[] {381 private static final String[] DEFAULT_PROJECTION = new String[] {
380 Songs._ID, Songs.SONG_ID, Songs.SONG_TITLE, Songs.SONG_ALBUM,382 Songs._ID, Songs.SONG_ID, Songs.SONG_TITLE, Songs.SONG_ALBUM,
381 Songs.SONG_ALBUM_ID, Songs.SONG_ALBUM_ARTIST,383 Songs.SONG_ALBUM_ID, Songs.SONG_ALBUM_ARTIST,
382 Songs.SONG_ARTIST, Songs.SONG_ARTIST_ID, Songs.SONG_GENRE,384 Songs.SONG_ARTIST, Songs.SONG_ARTIST_ID, Songs.SONG_GENRE,
@@ -385,6 +387,12 @@
385 Songs.SONG_BIT_RATE, Songs.SONG_SUFFIX, Songs.SONG_CONTENT_TYPE,387 Songs.SONG_BIT_RATE, Songs.SONG_SUFFIX, Songs.SONG_CONTENT_TYPE,
386 Songs.SONG_PATH, Songs.SONG_LAST_PLAYED_AT, Songs.STARRED, Songs.SONG_IS_PLAYING,388 Songs.SONG_PATH, Songs.SONG_LAST_PLAYED_AT, Songs.STARRED, Songs.SONG_IS_PLAYING,
387 };389 };
390
391 public static String[] getDefaultProjection() {
392 // karni: Earn $10 and tell me why you have to return a COPY of a *static final* for it
393 // not to change here, in MusicContract#DEFAULT_PROJECTION. Platform bug?
394 return DEFAULT_PROJECTION.clone();
395 }
388396
389 /** Default "ORDER BY" clause. */397 /** Default "ORDER BY" clause. */
390 public static final String DEFAULT_SORT = SongsColumns.SONG_TITLE398 public static final String DEFAULT_SORT = SongsColumns.SONG_TITLE
@@ -435,7 +443,7 @@
435 /** Default query projection. */443 /** Default query projection. */
436 public static final String[] DEFAULT_PROJECTION = new String[] {444 public static final String[] DEFAULT_PROJECTION = new String[] {
437 Playlists._ID, Playlists.PLAYLIST_ID, Playlists.PLAYLIST_NAME,445 Playlists._ID, Playlists.PLAYLIST_ID, Playlists.PLAYLIST_NAME,
438 Playlists.PLAYLIST_SONG_COUNT, Playlists.STARRED446 Playlists.PLAYLIST_SONG_COUNT, Playlists.STARRED, Playlists.DIRTY
439 };447 };
440 448
441 public static final String[] SONGS_DEFAULT_PROJECTION = new String[] {449 public static final String[] SONGS_DEFAULT_PROJECTION = new String[] {
@@ -503,7 +511,7 @@
503 }511 }
504 512
505 public static class PlaylistSongs implements PlaylistSongsColumns, SyncColumns,513 public static class PlaylistSongs implements PlaylistSongsColumns, SyncColumns,
506 SongsColumns, BaseColumns514 SongsColumns, StarredColumn, BaseColumns
507 {515 {
508 public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()516 public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
509 .appendPath(PATH_PLAYLISTS_SONGS).build();517 .appendPath(PATH_PLAYLISTS_SONGS).build();
510518
=== modified file 'src/com/ubuntuone/android/music/provider/MusicDatabase.java'
--- src/com/ubuntuone/android/music/provider/MusicDatabase.java 2012-11-21 15:03:12 +0000
+++ src/com/ubuntuone/android/music/provider/MusicDatabase.java 2012-11-28 17:49:20 +0000
@@ -202,6 +202,7 @@
202 + PlaylistsColumns.PLAYLIST_NAME + " TEXT NOT NULL, "202 + PlaylistsColumns.PLAYLIST_NAME + " TEXT NOT NULL, "
203 + PlaylistsColumns.PLAYLIST_SONG_COUNT + " INTEGER NOT NULL DEFAULT 0, "203 + PlaylistsColumns.PLAYLIST_SONG_COUNT + " INTEGER NOT NULL DEFAULT 0, "
204 + PlaylistsColumns.PLAYLIST_URL + " TEXT NOT NULL, "204 + PlaylistsColumns.PLAYLIST_URL + " TEXT NOT NULL, "
205 + PlaylistsColumns.DIRTY + " INTEGER NOT NULL DEFAULT 0, "
205 + "UNIQUE (" + PlaylistsColumns.PLAYLIST_ID + ") ON CONFLICT IGNORE)");206 + "UNIQUE (" + PlaylistsColumns.PLAYLIST_ID + ") ON CONFLICT IGNORE)");
206 207
207 // Create the play queue playlist.208 // Create the play queue playlist.
208209
=== modified file 'src/com/ubuntuone/android/music/provider/MusicProvider.java'
--- src/com/ubuntuone/android/music/provider/MusicProvider.java 2012-11-22 10:23:21 +0000
+++ src/com/ubuntuone/android/music/provider/MusicProvider.java 2012-11-28 17:49:20 +0000
@@ -602,18 +602,18 @@
602 final String playlistId = Playlists.getPlaylistId(uri);602 final String playlistId = Playlists.getPlaylistId(uri);
603 return builder.table(Tables.PLAYLIST_SONGS_JOIN_SONGS)603 return builder.table(Tables.PLAYLIST_SONGS_JOIN_SONGS)
604 .where(PlaylistSongs.PLAYLIST_ID + "=?", playlistId)604 .where(PlaylistSongs.PLAYLIST_ID + "=?", playlistId)
605 .mapToTable(Songs._ID, Tables.SONGS)605 .mapToTable(PlaylistSongs._ID, Tables.PLAYLISTS_SONGS)
606 .mapToTable(Songs.SONG_ID, Tables.SONGS);606 .mapToTable(PlaylistSongs.SONG_ID, Tables.SONGS);
607 }607 }
608 case PLAYLISTS_ID_SONGS_ID: {608 case PLAYLISTS_ID_SONGS_ID: {
609 final String playlistId = Playlists.getPlaylistId(uri);609 final String playlistId = Playlists.getPlaylistId(uri);
610 final String songId = Playlists.getSongId(uri);610 final String songId = Playlists.getSongId(uri);
611 return builder.table(Tables.PLAYLISTS_SONGS)611 return builder.table(Tables.PLAYLIST_SONGS_JOIN_SONGS)
612 .where(PlaylistSongs.PLAYLIST_ID + "=?", playlistId)612 .where(PlaylistSongs.PLAYLIST_ID + "=?", playlistId)
613 .where(PlaylistSongs.SONG_ID + "=?", songId)613 .where(PlaylistSongs.SONG_ID + "=?", songId)
614 .mapToTable(Songs._ID, Tables.SONGS)614 .mapToTable(PlaylistSongs._ID, Tables.PLAYLISTS_SONGS)
615 .mapToTable(Songs.SONG_ID, Tables.SONGS)615 .mapToTable(PlaylistSongs.SONG_ID, Tables.SONGS)
616 .mapToTable(Songs.STARRED, Tables.SONGS);616 .mapToTable(PlaylistSongs.STARRED, Tables.SONGS);
617 }617 }
618 618
619 case GENRES: {619 case GENRES: {
620620
=== modified file 'src/com/ubuntuone/android/music/provider/MusicProviderUtils.java'
--- src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2012-11-21 15:03:12 +0000
+++ src/com/ubuntuone/android/music/provider/MusicProviderUtils.java 2012-11-28 17:49:20 +0000
@@ -23,6 +23,7 @@
2323
24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
2525
26import java.util.ArrayList;
26import java.util.Random;27import java.util.Random;
2728
28import android.content.ContentResolver;29import android.content.ContentResolver;
@@ -208,11 +209,14 @@
208 209
209 public static int getPlaylistSongCount(Context context, Uri playlistUri) {210 public static int getPlaylistSongCount(Context context, Uri playlistUri) {
210 ContentResolver resolver = context.getContentResolver();211 ContentResolver resolver = context.getContentResolver();
212 String playlistId = Playlists.getPlaylistId(playlistUri);
213 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(playlistId);
214
211 String[] projection = new String[] { Songs._ID };215 String[] projection = new String[] { Songs._ID };
212 Cursor cursor = null;216 Cursor cursor = null;
213 int count = -1;217 int count = -1;
214 try {218 try {
215 cursor = resolver.query(playlistUri, projection, null, null, null);219 cursor = resolver.query(playlistSongsUri, projection, null, null, null);
216 count = (cursor != null) ? cursor.getCount() : 0;220 count = (cursor != null) ? cursor.getCount() : 0;
217 } finally {221 } finally {
218 if (cursor != null) cursor.close();222 if (cursor != null) cursor.close();
@@ -220,6 +224,28 @@
220 return count;224 return count;
221 }225 }
222 226
227 public static ArrayList<String> getPlaylistSongIdList(Context context, String playlistId) {
228 ContentResolver resolver = context.getContentResolver();
229 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(playlistId);
230 ArrayList<String> songIdList = new ArrayList<String>();
231
232 String[] projection = new String[] { PlaylistSongs.SONG_ID };
233 Cursor cursor = null;
234 try {
235 cursor = resolver.query(playlistSongsUri, projection, null, null, null);
236 if (cursor != null && cursor.isBeforeFirst()) {
237 while (cursor.moveToNext()) {
238 String songId = cursor.getString(cursor.getColumnIndex(PlaylistSongs.SONG_ID));
239 songIdList.add(songId);
240 }
241 }
242 } finally {
243 if (cursor != null) cursor.close();
244 }
245
246 return songIdList;
247 }
248
223 public static int getPlayQueueSongCount(Context context) {249 public static int getPlayQueueSongCount(Context context) {
224 Uri playQueueUri = Playlists.buildPlaylistSongsUri(QUEUE_ID);250 Uri playQueueUri = Playlists.buildPlaylistSongsUri(QUEUE_ID);
225 return getPlaylistSongCount(context, playQueueUri);251 return getPlaylistSongCount(context, playQueueUri);
@@ -236,14 +262,55 @@
236 Uri playQueueUri = Playlists.buildPlaylistSongsUri(MusicProviderUtils.QUEUE_ID);262 Uri playQueueUri = Playlists.buildPlaylistSongsUri(MusicProviderUtils.QUEUE_ID);
237 clearPlaylist(context, playQueueUri);263 clearPlaylist(context, playQueueUri);
238 }264 }
239265
266 /**
267 * Updates playlist song count, saves one request to the server upon
268 * enqueuing content.
269 *
270 * @param context
271 * The context to use.
272 * @param playlistUri
273 * Playlist Uri of which song count to update.
274 */
275 public static void updatePlaylistSongCount(Context context, Uri playlistUri) {
276 ContentResolver resolver = context.getContentResolver();
277 ContentValues values = new ContentValues();
278 int songCount = MusicProviderUtils.getPlaylistSongCount(context, playlistUri);
279 values.put(Playlists.PLAYLIST_SONG_COUNT, songCount);
280 resolver.update(playlistUri, values, null, null);
281 }
282
283 /**
284 * Flags the playlist as dirty for sync up. Next time metadata is refreshed,
285 * dirty playlists are first saved to the server.
286 *
287 * @param context
288 * The context to use.
289 * @param playlistUri
290 * Uri of the playlist to flag.
291 * @param isDirty
292 * true to mark for sync up, false otherwise.
293 */
294 public static void flagPlaylistForSync(Context context, Uri playlistUri, boolean isDirty) {
295 ContentResolver resolver = context.getContentResolver();
296 ContentValues values = new ContentValues();
297 values.put(Playlists.DIRTY, isDirty);
298 resolver.update(playlistUri, values, null, null);
299 }
300
240 public static void enqueueSong(Context context, Uri songUri, Uri playlistUri) {301 public static void enqueueSong(Context context, Uri songUri, Uri playlistUri) {
241 ContentResolver resolver = context.getContentResolver();302 ContentResolver resolver = context.getContentResolver();
242 String playlistId = Playlists.getPlaylistId(playlistUri);303 String playlistId = Playlists.getPlaylistId(playlistUri);
243 String songId = Songs.getSongId(songUri);304 String songId = Songs.getSongId(songUri);
305 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(playlistId);
244 306
245 ContentValues values = MusicContentValues.forPlaylistSong(playlistId, songId);307 ContentValues values = MusicContentValues.forPlaylistSong(playlistId, songId);
246 resolver.insert(playlistUri, values);308 resolver.insert(playlistSongsUri, values);
309
310 if (!QUEUE_ID.equals(playlistId)) {
311 updatePlaylistSongCount(context, playlistUri);
312 flagPlaylistForSync(context, playlistUri, true);
313 }
247 }314 }
248 315
249 public static void enqueueSong(Context context, Uri songUri) {316 public static void enqueueSong(Context context, Uri songUri) {
250317
=== modified file 'src/com/ubuntuone/android/music/service/MusicService.java'
--- src/com/ubuntuone/android/music/service/MusicService.java 2012-11-21 15:03:12 +0000
+++ src/com/ubuntuone/android/music/service/MusicService.java 2012-11-28 17:49:20 +0000
@@ -41,6 +41,7 @@
41import android.content.Context;41import android.content.Context;
42import android.content.Intent;42import android.content.Intent;
43import android.content.SharedPreferences;43import android.content.SharedPreferences;
44import android.database.sqlite.SQLiteDatabaseLockedException;
44import android.media.AudioManager;45import android.media.AudioManager;
45import android.media.MediaPlayer;46import android.media.MediaPlayer;
46import android.media.MediaPlayer.OnErrorListener;47import android.media.MediaPlayer.OnErrorListener;
@@ -57,6 +58,7 @@
57import com.ubuntuone.android.music.UbuntuOneMusic;58import com.ubuntuone.android.music.UbuntuOneMusic;
58import com.ubuntuone.android.music.model.PlayerState;59import com.ubuntuone.android.music.model.PlayerState;
59import com.ubuntuone.android.music.model.Song;60import com.ubuntuone.android.music.model.Song;
61import com.ubuntuone.android.music.provider.MusicContract.Songs;
60import com.ubuntuone.android.music.provider.MusicProviderUtils;62import com.ubuntuone.android.music.provider.MusicProviderUtils;
61import com.ubuntuone.android.music.util.UIUtils;63import com.ubuntuone.android.music.util.UIUtils;
62import com.ubuntuone.api.music.U1MusicAPI;64import com.ubuntuone.api.music.U1MusicAPI;
@@ -123,9 +125,13 @@
123 this.mShufflePlay = sharedPrefs.getBoolean(SHUFFLE_PREFERENCE_KEY, false);125 this.mShufflePlay = sharedPrefs.getBoolean(SHUFFLE_PREFERENCE_KEY, false);
124 this.mRepeatPlay = sharedPrefs.getBoolean(REPEAT_PREFERENCE_KEY, false);126 this.mRepeatPlay = sharedPrefs.getBoolean(REPEAT_PREFERENCE_KEY, false);
125 127
126 Uri songUri = MusicProviderUtils.getPlayingSong(this);128 try {
127 if (songUri != null) {129 Uri songUri = MusicProviderUtils.getPlayingSong(this);
128 mCurrentPlaying = new Song(this, songUri);130 if (songUri != null) {
131 mCurrentPlaying = new Song(this, songUri);
132 }
133 } catch (SQLiteDatabaseLockedException e) {
134 // Ignore. Assume no song has been playing.
129 }135 }
130 }136 }
131 137
@@ -325,8 +331,13 @@
325 }331 }
326 332
327 private synchronized void setCurrentPlaying(Uri uri) {333 private synchronized void setCurrentPlaying(Uri uri) {
328 MusicProviderUtils.setPlayingSong(this, uri);334 // Make sure this is a song Uri, rather than playlist song Uri.
329 this.mCurrentPlaying = new Song(this, uri);335 // This has to be changed when "now playing" column is moved to the PlaylistSongs table.
336 String songId = Songs.getSongId(uri);
337 Uri songUri = Songs.buildSongUri(songId);
338
339 MusicProviderUtils.setPlayingSong(this, songUri);
340 this.mCurrentPlaying = new Song(this, songUri);
330 callbackOnSongChanged();341 callbackOnSongChanged();
331 if (mCurrentPlaying.isCompleteFileAvailable()) {342 if (mCurrentPlaying.isCompleteFileAvailable()) {
332 callbackOnSongDownload(uri, 100);343 callbackOnSongDownload(uri, 100);
333344
=== modified file 'src/com/ubuntuone/android/music/service/SyncService.java'
--- src/com/ubuntuone/android/music/service/SyncService.java 2012-11-28 17:49:20 +0000
+++ src/com/ubuntuone/android/music/service/SyncService.java 2012-11-28 17:49:20 +0000
@@ -22,12 +22,16 @@
22package com.ubuntuone.android.music.service;22package com.ubuntuone.android.music.service;
2323
24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;24import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
25
26import java.util.ArrayList;
27
25import android.app.IntentService;28import android.app.IntentService;
26import android.content.ContentResolver;29import android.content.ContentResolver;
27import android.content.Context;30import android.content.Context;
28import android.content.Intent;31import android.content.Intent;
29import android.database.Cursor;32import android.database.Cursor;
30import android.database.sqlite.SQLiteDatabase;33import android.database.sqlite.SQLiteDatabase;
34import android.net.Uri;
31import android.os.Bundle;35import android.os.Bundle;
32import android.os.ResultReceiver;36import android.os.ResultReceiver;
33import android.util.Log;37import android.util.Log;
@@ -58,6 +62,7 @@
58import com.ubuntuone.api.music.model.U1Song;62import com.ubuntuone.api.music.model.U1Song;
59import com.ubuntuone.api.music.request.U1AlbumListener;63import com.ubuntuone.api.music.request.U1AlbumListener;
60import com.ubuntuone.api.music.request.U1ArtistListener;64import com.ubuntuone.api.music.request.U1ArtistListener;
65import com.ubuntuone.api.music.request.U1CallbackListener;
61import com.ubuntuone.api.music.request.U1PlaylistListener;66import com.ubuntuone.api.music.request.U1PlaylistListener;
62import com.ubuntuone.api.music.request.U1SongListener;67import com.ubuntuone.api.music.request.U1SongListener;
6368
@@ -67,6 +72,8 @@
67 72
68 public static final String ACTION_SYNC_MUSIC_METADATA =73 public static final String ACTION_SYNC_MUSIC_METADATA =
69 "com.ubuntuone.android.music.ACTION_SYNC_MUSIC_METADATA";74 "com.ubuntuone.android.music.ACTION_SYNC_MUSIC_METADATA";
75 public static final String ACTION_SYNC_PLAYLISTS =
76 "com.ubuntuone.android.music.ACTION_SYNC_PLAYLISTS";
70 77
71 public static final String EXTRA_RECEIVER = "result_receiver";78 public static final String EXTRA_RECEIVER = "result_receiver";
72 public static final String EXTRA_LOADER_ID = "loader_id";79 public static final String EXTRA_LOADER_ID = "loader_id";
@@ -103,19 +110,25 @@
103 @Override110 @Override
104 protected void onHandleIntent(Intent intent) {111 protected void onHandleIntent(Intent intent) {
105 final String action = intent.getAction();112 final String action = intent.getAction();
113 if (action == null) return;
106 mReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);114 mReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
107 if (action != null && ACTION_SYNC_MUSIC_METADATA.equals(action)) {115 mTimeSyncStarted = System.currentTimeMillis();
108 mTimeSyncStarted = System.currentTimeMillis();116 if (mApi != null && UbuntuOneMusic.SHOULD_SYNC) {
109 if (mApi != null && UbuntuOneMusic.SHOULD_SYNC) {117 sSyncStatus = SYNC_STARTED;
110 sSyncStatus = SYNC_STARTED;118 if (mReceiver != null) mReceiver.send(SYNC_STARTED, Bundle.EMPTY);
111 if (mReceiver != null) mReceiver.send(SYNC_STARTED, Bundle.EMPTY);119
120 if (ACTION_SYNC_MUSIC_METADATA.equals(action)) {
121 syncPlaylistMetadata();
112 syncMusicMetadata();122 syncMusicMetadata();
113 if (mReceiver != null) mReceiver.send(SYNC_FINISHED, Bundle.EMPTY);123 } else if (ACTION_SYNC_PLAYLISTS.equals(action)) {
114 sSyncStatus = SYNC_FINISHED;124 syncPlaylistMetadata();
115 }125 }
116 long now = System.currentTimeMillis();126
117 Log.i(TAG, "Ubuntu One music metadata sync sync took " + (now - mTimeSyncStarted) + " ms");127 if (mReceiver != null) mReceiver.send(SYNC_FINISHED, Bundle.EMPTY);
128 sSyncStatus = SYNC_FINISHED;
118 }129 }
130 long now = System.currentTimeMillis();
131 Log.i(TAG, "Ubuntu One music metadata sync sync took " + (now - mTimeSyncStarted) + " ms");
119 }132 }
120 133
121 private void syncMusicMetadata() {134 private void syncMusicMetadata() {
@@ -130,6 +143,10 @@
130 }143 }
131 }144 }
132 145
146 private void syncPlaylistMetadata() {
147 syncUpPlaylists();
148 }
149
133 private void syncArtists() {150 private void syncArtists() {
134 final long timeMillis = System.currentTimeMillis();151 final long timeMillis = System.currentTimeMillis();
135 mApi.getArtists(mArtistListener);152 mApi.getArtists(mArtistListener);
@@ -158,6 +175,38 @@
158 Log.i(TAG, "Playlists sync took " + (nowMillis - timeMillis) + "ms");175 Log.i(TAG, "Playlists sync took " + (nowMillis - timeMillis) + "ms");
159 }176 }
160 177
178 private void syncUpPlaylists() {
179 final long timeMillis = System.currentTimeMillis();
180 Cursor cursor = null;
181 try {
182 String[] projection = new String[] { Playlists.PLAYLIST_ID, Playlists.PLAYLIST_NAME };
183 String selection = Playlists.DIRTY + "=1";
184 cursor = getContentResolver().query(Playlists.CONTENT_URI,
185 projection, selection, null, null);
186 if (cursor != null && cursor.isBeforeFirst()) {
187 while (cursor.moveToNext()) {
188 final String playlistId = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_ID));
189 String playlistName = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));
190 ArrayList<String> songIdList = MusicProviderUtils.getPlaylistSongIdList(this, playlistId);
191
192 mApi.updatePlaylist(playlistId, playlistName, songIdList, new U1CallbackListener() {
193 @Override
194 public void onSuccess() {
195 Log.i(TAG, "Updated playlist: " + playlistId);
196 Uri playlistUri = Playlists.buildPlaylistUri(playlistId);
197 MusicProviderUtils.flagPlaylistForSync(SyncService.this, playlistUri, false);
198 // TODO Persist playlist entity from response once CSRF fix is rolled out.
199 }
200 });
201 }
202 }
203 } finally {
204 if (cursor != null) cursor.close();
205 }
206 final long nowMillis = System.currentTimeMillis();
207 Log.i(TAG, "Playlist songs sync up took " + (nowMillis - timeMillis) + "ms");
208 }
209
161 private void syncPlaylistSongs() {210 private void syncPlaylistSongs() {
162 final long timeMillis = System.currentTimeMillis();211 final long timeMillis = System.currentTimeMillis();
163 Cursor cursor = null;212 Cursor cursor = null;
@@ -175,7 +224,7 @@
175 if (cursor != null) cursor.close();224 if (cursor != null) cursor.close();
176 }225 }
177 final long nowMillis = System.currentTimeMillis();226 final long nowMillis = System.currentTimeMillis();
178 Log.i(TAG, "Playlist songs sync took " + (nowMillis - timeMillis) + "ms");227 Log.i(TAG, "Playlist songs sync down took " + (nowMillis - timeMillis) + "ms");
179 }228 }
180 229
181 private U1ArtistListener mArtistListener = new U1ArtistListener() {230 private U1ArtistListener mArtistListener = new U1ArtistListener() {
182231
=== modified file 'src/com/ubuntuone/android/music/ui/AlbumsFragment.java'
--- src/com/ubuntuone/android/music/ui/AlbumsFragment.java 2012-11-22 10:23:21 +0000
+++ src/com/ubuntuone/android/music/ui/AlbumsFragment.java 2012-11-28 17:49:20 +0000
@@ -25,6 +25,7 @@
25import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;25import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
26import android.app.Activity;26import android.app.Activity;
27import android.content.Context;27import android.content.Context;
28import android.content.Intent;
28import android.database.Cursor;29import android.database.Cursor;
29import android.net.Uri;30import android.net.Uri;
30import android.os.Bundle;31import android.os.Bundle;
@@ -187,7 +188,13 @@
187 }188 }
188 189
189 private boolean onAddToPlaylistContextItemSelected(int position) {190 private boolean onAddToPlaylistContextItemSelected(int position) {
190 // TODO191 Cursor cursor = (Cursor) mListView.getItemAtPosition(position);
192 String albumId = cursor.getString(cursor.getColumnIndex(Albums.ALBUM_ID));
193 Uri albumUri = Albums.buildAlbumUri(albumId);
194
195 Intent intent = new Intent(getActivity(), PlaylistsActivity.class);
196 intent.putExtra(PlaylistsActivity.EXTRA_ENQUEUE_CONTENT_ID, albumUri);
197 startActivity(intent);
191 return true;198 return true;
192 }199 }
193 200
194201
=== modified file 'src/com/ubuntuone/android/music/ui/HomeActivity.java'
--- src/com/ubuntuone/android/music/ui/HomeActivity.java 2012-11-21 15:03:12 +0000
+++ src/com/ubuntuone/android/music/ui/HomeActivity.java 2012-11-28 17:49:20 +0000
@@ -44,8 +44,10 @@
44import android.support.v4.app.LoaderManager;44import android.support.v4.app.LoaderManager;
45import android.support.v4.content.Loader;45import android.support.v4.content.Loader;
46import android.support.v4.view.ViewPager;46import android.support.v4.view.ViewPager;
47import android.support.v4.widget.CursorAdapter;
47import android.view.View;48import android.view.View;
48import android.view.View.OnClickListener;49import android.view.View.OnClickListener;
50import android.widget.Adapter;
49import android.widget.LinearLayout;51import android.widget.LinearLayout;
50import android.widget.Toast;52import android.widget.Toast;
5153
@@ -55,22 +57,26 @@
55import com.actionbarsherlock.view.MenuItem;57import com.actionbarsherlock.view.MenuItem;
56import com.ubuntuone.android.music.R;58import com.ubuntuone.android.music.R;
57import com.ubuntuone.android.music.UbuntuOneMusic;59import com.ubuntuone.android.music.UbuntuOneMusic;
60import com.ubuntuone.android.music.adapter.U1PlaylistAdapter;
61import com.ubuntuone.android.music.adapter.U1PlaylistAdapter.Position;
58import com.ubuntuone.android.music.adapter.holder.NowPlayingViewHolder;62import com.ubuntuone.android.music.adapter.holder.NowPlayingViewHolder;
59import com.ubuntuone.android.music.model.PlayerState;63import com.ubuntuone.android.music.model.PlayerState;
64import com.ubuntuone.android.music.provider.MusicContract.Playlists;
60import com.ubuntuone.android.music.provider.MusicContract.Songs;65import com.ubuntuone.android.music.provider.MusicContract.Songs;
61import com.ubuntuone.android.music.provider.MusicProviderUtils;66import com.ubuntuone.android.music.provider.MusicProviderUtils;
62import com.ubuntuone.android.music.service.MusicService;67import com.ubuntuone.android.music.service.MusicService;
63import com.ubuntuone.android.music.service.MusicServiceBinder;68import com.ubuntuone.android.music.service.MusicServiceBinder;
64import com.ubuntuone.android.music.service.MusicServiceCallback;69import com.ubuntuone.android.music.service.MusicServiceCallback;
65import com.ubuntuone.android.music.service.SyncService;70import com.ubuntuone.android.music.service.SyncService;
71import com.ubuntuone.android.music.ui.PlaylistsFragment.OnPlaylistSelectedListener;
66import com.ubuntuone.android.music.ui.dialog.InputAlertDialog.InputAlertDialogListener;72import com.ubuntuone.android.music.ui.dialog.InputAlertDialog.InputAlertDialogListener;
67import com.ubuntuone.android.music.util.AccountUtils;73import com.ubuntuone.android.music.util.AccountUtils;
68import com.ubuntuone.android.music.util.U1ImageDownloader;74import com.ubuntuone.android.music.util.U1ImageDownloader;
69import com.ubuntuone.android.music.util.UIUtils;75import com.ubuntuone.android.music.util.UIUtils;
7076
71public class HomeActivity extends SherlockFragmentActivity implements ActionBar.TabListener,77public class HomeActivity extends SherlockFragmentActivity implements ActionBar.TabListener,
72 ViewPager.OnPageChangeListener, OnClickListener, InputAlertDialogListener,78 ViewPager.OnPageChangeListener, OnClickListener, OnPlaylistSelectedListener,
73 MusicServiceCallback79 InputAlertDialogListener, MusicServiceCallback
74{80{
75 @SuppressWarnings("unused")81 @SuppressWarnings("unused")
76 private static final String TAG = makeLogTag(HomeActivity.class);82 private static final String TAG = makeLogTag(HomeActivity.class);
@@ -80,7 +86,7 @@
80 private static final int REQUEST_AUTHENTICATE = 1;86 private static final int REQUEST_AUTHENTICATE = 1;
81 87
82 /* package */ static final int PLEASE_WAIT_DIALOG_ID = 1;88 /* package */ static final int PLEASE_WAIT_DIALOG_ID = 1;
83 /* package */ static final int INFO_DIALOG_ID = 2;89 /* package */ static final int ERROR_DIALOG_ID = 2;
84 /* package */ static final int CREATE_PLAYLIST_DIALOG_ID = 3;90 /* package */ static final int CREATE_PLAYLIST_DIALOG_ID = 3;
85 91
86 private Handler mHandler = new Handler();92 private Handler mHandler = new Handler();
@@ -572,7 +578,47 @@
572 mShuffleAllSongsTask = null;578 mShuffleAllSongsTask = null;
573 }579 }
574 }580 }
575581
582 @Override
583 public void onPlaylistSelected(Adapter adapter, int position, boolean hasHeaders) {
584 if (hasHeaders && position < U1PlaylistAdapter.Position.COUNT) {
585 switch (position) {
586 case Position.STARRED: {
587 int count = MusicProviderUtils.getStarredSongCount(this);
588 PlaylistActivity.startFrom(this, MusicProviderUtils.STARRED_ID,
589 getString(R.string.starred), count);
590 break;
591 }
592 case Position.CACHED: {
593 int count = MusicProviderUtils.getCachedSongCount(this);
594 PlaylistActivity.startFrom(this, MusicProviderUtils.CACHED_ID,
595 getString(R.string.cached), count);
596 break;
597 }
598 case Position.QUEUED: {
599 PlayerActivity.startWithQueue(this);
600 break;
601 }
602 }
603 } else {
604 if (hasHeaders) position -= Position.COUNT;
605
606 Cursor cursor = ((CursorAdapter) adapter).getCursor();
607 cursor.moveToPosition(position);
608
609 int songCount = cursor.getInt(cursor.getColumnIndex(Playlists.PLAYLIST_SONG_COUNT));
610 if (songCount == 0) {
611 String title = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));
612 String text = getString(R.string.playlist_is_empty, title);
613 if (!isFinishing()) {
614 Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
615 }
616 } else {
617 PlaylistActivity.startFrom(this, cursor);
618 }
619 }
620 }
621
576 @Override622 @Override
577 public void onClick(View v) {623 public void onClick(View v) {
578 switch (v.getId()) {624 switch (v.getId()) {
579625
=== modified file 'src/com/ubuntuone/android/music/ui/PlayerFragment.java'
--- src/com/ubuntuone/android/music/ui/PlayerFragment.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/ui/PlayerFragment.java 2012-11-28 17:49:20 +0000
@@ -401,7 +401,7 @@
401401
402 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(MusicProviderUtils.QUEUE_ID);402 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(MusicProviderUtils.QUEUE_ID);
403 CursorLoader loader = new CursorLoader(getSherlockActivity(), playlistSongsUri,403 CursorLoader loader = new CursorLoader(getSherlockActivity(), playlistSongsUri,
404 Songs.DEFAULT_PROJECTION, null, null, PlaylistSongs.DEFAULT_SORT);404 Songs.getDefaultProjection(), null, null, PlaylistSongs.DEFAULT_SORT);
405 405
406 setIsLoading(true);406 setIsLoading(true);
407 return loader;407 return loader;
408408
=== modified file 'src/com/ubuntuone/android/music/ui/PlaylistActivity.java'
--- src/com/ubuntuone/android/music/ui/PlaylistActivity.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/ui/PlaylistActivity.java 2012-11-28 17:49:20 +0000
@@ -58,7 +58,6 @@
58 @Override58 @Override
59 protected void onCreate(Bundle savedInstanceState) {59 protected void onCreate(Bundle savedInstanceState) {
60 super.onCreate(savedInstanceState);60 super.onCreate(savedInstanceState);
61 // TODO Rename layout?
62 setContentView(R.layout.activity_collection);61 setContentView(R.layout.activity_collection);
63 62
64 mPlaylistArt = (ImageView) findViewById(R.id.image);63 mPlaylistArt = (ImageView) findViewById(R.id.image);
6564
=== modified file 'src/com/ubuntuone/android/music/ui/PlaylistsActivity.java'
--- src/com/ubuntuone/android/music/ui/PlaylistsActivity.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/ui/PlaylistsActivity.java 2012-11-28 17:49:20 +0000
@@ -21,16 +21,158 @@
2121
22package com.ubuntuone.android.music.ui;22package com.ubuntuone.android.music.ui;
2323
24import static com.ubuntuone.android.music.ui.HomeActivity.ERROR_DIALOG_ID;
25import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
26import android.app.Activity;
27import android.content.ContentResolver;
28import android.content.Intent;
29import android.database.Cursor;
30import android.net.Uri;
31import android.os.AsyncTask;
32import android.os.Bundle;
33import android.support.v4.app.LoaderManager;
34import android.support.v4.content.Loader;
35import android.support.v4.widget.CursorAdapter;
36import android.util.Log;
37import android.widget.Adapter;
38import android.widget.Toast;
39
24import com.actionbarsherlock.app.SherlockFragmentActivity;40import com.actionbarsherlock.app.SherlockFragmentActivity;
41import com.actionbarsherlock.view.MenuItem;
42import com.ubuntuone.android.music.R;
25import com.ubuntuone.android.music.provider.MusicContract.Albums;43import com.ubuntuone.android.music.provider.MusicContract.Albums;
26import com.ubuntuone.android.music.provider.MusicContract.Artists;44import com.ubuntuone.android.music.provider.MusicContract.Artists;
45import com.ubuntuone.android.music.provider.MusicContract.Playlists;
27import com.ubuntuone.android.music.provider.MusicContract.Songs;46import com.ubuntuone.android.music.provider.MusicContract.Songs;
47import com.ubuntuone.android.music.provider.MusicProviderUtils;
48import com.ubuntuone.android.music.service.SyncService;
49import com.ubuntuone.android.music.ui.PlaylistsFragment.OnPlaylistSelectedListener;
50import com.ubuntuone.android.music.ui.dialog.InfoAlertDialog.InfoAlertDialogListener;
51import com.ubuntuone.android.music.util.UIUtils;
2852
29public class PlaylistsActivity extends SherlockFragmentActivity53public class PlaylistsActivity extends SherlockFragmentActivity implements
54 OnPlaylistSelectedListener, InfoAlertDialogListener
30{55{
31 public static final String EXTRA_ENQUEUE_SONG_ID = Songs.SONG_ID;56 private static final String TAG = makeLogTag(PlaylistActivity.class);
32 public static final String EXTRA_ENQUEUE_ALBUM_ID = Albums.ALBUM_ID;57
33 public static final String EXTRA_ENQUEUE_ARTIST_ID = Artists.ARTIST_ID;58 public static final String EXTRA_ENQUEUE_CONTENT_ID = "content_uri";
34 59
35 // TODO enqueue provided content to selected playlist, allow creating a playlist60 private Uri mContentUri;
61
62 @Override
63 protected void onCreate(Bundle savedInstanceState) {
64 super.onCreate(savedInstanceState);
65 setContentView(R.layout.activity_playlists);
66
67 mContentUri = getIntent().getParcelableExtra(EXTRA_ENQUEUE_CONTENT_ID);
68
69 if (mContentUri == null) {
70 throw new IllegalStateException("You must provide content_uri which is " +
71 "to be added to a playlist.");
72 }
73
74 LoaderManager lm = getSupportLoaderManager();
75 Loader<?> loader = lm.getLoader(R.id.playlist_songs_loader_id);
76 if (loader != null) {
77 loader.stopLoading();
78 }
79 }
80
81 @Override
82 public boolean onOptionsItemSelected(MenuItem item) {
83 switch (item.getItemId()) {
84 case android.R.id.home:
85 finish();
86 return true;
87
88 default:
89 return super.onOptionsItemSelected(item);
90 }
91 }
92
93 @Override
94 public void onPlaylistSelected(Adapter adapter, int position, boolean hasHeaders) {
95 if (hasHeaders) {
96 throw new IllegalArgumentException(
97 "PlaylistsActivity must not contain custom playlists.");
98 }
99
100 Cursor cursor = ((CursorAdapter) adapter).getCursor();
101 cursor.moveToPosition(position);
102
103 String playlistId = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_ID));
104 Uri playlistUri = Playlists.buildPlaylistUri(playlistId);
105
106 if (mContentUri != null) {
107 AppendToPlaylistTask appendTask = new AppendToPlaylistTask();
108 appendTask.execute(playlistUri, mContentUri);
109 }
110 }
111
112 private class AppendToPlaylistTask extends AsyncTask<Uri, Void, Uri> {
113 @Override
114 protected Uri doInBackground(Uri... params) {
115 if (params.length != 2) {
116 throw new IllegalArgumentException(
117 "AppendToPlaylistTask takes two arguments, playlist uri and content uri");
118 }
119 final Uri playlistUri = params[0];
120 final Uri contentUri = params[1];
121
122 ContentResolver resolver = getContentResolver();
123
124 String mimeType = resolver.getType(contentUri);
125 Activity activity = PlaylistsActivity.this;
126 if (mimeType.equals(Artists.CONTENT_ITEM_TYPE)) {
127 Log.e(TAG, "artist");
128 MusicProviderUtils.enqueueArtist(activity, contentUri, playlistUri);
129 } else if (mimeType.equals(Albums.CONTENT_ITEM_TYPE)) {
130 Log.e(TAG, "album");
131 MusicProviderUtils.enqueueAlbum(activity, contentUri, playlistUri);
132 } else if (mimeType.equals(Songs.CONTENT_ITEM_TYPE)) {
133 Log.e(TAG, "song");
134 MusicProviderUtils.enqueueSong(activity, contentUri, playlistUri);
135 } else {
136 Log.e(TAG, "Unknown content uri to enqueue to playlist: " + contentUri.toString());
137 }
138 return playlistUri;
139 }
140
141 @Override
142 protected void onPostExecute(Uri playlistUri) {
143 if (playlistUri == null) {
144 Log.e(TAG, "PlaylistsActivity#AppendToPlaylistTask#onPostExecute null playlistUri");
145 return;
146 }
147
148 String playlistName = null;
149 Cursor cursor = null;
150 try {
151 final String[] projection = new String[] { Playlists.PLAYLIST_NAME };
152 cursor = getContentResolver().query(playlistUri, projection, null, null, null);
153 if (cursor != null && cursor.moveToFirst()) {
154 playlistName = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));
155 }
156 } finally {
157 cursor.close();
158 }
159
160 String msg = getString(R.string.added_to_fmt, playlistName);
161 Toast.makeText(PlaylistsActivity.this, msg, Toast.LENGTH_SHORT).show();
162
163 if (UIUtils.isNetworkConnected(PlaylistsActivity.this)) {
164 Intent syncPlaylists = new Intent(SyncService.ACTION_SYNC_PLAYLISTS);
165 startService(syncPlaylists);
166 }
167
168 finish();
169 }
170 }
171
172 @Override
173 public void onPositiveButtonClicked(int dialogId) {
174 if (dialogId == ERROR_DIALOG_ID) {
175 finish();
176 }
177 }
36}178}
37179
=== modified file 'src/com/ubuntuone/android/music/ui/PlaylistsFragment.java'
--- src/com/ubuntuone/android/music/ui/PlaylistsFragment.java 2012-11-22 10:23:21 +0000
+++ src/com/ubuntuone/android/music/ui/PlaylistsFragment.java 2012-11-28 17:49:20 +0000
@@ -21,7 +21,7 @@
2121
22package com.ubuntuone.android.music.ui;22package com.ubuntuone.android.music.ui;
2323
24import static com.ubuntuone.android.music.ui.HomeActivity.INFO_DIALOG_ID;24import static com.ubuntuone.android.music.ui.HomeActivity.ERROR_DIALOG_ID;
25import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;25import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
2626
27import java.util.ArrayList;27import java.util.ArrayList;
@@ -30,7 +30,6 @@
30import android.content.ContentResolver;30import android.content.ContentResolver;
31import android.content.Context;31import android.content.Context;
32import android.database.Cursor;32import android.database.Cursor;
33import android.net.Uri;
34import android.os.AsyncTask;33import android.os.AsyncTask;
35import android.os.Bundle;34import android.os.Bundle;
36import android.support.v4.app.DialogFragment;35import android.support.v4.app.DialogFragment;
@@ -44,27 +43,26 @@
44import android.view.View;43import android.view.View;
45import android.view.View.OnClickListener;44import android.view.View.OnClickListener;
46import android.view.ViewGroup;45import android.view.ViewGroup;
46import android.widget.Adapter;
47import android.widget.Button;47import android.widget.Button;
48import android.widget.ListView;48import android.widget.ListView;
49import android.widget.Toast;
5049
51import com.actionbarsherlock.app.SherlockFragmentActivity;50import com.actionbarsherlock.app.SherlockFragmentActivity;
52import com.ubuntuone.android.music.R;51import com.ubuntuone.android.music.R;
53import com.ubuntuone.android.music.UbuntuOneMusic;52import com.ubuntuone.android.music.UbuntuOneMusic;
54import com.ubuntuone.android.music.adapter.U1PlaylistAdapter;53import com.ubuntuone.android.music.adapter.U1PlaylistAdapter;
55import com.ubuntuone.android.music.adapter.U1PlaylistAdapter.Position;
56import com.ubuntuone.android.music.provider.MusicContract.Albums;
57import com.ubuntuone.android.music.provider.MusicContract.Artists;
58import com.ubuntuone.android.music.provider.MusicContract.Playlists;54import com.ubuntuone.android.music.provider.MusicContract.Playlists;
59import com.ubuntuone.android.music.provider.MusicContract.Songs;
60import com.ubuntuone.android.music.provider.MusicProviderUtils;55import com.ubuntuone.android.music.provider.MusicProviderUtils;
56import com.ubuntuone.android.music.provider.dao.PlaylistDao;
61import com.ubuntuone.android.music.ui.dialog.InfoAlertDialog;57import com.ubuntuone.android.music.ui.dialog.InfoAlertDialog;
62import com.ubuntuone.android.music.ui.dialog.InputAlertDialog;58import com.ubuntuone.android.music.ui.dialog.InputAlertDialog;
63import com.ubuntuone.android.music.ui.dialog.InputAlertDialog.InputAlertDialogListener;59import com.ubuntuone.android.music.ui.dialog.InputAlertDialog.InputAlertDialogListener;
64import com.ubuntuone.android.music.ui.dialog.ModalAlertDialog;60import com.ubuntuone.android.music.ui.dialog.ModalAlertDialog;
65import com.ubuntuone.api.music.U1MusicAPI;61import com.ubuntuone.api.music.U1MusicAPI;
66import com.ubuntuone.api.music.client.Failure;62import com.ubuntuone.api.music.client.Failure;
63import com.ubuntuone.api.music.model.U1Playlist;
67import com.ubuntuone.api.music.request.U1PlaylistCreateListener;64import com.ubuntuone.api.music.request.U1PlaylistCreateListener;
65import com.ubuntuone.api.music.request.U1PlaylistListener;
6866
69public class PlaylistsFragment extends LoadingFragment<U1PlaylistAdapter> implements67public class PlaylistsFragment extends LoadingFragment<U1PlaylistAdapter> implements
70 OnClickListener, InputAlertDialogListener68 OnClickListener, InputAlertDialogListener
@@ -74,6 +72,8 @@
74 public static final String EXTRA_INCLUDE_CUSTOM = "include_custom";72 public static final String EXTRA_INCLUDE_CUSTOM = "include_custom";
75 73
76 private boolean mIncludeCustom = false;74 private boolean mIncludeCustom = false;
75
76 private OnPlaylistSelectedListener mOnPlaylistSelectedListener;
7777
78 @Override78 @Override
79 public int onGetHeaderResId() {79 public int onGetHeaderResId() {
@@ -113,52 +113,18 @@
113 Button createPlaylist = (Button) view.findViewById(R.id.create_playlist);113 Button createPlaylist = (Button) view.findViewById(R.id.create_playlist);
114 createPlaylist.setOnClickListener(this);114 createPlaylist.setOnClickListener(this);
115 115
116 try {
117 mOnPlaylistSelectedListener = (OnPlaylistSelectedListener) getActivity();
118 } catch (ClassCastException e) {
119 throw new IllegalArgumentException("Activity must implement OnPlaylistSelectedListener");
120 }
121
116 return view;122 return view;
117 }123 }
118 124
119 @Override125 @Override
120 public void onListItemClick(ListView l, View v, int position, long id) {126 public void onListItemClick(ListView l, View v, int position, long id) {
121 showPlaylistSongs(position);127 mOnPlaylistSelectedListener.onPlaylistSelected(mAdapter, position, mIncludeCustom);
122 }
123
124 private void showPlaylistSongs(int position) {
125 if (mIncludeCustom && position < U1PlaylistAdapter.Position.COUNT) {
126 Context context = getActivity();
127 switch (position) {
128 case Position.STARRED: {
129 int count = MusicProviderUtils.getStarredSongCount(context);
130 PlaylistActivity.startFrom(context, MusicProviderUtils.STARRED_ID,
131 context.getString(R.string.starred), count);
132 break;
133 }
134 case Position.CACHED: {
135 int count = MusicProviderUtils.getCachedSongCount(context);
136 PlaylistActivity.startFrom(getActivity(), MusicProviderUtils.CACHED_ID,
137 context.getString(R.string.cached), count);
138 break;
139 }
140 case Position.QUEUED: {
141 PlayerActivity.startWithQueue(getSherlockActivity());
142 break;
143 }
144 }
145 } else {
146 if (mIncludeCustom) position -= Position.COUNT;
147
148 Cursor cursor = mAdapter.getCursor();
149 cursor.moveToPosition(position);
150
151 int songCount = cursor.getInt(cursor.getColumnIndex(Playlists.PLAYLIST_SONG_COUNT));
152 if (songCount == 0) {
153 String title = cursor.getString(cursor.getColumnIndex(Playlists.PLAYLIST_NAME));
154 String text = getString(R.string.playlist_is_empty, title);
155 if (isResumed()) {
156 Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show();
157 }
158 } else {
159 PlaylistActivity.startFrom(getActivity(), cursor);
160 }
161 }
162 }128 }
163129
164 @Override130 @Override
@@ -238,6 +204,7 @@
238 api.createPlaylist(name, emptyList, new U1PlaylistCreateListener() {204 api.createPlaylist(name, emptyList, new U1PlaylistCreateListener() {
239 @Override205 @Override
240 public void onSuccess(String playlistId) {206 public void onSuccess(String playlistId) {
207 // TODO Persist playlist once CSRF fix is rolled out.
241 Log.i(TAG, "Created playlist with id: " + playlistId);208 Log.i(TAG, "Created playlist with id: " + playlistId);
242 mPlaylistId = playlistId;209 mPlaylistId = playlistId;
243 }210 }
@@ -250,85 +217,40 @@
250 }217 }
251 });218 });
252 219
253 // TODO karni: save the playlist220 // TODO This block will be removed soon, see TODO above.
221 Activity activity = getSherlockActivity();
222 final ContentResolver resolver = activity != null ? activity.getContentResolver() : null;
223 if (mPlaylistId != null && resolver != null) {
224 api.getPlaylists(new U1PlaylistListener() {
225 @Override
226 public void onSuccess(U1Playlist playlist) {
227 if (mPlaylistId.equals(playlist.getId())) {
228 new PlaylistDao().updateOrInsert(resolver, playlist);
229 }
230 }
231 });
232 }
233
254 return mPlaylistId != null;234 return mPlaylistId != null;
255 }235 }
256236
257 @Override237 @Override
258 protected void onPostExecute(Boolean result) {238 protected void onPostExecute(Boolean created) {
259 if (mDialogFragment != null && mDialogFragment.isAdded()) {239 if (mDialogFragment != null && mDialogFragment.isAdded()) {
260 mDialogFragment.dismiss();240 mDialogFragment.dismiss();
261 }241 }
262 242
263 SherlockFragmentActivity activity = getSherlockActivity();243 SherlockFragmentActivity activity = getSherlockActivity();
264 boolean created = mPlaylistId != null;
265 if (created && activity != null) {244 if (created && activity != null) {
266 ((HomeActivity) activity).forcePlaylistsRefresh();245 ((HomeActivity) activity).forcePlaylistsRefresh();
267 } else if (!created && activity != null && !TextUtils.isEmpty(mErrorMessage)) {246 } else if (!created && activity != null && !TextUtils.isEmpty(mErrorMessage)) {
268 DialogFragment frag = InfoAlertDialog.newInstance(INFO_DIALOG_ID,247 DialogFragment frag = InfoAlertDialog.newInstance(ERROR_DIALOG_ID, 0, mErrorMessage);
269 R.string.creating_playlist_failed_title, mErrorMessage);
270 frag.show(activity.getSupportFragmentManager(), null);248 frag.show(activity.getSupportFragmentManager(), null);
271 }249 }
272 }250 }
273 }251 }
274 252
275 private class AppendToPlaylistTask extends AsyncTask<Uri, Void, Boolean> {253 public interface OnPlaylistSelectedListener {
276 private DialogFragment mDialogFragment;254 public void onPlaylistSelected(Adapter adapter, int position, boolean hasHeaders);
277 private Boolean mUpdated = false;
278 private String mErrorMessage;
279
280 @Override
281 protected void onPreExecute() {
282 SherlockFragmentActivity activity = getSherlockActivity();
283 if (activity != null) {
284 mDialogFragment = ModalAlertDialog.newInstance();
285 mDialogFragment.show(activity.getSupportFragmentManager(), "pleaseWaitDialog");
286 }
287 }
288
289 @Override
290 protected Boolean doInBackground(Uri... params) {
291 if (params.length != 1) {
292 throw new IllegalArgumentException(
293 "AppendToPlaylistTask takes two arguments, playlist uri and content uri");
294 }
295 final Uri playlistUri = params[0];
296 final Uri contentUri = params[1];
297
298 Activity activity = getSherlockActivity();
299 ContentResolver resolver = activity != null ? activity.getContentResolver() : null;
300 if (resolver == null) return mUpdated;
301
302 String mimeType = resolver.getType(contentUri);
303 if (mimeType.equals(Artists.CONTENT_ITEM_TYPE)) {
304 MusicProviderUtils.enqueueArtist(activity, contentUri, playlistUri);
305 } else if (mimeType.equals(Albums.CONTENT_ITEM_TYPE)) {
306 MusicProviderUtils.enqueueAlbum(activity, contentUri, playlistUri);
307 } else if (mimeType.equals(Songs.CONTENT_ITEM_TYPE)) {
308 MusicProviderUtils.enqueueSong(activity, contentUri, playlistUri);
309 } else {
310 Log.e(TAG, "Unknown content uri to enqueue to playlist: " + contentUri.toString());
311 }
312
313 U1MusicAPI api = UbuntuOneMusic.getInstance().getMusicApi();
314 // TODO save playlist to server
315 return mUpdated;
316 }
317
318 @Override
319 protected void onPostExecute(Boolean result) {
320 if (mDialogFragment != null && mDialogFragment.isAdded()) {
321 mDialogFragment.dismiss();
322 }
323
324 SherlockFragmentActivity activity = getSherlockActivity();
325 if (mUpdated && activity != null) {
326 ((HomeActivity) activity).forcePlaylistsRefresh();
327 } else if (!mUpdated && activity != null && !TextUtils.isEmpty(mErrorMessage)) {
328 DialogFragment frag = InfoAlertDialog.newInstance(INFO_DIALOG_ID,
329 R.string.creating_playlist_failed_title, mErrorMessage);
330 frag.show(activity.getSupportFragmentManager(), null);
331 }
332 }
333 }255 }
334}256}
335257
=== modified file 'src/com/ubuntuone/android/music/ui/SongsFragment.java'
--- src/com/ubuntuone/android/music/ui/SongsFragment.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/ui/SongsFragment.java 2012-11-28 17:49:20 +0000
@@ -26,7 +26,9 @@
26import static com.ubuntuone.android.music.ui.PlaylistActivity.EXTRA_PLAYLIST_ID;26import static com.ubuntuone.android.music.ui.PlaylistActivity.EXTRA_PLAYLIST_ID;
27import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;27import static com.ubuntuone.android.music.util.LogUtils.makeLogTag;
28import android.app.Activity;28import android.app.Activity;
29import android.content.ContentResolver;
29import android.content.Context;30import android.content.Context;
31import android.content.Intent;
30import android.database.Cursor;32import android.database.Cursor;
31import android.net.Uri;33import android.net.Uri;
32import android.os.Bundle;34import android.os.Bundle;
@@ -49,9 +51,11 @@
49import com.ubuntuone.android.music.adapter.U1SongAdapter;51import com.ubuntuone.android.music.adapter.U1SongAdapter;
50import com.ubuntuone.android.music.provider.MusicContract.Albums;52import com.ubuntuone.android.music.provider.MusicContract.Albums;
51import com.ubuntuone.android.music.provider.MusicContract.Genres;53import com.ubuntuone.android.music.provider.MusicContract.Genres;
54import com.ubuntuone.android.music.provider.MusicContract.PlaylistSongs;
52import com.ubuntuone.android.music.provider.MusicContract.Playlists;55import com.ubuntuone.android.music.provider.MusicContract.Playlists;
53import com.ubuntuone.android.music.provider.MusicContract.Songs;56import com.ubuntuone.android.music.provider.MusicContract.Songs;
54import com.ubuntuone.android.music.provider.MusicProviderUtils;57import com.ubuntuone.android.music.provider.MusicProviderUtils;
58import com.ubuntuone.android.music.service.SyncService;
55import com.ubuntuone.android.music.util.UIUtils;59import com.ubuntuone.android.music.util.UIUtils;
5660
57public class SongsFragment extends LoadingFragment<U1SongAdapter> implements OnClickListener61public class SongsFragment extends LoadingFragment<U1SongAdapter> implements OnClickListener
@@ -129,28 +133,28 @@
129 if (mAlbumId != null) {133 if (mAlbumId != null) {
130 Uri albumSongsUri = Albums.buildSongsUri(mAlbumId);134 Uri albumSongsUri = Albums.buildSongsUri(mAlbumId);
131 loader = new CursorLoader(getSherlockActivity(), albumSongsUri,135 loader = new CursorLoader(getSherlockActivity(), albumSongsUri,
132 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);136 Songs.getDefaultProjection(), null, null, Albums.DEFAULT_SONG_SORT);
133 } else if (mPlaylistId != null) {137 } else if (mPlaylistId != null) {
134 if (mPlaylistId.equals(MusicProviderUtils.STARRED_ID)) {138 if (mPlaylistId.equals(MusicProviderUtils.STARRED_ID)) {
135 loader = new CursorLoader(getSherlockActivity(), Songs.STARRED_SONGS_URI,139 loader = new CursorLoader(getSherlockActivity(), Songs.STARRED_SONGS_URI,
136 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);140 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
137 loader.onContentChanged();141 loader.onContentChanged();
138 } else if (mPlaylistId.equals(MusicProviderUtils.CACHED_ID)) {142 } else if (mPlaylistId.equals(MusicProviderUtils.CACHED_ID)) {
139 loader = new CursorLoader(getSherlockActivity(), Songs.CACHED_SONGS_URI,143 loader = new CursorLoader(getSherlockActivity(), Songs.CACHED_SONGS_URI,
140 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);144 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
141 loader.onContentChanged();145 loader.onContentChanged();
142 } else {146 } else {
143 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(mPlaylistId);147 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri(mPlaylistId);
144 loader = new CursorLoader(getSherlockActivity(), playlistSongsUri,148 loader = new CursorLoader(getSherlockActivity(), playlistSongsUri,
145 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);149 PlaylistSongs.DEFAULT_PROJECTION.clone(), null, null, PlaylistSongs.DEFAULT_SORT);
146 }150 }
147 } else if (mGenreId != null) {151 } else if (mGenreId != null) {
148 Uri genreSongsUri = Genres.buildGenreSongsUri(mGenreId);152 Uri genreSongsUri = Genres.buildGenreSongsUri(mGenreId);
149 loader = new CursorLoader(getSherlockActivity(), genreSongsUri,153 loader = new CursorLoader(getSherlockActivity(), genreSongsUri,
150 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);154 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
151 } else {155 } else {
152 loader = new CursorLoader(getSherlockActivity(), Songs.CONTENT_URI,156 loader = new CursorLoader(getSherlockActivity(), Songs.CONTENT_URI,
153 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);157 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
154 }158 }
155 setIsLoading(true);159 setIsLoading(true);
156 return loader;160 return loader;
@@ -189,15 +193,18 @@
189 @Override193 @Override
190 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {194 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
191 if (v.getId() == android.R.id.list) {195 if (v.getId() == android.R.id.list) {
192 menu.add(Menu.NONE, R.id.context_play, 0, R.string.description_play);196 menu.add(Menu.NONE, R.id.context_play, 0,
193 197 R.string.description_play);
194 menu.add(Menu.NONE, R.id.context_star, 0, R.string.description_star);198 menu.add(Menu.NONE, R.id.context_star, 0,
195 Activity activity = getSherlockActivity();199 R.string.description_star);
196 if (activity != null && UIUtils.isNetworkConnected(activity)) {200 menu.add(Menu.NONE, R.id.context_add_to_playlist, 0,
197 menu.add(Menu.NONE, R.id.context_add_to_playlist, 0,201 R.string.description_add_to_playlist);
198 R.string.description_add_to_playlist);202 if (mPlaylistId != null) {
203 menu.add(Menu.NONE, R.id.context_remove_from_playlist, 0,
204 R.string.description_remove_from_playlist);
199 }205 }
200 menu.add(Menu.NONE, R.id.context_add_to_queue, 0, R.string.description_add_to_queue);206 menu.add(Menu.NONE, R.id.context_add_to_queue, 0,
207 R.string.description_add_to_queue);
201 } else {208 } else {
202 super.onCreateContextMenu(menu, v, menuInfo);209 super.onCreateContextMenu(menu, v, menuInfo);
203 }210 }
@@ -216,6 +223,8 @@
216 return onStarContextItemSelected(position);223 return onStarContextItemSelected(position);
217 case R.id.context_add_to_playlist:224 case R.id.context_add_to_playlist:
218 return onAddToPlaylistContextItemSelected(position);225 return onAddToPlaylistContextItemSelected(position);
226 case R.id.context_remove_from_playlist:
227 return onRemoveFromPlaylistContextItemSelected(position);
219 case R.id.context_add_to_queue:228 case R.id.context_add_to_queue:
220 return onAddToPlayQueueContextItemSelected(position);229 return onAddToPlayQueueContextItemSelected(position);
221 default:230 default:
@@ -223,6 +232,12 @@
223 }232 }
224 }233 }
225 234
235 private void forceReload() {
236 int loaderId = getLoaderId();
237 LoaderManager lm = getSherlockActivity().getSupportLoaderManager();
238 lm.getLoader(loaderId).forceLoad();
239 }
240
226 private boolean onPlayContextItemSelected(int position) {241 private boolean onPlayContextItemSelected(int position) {
227 clearQueueAndPlay(mListView, position);242 clearQueueAndPlay(mListView, position);
228 return true;243 return true;
@@ -238,16 +253,41 @@
238 253
239 MusicProviderUtils.toggleSongStarred(context, songUri);254 MusicProviderUtils.toggleSongStarred(context, songUri);
240 255
241 int loaderId = getLoaderId();256 forceReload();
242 LoaderManager lm = getSherlockActivity().getSupportLoaderManager();
243 lm.getLoader(loaderId).forceLoad();
244 }257 }
245 }258 }
246 return true;259 return true;
247 }260 }
248 261
249 private boolean onAddToPlaylistContextItemSelected(int position) {262 private boolean onAddToPlaylistContextItemSelected(int position) {
250 // TODO263 Cursor cursor = (Cursor) mListView.getItemAtPosition(position);
264 String songId = cursor.getString(cursor.getColumnIndex(Songs.SONG_ID));
265 Uri songUri = Songs.buildSongUri(songId);
266
267 Intent intent = new Intent(getActivity(), PlaylistsActivity.class);
268 intent.putExtra(PlaylistsActivity.EXTRA_ENQUEUE_CONTENT_ID, songUri);
269 startActivity(intent);
270 return true;
271 }
272
273 private boolean onRemoveFromPlaylistContextItemSelected(int position) {
274 Cursor cursor = (Cursor) mListView.getItemAtPosition(position);
275 String rowId = cursor.getString(cursor.getColumnIndex(Songs._ID));
276
277 ContentResolver resolver = getActivity().getContentResolver();
278 String selection = Songs._ID + "=?";
279 String[] selectionArgs = new String[] { rowId };
280 resolver.delete(PlaylistSongs.CONTENT_URI, selection, selectionArgs);
281
282 Activity activity = getActivity();
283 Uri playlistUri = Playlists.buildPlaylistUri(mPlaylistId);
284 MusicProviderUtils.flagPlaylistForSync(activity, playlistUri, true);
285
286 forceReload();
287 if (activity != null && UIUtils.isNetworkConnected(activity)) {
288 Intent syncPlaylists = new Intent(SyncService.ACTION_SYNC_PLAYLISTS);
289 activity.startService(syncPlaylists);
290 }
251 return true;291 return true;
252 }292 }
253 293
254294
=== modified file 'src/com/ubuntuone/android/music/ui/dialog/InfoAlertDialog.java'
--- src/com/ubuntuone/android/music/ui/dialog/InfoAlertDialog.java 2012-11-08 14:14:54 +0000
+++ src/com/ubuntuone/android/music/ui/dialog/InfoAlertDialog.java 2012-11-28 17:49:20 +0000
@@ -28,6 +28,7 @@
28import android.content.DialogInterface.OnClickListener;28import android.content.DialogInterface.OnClickListener;
29import android.os.Bundle;29import android.os.Bundle;
30import android.view.Gravity;30import android.view.Gravity;
31import android.view.ViewGroup.LayoutParams;
31import android.widget.TextView;32import android.widget.TextView;
3233
33import com.actionbarsherlock.app.SherlockDialogFragment;34import com.actionbarsherlock.app.SherlockDialogFragment;
@@ -41,7 +42,7 @@
41 InfoAlertDialog frag = new InfoAlertDialog();42 InfoAlertDialog frag = new InfoAlertDialog();
42 Bundle args = new Bundle();43 Bundle args = new Bundle();
43 args.putInt("dialogId", dialogId);44 args.putInt("dialogId", dialogId);
44 args.putInt("titleId", titleId);45 if (titleId != 0) args.putInt("titleId", titleId);
45 args.putString("message", message);46 args.putString("message", message);
46 frag.setArguments(args);47 frag.setArguments(args);
47 return frag;48 return frag;
@@ -61,11 +62,12 @@
61 public Dialog onCreateDialog(Bundle savedInstanceState) {62 public Dialog onCreateDialog(Bundle savedInstanceState) {
62 Bundle args = getArguments();63 Bundle args = getArguments();
63 final int dialogId = args.getInt("dialogId");64 final int dialogId = args.getInt("dialogId");
64 final int titleId = args.getInt("titleId");
65 final String message = args.getString("message");65 final String message = args.getString("message");
66 66
67 final TextView view = new TextView(getSherlockActivity());67 final TextView view = new TextView(getSherlockActivity());
68 view.setTextAppearance(getSherlockActivity(), android.R.style.TextAppearance_Large);
68 view.setGravity(Gravity.CENTER);69 view.setGravity(Gravity.CENTER);
70 view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
69 view.setText(message);71 view.setText(message);
70 72
71 OnClickListener listener = new OnClickListener() {73 OnClickListener listener = new OnClickListener() {
@@ -79,11 +81,15 @@
79 }81 }
80 };82 };
81 83
82 return new AlertDialog.Builder(getActivity())84 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
83 .setTitle(titleId)
84 .setView(view)85 .setView(view)
85 .setPositiveButton(R.string.ok, listener)86 .setPositiveButton(R.string.ok, listener);
86 .create();87 if (args.containsKey("titleId")) {
88 int titleId = args.getInt("titleId");
89 builder.setTitle(titleId);
90 }
91
92 return builder.create();
87 }93 }
88 94
89 public static interface InfoAlertDialogListener {95 public static interface InfoAlertDialogListener {
9096
=== modified file 'test/src/com/ubuntuone/android/music/provider/MusicProviderInsertTest.java'
--- test/src/com/ubuntuone/android/music/provider/MusicProviderInsertTest.java 2012-11-22 11:17:42 +0000
+++ test/src/com/ubuntuone/android/music/provider/MusicProviderInsertTest.java 2012-11-28 17:49:20 +0000
@@ -161,7 +161,7 @@
161 161
162 assertNotNull("Inserted song uri is null.", uri);162 assertNotNull("Inserted song uri is null.", uri);
163 163
164 Cursor cursor = mResolver.query(uri, Songs.DEFAULT_PROJECTION, null, null, null);164 Cursor cursor = mResolver.query(uri, Songs.getDefaultProjection(), null, null, null);
165 assertEquals("Wrong song row count returned.", 1, cursor.getCount());165 assertEquals("Wrong song row count returned.", 1, cursor.getCount());
166 assertTrue("Cursor missing rows.", cursor.moveToNext());166 assertTrue("Cursor missing rows.", cursor.moveToNext());
167 167
@@ -218,7 +218,7 @@
218 218
219 Uri uri = Songs.buildSongUri(song.getId());219 Uri uri = Songs.buildSongUri(song.getId());
220 220
221 Cursor cursor = mResolver.query(uri, Songs.DEFAULT_PROJECTION, null, null, null);221 Cursor cursor = mResolver.query(uri, Songs.getDefaultProjection(), null, null, null);
222 assertEquals("Wrong song row count returned.", 1, cursor.getCount());222 assertEquals("Wrong song row count returned.", 1, cursor.getCount());
223 assertTrue("Cursor missing rows.", cursor.moveToNext());223 assertTrue("Cursor missing rows.", cursor.moveToNext());
224 224
225225
=== modified file 'test/src/com/ubuntuone/android/music/provider/MusicProviderQueryTest.java'
--- test/src/com/ubuntuone/android/music/provider/MusicProviderQueryTest.java 2012-11-08 14:14:54 +0000
+++ test/src/com/ubuntuone/android/music/provider/MusicProviderQueryTest.java 2012-11-28 17:49:20 +0000
@@ -81,7 +81,7 @@
81 public void testQueryArtistSongs() {81 public void testQueryArtistSongs() {
82 Uri artistSongsUri = Artists.buildSongsUri(HashUtils.md5("artist1-artist_name"));82 Uri artistSongsUri = Artists.buildSongsUri(HashUtils.md5("artist1-artist_name"));
83 Cursor cursor = mResolver.query(artistSongsUri,83 Cursor cursor = mResolver.query(artistSongsUri,
84 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);84 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
85 85
86 assertEquals("Wrong artist songs count returned.", 2, cursor.getCount());86 assertEquals("Wrong artist songs count returned.", 2, cursor.getCount());
87 87
@@ -141,7 +141,7 @@
141 Uri albumSongsUri = Albums.buildSongsUri(141 Uri albumSongsUri = Albums.buildSongsUri(
142 HashUtils.md5("album1-album_titleartist1-artist_name"));142 HashUtils.md5("album1-album_titleartist1-artist_name"));
143 Cursor cursor = mResolver.query(albumSongsUri,143 Cursor cursor = mResolver.query(albumSongsUri,
144 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);144 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
145 145
146 assertEquals("Wrong album songs count returned.", 2, cursor.getCount());146 assertEquals("Wrong album songs count returned.", 2, cursor.getCount());
147 147
@@ -156,7 +156,7 @@
156 156
157 public void testQuerySongs() {157 public void testQuerySongs() {
158 Cursor cursor = mResolver.query(Songs.CONTENT_URI,158 Cursor cursor = mResolver.query(Songs.CONTENT_URI,
159 Songs.DEFAULT_PROJECTION, null, null, Songs.DEFAULT_SORT);159 Songs.getDefaultProjection(), null, null, Songs.DEFAULT_SORT);
160 160
161 assertEquals("Wrong song count returned.", MockData.SONGS_COUNT, cursor.getCount());161 assertEquals("Wrong song count returned.", MockData.SONGS_COUNT, cursor.getCount());
162 162
@@ -173,7 +173,7 @@
173 public void testQuerySongsId() {173 public void testQuerySongsId() {
174 Uri songUri = Songs.buildSongUri("song1-song_id");174 Uri songUri = Songs.buildSongUri("song1-song_id");
175 Cursor cursor = mResolver.query(songUri,175 Cursor cursor = mResolver.query(songUri,
176 Songs.DEFAULT_PROJECTION, null, null, null);176 Songs.getDefaultProjection(), null, null, null);
177 177
178 assertEquals("Wrong songs count returned.",178 assertEquals("Wrong songs count returned.",
179 1, cursor.getCount());179 1, cursor.getCount());
180180
=== modified file 'test/src/com/ubuntuone/android/music/provider/MusicProviderUtilsTest.java'
--- test/src/com/ubuntuone/android/music/provider/MusicProviderUtilsTest.java 2012-11-13 13:58:31 +0000
+++ test/src/com/ubuntuone/android/music/provider/MusicProviderUtilsTest.java 2012-11-28 17:49:20 +0000
@@ -113,7 +113,7 @@
113 }113 }
114 114
115 public void testGetPlaylistSongCount() {115 public void testGetPlaylistSongCount() {
116 Uri playlistUri = Playlists.buildPlaylistSongsUri("playlist1-playlist_id");116 Uri playlistUri = Playlists.buildPlaylistUri("playlist1-playlist_id");
117 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);117 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
118 assertEquals("Wrong playlist song count.", 2, songCount);118 assertEquals("Wrong playlist song count.", 2, songCount);
119 }119 }
@@ -181,7 +181,7 @@
181 }181 }
182 182
183 public void testEnqueueSongInPlaylist() {183 public void testEnqueueSongInPlaylist() {
184 Uri playlistUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");184 Uri playlistUri = Playlists.buildPlaylistUri("playlist2-playlist_id");
185 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);185 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
186 assertEquals("Wrong song count of playlist.", 1, songCount);186 assertEquals("Wrong song count of playlist.", 1, songCount);
187 187
@@ -192,10 +192,11 @@
192 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);192 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
193 assertEquals("Wrong new song count of playlist.", songCount + 1, newSongCount);193 assertEquals("Wrong new song count of playlist.", songCount + 1, newSongCount);
194 194
195 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");
195 Cursor cursor = null;196 Cursor cursor = null;
196 try {197 try {
197 String[] projection = new String[] { Songs.SONG_ID };198 String[] projection = new String[] { Songs.SONG_ID };
198 cursor = mResolver.query(playlistUri, projection,199 cursor = mResolver.query(playlistSongsUri, projection,
199 null, null, PlaylistSongs.DEFAULT_SORT);200 null, null, PlaylistSongs.DEFAULT_SORT);
200 if (cursor != null && cursor.isBeforeFirst()) {201 if (cursor != null && cursor.isBeforeFirst()) {
201 cursor.moveToLast();202 cursor.moveToLast();
@@ -232,7 +233,7 @@
232 }233 }
233234
234 public void testEnqueueAlbumInPlaylist() {235 public void testEnqueueAlbumInPlaylist() {
235 Uri playlistUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");236 Uri playlistUri = Playlists.buildPlaylistUri("playlist2-playlist_id");
236 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);237 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
237 assertEquals("Wrong song count of playlist.", 1, songCount);238 assertEquals("Wrong song count of playlist.", 1, songCount);
238 239
@@ -244,10 +245,11 @@
244 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);245 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
245 assertEquals("Wrong new song count of playlist.", songCount + albumSongCount, newSongCount);246 assertEquals("Wrong new song count of playlist.", songCount + albumSongCount, newSongCount);
246 247
248 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");
247 Cursor cursor = null;249 Cursor cursor = null;
248 try {250 try {
249 String[] projection = new String[] { Songs.SONG_ID };251 String[] projection = new String[] { Songs.SONG_ID };
250 cursor = mResolver.query(playlistUri, projection,252 cursor = mResolver.query(playlistSongsUri, projection,
251 null, null, PlaylistSongs.DEFAULT_SORT);253 null, null, PlaylistSongs.DEFAULT_SORT);
252 if (cursor != null && cursor.isBeforeFirst()) {254 if (cursor != null && cursor.isBeforeFirst()) {
253 cursor.moveToFirst();255 cursor.moveToFirst();
@@ -298,7 +300,7 @@
298 }300 }
299 301
300 public void testEnqueueArtistInPlaylist() {302 public void testEnqueueArtistInPlaylist() {
301 Uri playlistUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");303 Uri playlistUri = Playlists.buildPlaylistUri("playlist2-playlist_id");
302 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);304 int songCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
303 assertEquals("Wrong song count of playlist.", 1, songCount);305 assertEquals("Wrong song count of playlist.", 1, songCount);
304 306
@@ -311,10 +313,11 @@
311 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);313 int newSongCount = MusicProviderUtils.getPlaylistSongCount(mContext, playlistUri);
312 assertEquals("Wrong new song count of playlist.", songCount + artistSongCount, newSongCount);314 assertEquals("Wrong new song count of playlist.", songCount + artistSongCount, newSongCount);
313 315
316 Uri playlistSongsUri = Playlists.buildPlaylistSongsUri("playlist2-playlist_id");
314 Cursor cursor = null;317 Cursor cursor = null;
315 try {318 try {
316 String[] projection = new String[] { Songs.SONG_ID };319 String[] projection = new String[] { Songs.SONG_ID };
317 cursor = mResolver.query(playlistUri, projection,320 cursor = mResolver.query(playlistSongsUri, projection,
318 null, null, PlaylistSongs.DEFAULT_SORT);321 null, null, PlaylistSongs.DEFAULT_SORT);
319 if (cursor != null && cursor.isBeforeFirst()) {322 if (cursor != null && cursor.isBeforeFirst()) {
320 cursor.moveToFirst();323 cursor.moveToFirst();
321324
=== modified file 'test/src/com/ubuntuone/android/music/ui/SongsPageTest.java'
--- test/src/com/ubuntuone/android/music/ui/SongsPageTest.java 2012-11-08 14:14:54 +0000
+++ test/src/com/ubuntuone/android/music/ui/SongsPageTest.java 2012-11-28 17:49:20 +0000
@@ -100,6 +100,8 @@
100 assertNotNull("PlayerActivity was not started",100 assertNotNull("PlayerActivity was not started",
101 mInstrumentation.checkMonitorHit(mPlayerActivityMonitor, 1));101 mInstrumentation.checkMonitorHit(mPlayerActivityMonitor, 1));
102 activity.finish();102 activity.finish();
103
104 mInstrumentation.waitForIdleSync();
103 }105 }
104106
105 public void testClearQueueAndPlaySongOnOnListItemClick() {107 public void testClearQueueAndPlaySongOnOnListItemClick() {
@@ -154,6 +156,8 @@
154 assertNotNull("PlayerActivity was not started",156 assertNotNull("PlayerActivity was not started",
155 mInstrumentation.checkMonitorHit(mPlayerActivityMonitor, 1));157 mInstrumentation.checkMonitorHit(mPlayerActivityMonitor, 1));
156 activity.finish();158 activity.finish();
159
160 mInstrumentation.waitForIdleSync();
157 }161 }
158 162
159 public void testOnStarContextItemSelected() {163 public void testOnStarContextItemSelected() {

Subscribers

People subscribed via source and target branches

to all changes: