1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.provider.cts; 18 19 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.provider.MediaStore; 25 import android.provider.MediaStore.Audio.Media; 26 import android.provider.MediaStore.Audio.Playlists; 27 import android.provider.MediaStore.Audio.Playlists.Members; 28 import android.provider.cts.MediaStoreAudioTestHelper.Audio1; 29 import android.provider.cts.MediaStoreAudioTestHelper.Audio2; 30 import android.provider.cts.MediaStoreAudioTestHelper.Audio3; 31 import android.provider.cts.MediaStoreAudioTestHelper.Audio4; 32 import android.provider.cts.MediaStoreAudioTestHelper.Audio5; 33 import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo; 34 import android.test.InstrumentationTestCase; 35 36 import java.util.regex.Pattern; 37 38 public class MediaStore_Audio_Playlists_MembersTest extends InstrumentationTestCase { 39 private String[] mAudioProjection = { 40 Members._ID, 41 Members.ALBUM, 42 Members.ALBUM_ID, 43 Members.ALBUM_KEY, 44 Members.ARTIST, 45 Members.ARTIST_ID, 46 Members.ARTIST_KEY, 47 Members.COMPOSER, 48 Members.DATA, 49 Members.DATE_ADDED, 50 Members.DATE_MODIFIED, 51 Members.DISPLAY_NAME, 52 Members.DURATION, 53 Members.IS_ALARM, 54 Members.IS_DRM, 55 Members.IS_MUSIC, 56 Members.IS_NOTIFICATION, 57 Members.IS_RINGTONE, 58 Members.MIME_TYPE, 59 Members.SIZE, 60 Members.TITLE, 61 Members.TITLE_KEY, 62 Members.TRACK, 63 Members.YEAR, 64 }; 65 66 private String[] mMembersProjection = { 67 Members._ID, 68 Members.AUDIO_ID, 69 Members.PLAYLIST_ID, 70 Members.PLAY_ORDER, 71 Members.ALBUM, 72 Members.ALBUM_ID, 73 Members.ALBUM_KEY, 74 Members.ARTIST, 75 Members.ARTIST_ID, 76 Members.ARTIST_KEY, 77 Members.COMPOSER, 78 Members.DATA, 79 Members.DATE_ADDED, 80 Members.DATE_MODIFIED, 81 Members.DISPLAY_NAME, 82 Members.DURATION, 83 Members.IS_ALARM, 84 Members.IS_DRM, 85 Members.IS_MUSIC, 86 Members.IS_NOTIFICATION, 87 Members.IS_RINGTONE, 88 Members.MIME_TYPE, 89 Members.SIZE, 90 Members.TITLE, 91 Members.TITLE_KEY, 92 Members.TRACK, 93 Members.YEAR, 94 }; 95 96 private ContentResolver mContentResolver; 97 98 private long mIdOfAudio1; 99 private long mIdOfAudio2; 100 private long mIdOfAudio3; 101 private long mIdOfAudio4; 102 private long mIdOfAudio5; 103 104 private long insertAudioItem(MockAudioMediaInfo which) { 105 Uri uri = which.insertToExternal(mContentResolver); 106 Cursor c = mContentResolver.query(uri, null, null, null, null); 107 c.moveToFirst(); 108 long id = c.getLong(c.getColumnIndex(Media._ID)); 109 c.close(); 110 return id; 111 } 112 113 @Override 114 protected void setUp() throws Exception { 115 super.setUp(); 116 117 mContentResolver = getInstrumentation().getContext().getContentResolver(); 118 mIdOfAudio1 = insertAudioItem(Audio1.getInstance()); 119 mIdOfAudio2 = insertAudioItem(Audio2.getInstance()); 120 mIdOfAudio3 = insertAudioItem(Audio3.getInstance()); 121 mIdOfAudio4 = insertAudioItem(Audio4.getInstance()); 122 mIdOfAudio5 = insertAudioItem(Audio5.getInstance()); 123 } 124 125 @Override 126 protected void tearDown() throws Exception { 127 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio1, null); 128 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio2, null); 129 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio3, null); 130 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio4, null); 131 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio5, null); 132 super.tearDown(); 133 } 134 135 public void testGetContentUri() { 136 assertEquals("content://media/external/audio/playlists/1337/members", 137 Members.getContentUri("external", 1337).toString()); 138 assertEquals("content://media/internal/audio/playlists/3007/members", 139 Members.getContentUri("internal", 3007).toString()); 140 } 141 142 private Uri insertPlaylistItem(Uri playlistMembersUri, long itemid, int order) { 143 ContentValues values = new ContentValues(); 144 values.put(Members.AUDIO_ID, itemid); 145 values.put(Members.PLAY_ORDER, order); 146 return mContentResolver.insert(playlistMembersUri, values); 147 } 148 149 /** 150 * check that the specified playlist contains the given members in the given order 151 */ 152 private void verifyPlaylist(Uri playlistMembersUri, long [] members, int [] ordering) { 153 Cursor c = mContentResolver.query(playlistMembersUri, 154 new String[] { Members.AUDIO_ID, Members.PLAY_ORDER }, 155 null, null, // selection, selection args 156 Members.PLAY_ORDER); 157 assertFalse("neither members nor ordering specified", 158 members == null && ordering == null); 159 if (members != null) { 160 assertEquals("members length doesn't match cursor length", 161 members.length, c.getCount()); 162 if (ordering != null) { 163 assertEquals("members and ordering must have same length", 164 members.length, ordering.length); 165 } 166 } 167 if (ordering != null) { 168 assertEquals("ordering length doesn't match cursor length", 169 ordering.length, c.getCount()); 170 } 171 while (c.moveToNext()) { 172 int pos = c.getPosition(); 173 if (members != null) { 174 assertEquals("mismatched member at position " + pos, 175 members[pos], c.getInt(c.getColumnIndex(Members.AUDIO_ID))); 176 } 177 if (ordering != null) { 178 assertEquals("mismatched ordering at position " + pos, 179 ordering[pos], c.getInt(c.getColumnIndex(Members.PLAY_ORDER))); 180 } 181 } 182 c.close(); 183 } 184 185 public void testStoreAudioPlaylistsMembersExternal() { 186 ContentValues values = new ContentValues(); 187 values.put(Playlists.NAME, "My favourites"); 188 values.put(Playlists.DATA, ""); 189 long dateAdded = System.currentTimeMillis(); 190 values.put(Playlists.DATE_ADDED, dateAdded); 191 long dateModified = System.currentTimeMillis(); 192 values.put(Playlists.DATE_MODIFIED, dateModified); 193 // insert 194 Uri uri = mContentResolver.insert(Playlists.EXTERNAL_CONTENT_URI, values); 195 assertNotNull(uri); 196 Cursor c = mContentResolver.query(uri, null, null, null, null); 197 c.moveToFirst(); 198 long playlistId = c.getLong(c.getColumnIndex(Playlists._ID)); 199 long playlist2Id = -1; // used later 200 201 // verify that the Uri has the correct format and playlist value 202 assertEquals(uri.toString(), "content://media/external/audio/playlists/" + playlistId); 203 204 // insert audio as the member of the playlist 205 Uri membersUri = Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 206 playlistId); 207 Uri audioUri = insertPlaylistItem(membersUri, mIdOfAudio1, 1); 208 209 assertNotNull(audioUri); 210 assertTrue(audioUri.toString().startsWith(membersUri.toString())); 211 212 try { 213 // query the audio info 214 c = mContentResolver.query(audioUri, mAudioProjection, null, null, null); 215 assertEquals(1, c.getCount()); 216 c.moveToFirst(); 217 long memberId = c.getLong(c.getColumnIndex(Members._ID)); 218 assertEquals(memberId, Long.parseLong(audioUri.getPathSegments().get(5))); 219 assertEquals(Audio1.EXTERNAL_DATA, c.getString(c.getColumnIndex(Members.DATA))); 220 assertTrue(c.getLong(c.getColumnIndex(Members.DATE_ADDED)) > 0); 221 assertEquals(Audio1.DATE_MODIFIED, c.getLong(c.getColumnIndex(Members.DATE_MODIFIED))); 222 assertEquals(Audio1.IS_DRM, c.getInt(c.getColumnIndex(Members.IS_DRM))); 223 assertEquals(Audio1.FILE_NAME, c.getString(c.getColumnIndex(Members.DISPLAY_NAME))); 224 assertEquals(Audio1.MIME_TYPE, c.getString(c.getColumnIndex(Members.MIME_TYPE))); 225 assertEquals(Audio1.SIZE, c.getInt(c.getColumnIndex(Members.SIZE))); 226 assertEquals(Audio1.TITLE, c.getString(c.getColumnIndex(Members.TITLE))); 227 assertEquals(Audio1.ALBUM, c.getString(c.getColumnIndex(Members.ALBUM))); 228 assertNotNull(c.getString(c.getColumnIndex(Members.ALBUM_KEY))); 229 assertTrue(c.getLong(c.getColumnIndex(Members.ALBUM_ID)) > 0); 230 assertEquals(Audio1.ARTIST, c.getString(c.getColumnIndex(Members.ARTIST))); 231 assertNotNull(c.getString(c.getColumnIndex(Members.ARTIST_KEY))); 232 assertTrue(c.getLong(c.getColumnIndex(Members.ARTIST_ID)) > 0); 233 assertEquals(Audio1.COMPOSER, c.getString(c.getColumnIndex(Members.COMPOSER))); 234 assertEquals(Audio1.DURATION, c.getLong(c.getColumnIndex(Members.DURATION))); 235 assertEquals(Audio1.IS_ALARM, c.getInt(c.getColumnIndex(Members.IS_ALARM))); 236 assertEquals(Audio1.IS_MUSIC, c.getInt(c.getColumnIndex(Members.IS_MUSIC))); 237 assertEquals(Audio1.IS_NOTIFICATION, 238 c.getInt(c.getColumnIndex(Members.IS_NOTIFICATION))); 239 assertEquals(Audio1.IS_RINGTONE, c.getInt(c.getColumnIndex(Members.IS_RINGTONE))); 240 assertEquals(Audio1.TRACK, c.getInt(c.getColumnIndex(Members.TRACK))); 241 assertEquals(Audio1.YEAR, c.getInt(c.getColumnIndex(Members.YEAR))); 242 assertNotNull(c.getString(c.getColumnIndex(Members.TITLE_KEY))); 243 c.close(); 244 245 // query the play order of the audio 246 verifyPlaylist(membersUri, new long [] {mIdOfAudio1}, new int [] {1}); 247 248 // update the member 249 values.clear(); 250 values.put(Members.PLAY_ORDER, 2); 251 values.put(Members.AUDIO_ID, mIdOfAudio2); 252 int result = mContentResolver.update(membersUri, values, Members.AUDIO_ID + "=" 253 + mIdOfAudio1, null); 254 assertEquals(1, result); 255 256 // query all info 257 c = mContentResolver.query(membersUri, mMembersProjection, null, null, 258 Members.DEFAULT_SORT_ORDER); 259 assertEquals(1, c.getCount()); 260 c.moveToFirst(); 261 assertEquals(2, c.getInt(c.getColumnIndex(Members.PLAY_ORDER))); 262 assertEquals(memberId, c.getLong(c.getColumnIndex(Members._ID))); 263 assertEquals(Audio2.EXTERNAL_DATA, c.getString(c.getColumnIndex(Members.DATA))); 264 assertTrue(c.getLong(c.getColumnIndex(Members.DATE_ADDED)) > 0); 265 assertEquals(Audio2.DATE_MODIFIED, c.getLong(c.getColumnIndex(Members.DATE_MODIFIED))); 266 assertEquals(Audio2.IS_DRM, c.getInt(c.getColumnIndex(Members.IS_DRM))); 267 assertEquals(Audio2.FILE_NAME, c.getString(c.getColumnIndex(Members.DISPLAY_NAME))); 268 assertEquals(Audio2.MIME_TYPE, c.getString(c.getColumnIndex(Members.MIME_TYPE))); 269 assertEquals(Audio2.SIZE, c.getInt(c.getColumnIndex(Members.SIZE))); 270 assertEquals(Audio2.TITLE, c.getString(c.getColumnIndex(Members.TITLE))); 271 assertEquals(Audio2.ALBUM, c.getString(c.getColumnIndex(Members.ALBUM))); 272 assertNotNull(c.getString(c.getColumnIndex(Members.ALBUM_KEY))); 273 assertTrue(c.getLong(c.getColumnIndex(Members.ALBUM_ID)) > 0); 274 assertEquals(Audio2.ARTIST, c.getString(c.getColumnIndex(Members.ARTIST))); 275 assertNotNull(c.getString(c.getColumnIndex(Members.ARTIST_KEY))); 276 assertTrue(c.getLong(c.getColumnIndex(Members.ARTIST_ID)) > 0); 277 assertEquals(Audio2.COMPOSER, c.getString(c.getColumnIndex(Members.COMPOSER))); 278 assertEquals(Audio2.DURATION, c.getLong(c.getColumnIndex(Members.DURATION))); 279 assertEquals(Audio2.IS_ALARM, c.getInt(c.getColumnIndex(Members.IS_ALARM))); 280 assertEquals(Audio2.IS_MUSIC, c.getInt(c.getColumnIndex(Members.IS_MUSIC))); 281 assertEquals(Audio2.IS_NOTIFICATION, 282 c.getInt(c.getColumnIndex(Members.IS_NOTIFICATION))); 283 assertEquals(Audio2.IS_RINGTONE, c.getInt(c.getColumnIndex(Members.IS_RINGTONE))); 284 assertEquals(Audio2.TRACK, c.getInt(c.getColumnIndex(Members.TRACK))); 285 assertEquals(Audio2.YEAR, c.getInt(c.getColumnIndex(Members.YEAR))); 286 assertNotNull(c.getString(c.getColumnIndex(Members.TITLE_KEY))); 287 c.close(); 288 289 // update the member back to its original state 290 values.clear(); 291 values.put(Members.PLAY_ORDER, 1); 292 values.put(Members.AUDIO_ID, mIdOfAudio1); 293 result = mContentResolver.update(membersUri, values, Members.AUDIO_ID + "=" 294 + mIdOfAudio2, null); 295 assertEquals(1, result); 296 297 // insert another member into the playlist 298 Uri audioUri2 = insertPlaylistItem(membersUri, mIdOfAudio2, 2); 299 // the playlist should now have id1 at position 1 and id2 at position2, check that 300 verifyPlaylist(membersUri, new long [] {mIdOfAudio1, mIdOfAudio2}, new int [] {1,2}); 301 302 // swap the items around 303 assertTrue(MediaStore.Audio.Playlists.Members.moveItem(mContentResolver, playlistId, 1, 0)); 304 305 // check the new positions 306 verifyPlaylist(membersUri, new long [] {mIdOfAudio2, mIdOfAudio1}, new int [] {1,2}); 307 308 // swap the items around in the other direction 309 assertTrue(MediaStore.Audio.Playlists.Members.moveItem(mContentResolver, playlistId, 0, 1)); 310 311 // check the positions again 312 verifyPlaylist(membersUri, new long [] {mIdOfAudio1, mIdOfAudio2}, new int [] {1,2}); 313 314 // insert a third item into the playlist 315 Uri audioUri3 = insertPlaylistItem(membersUri, mIdOfAudio3, 3); 316 // the playlist should now have id1 at position 1, id2 at position2, and 317 // id3 at position3, check that 318 verifyPlaylist(membersUri, 319 new long [] {mIdOfAudio1, mIdOfAudio2, mIdOfAudio3}, new int [] {1,2,3}); 320 321 // delete the middle item 322 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, Media._ID + "=" + mIdOfAudio2, null); 323 324 // check the remaining items are still in the right order, and the play_order of the 325 // last item has been adjusted 326 verifyPlaylist(membersUri, new long [] {mIdOfAudio1, mIdOfAudio3}, new int [] {1,2}); 327 328 // try to swap the remaining two items 329 assertTrue(MediaStore.Audio.Playlists.Members.moveItem(mContentResolver, playlistId, 1, 0)); 330 331 // check that they're swapped 332 verifyPlaylist(membersUri, new long [] {mIdOfAudio3, mIdOfAudio1}, new int [] {1,2}); 333 334 // add 3 items, do some more moving and checking 335 mIdOfAudio2 = insertAudioItem(Audio2.getInstance()); 336 insertPlaylistItem(membersUri, mIdOfAudio2, 3); 337 insertPlaylistItem(membersUri, mIdOfAudio4, 4); 338 insertPlaylistItem(membersUri, mIdOfAudio5, 5); 339 verifyPlaylist(membersUri, 340 new long [] {mIdOfAudio3, mIdOfAudio1, mIdOfAudio2, mIdOfAudio4, mIdOfAudio5}, 341 new int[] {1, 2, 3, 4, 5}); 342 assertTrue(MediaStore.Audio.Playlists.Members.moveItem(mContentResolver, playlistId, 1, 3)); 343 verifyPlaylist(membersUri, 344 new long [] {mIdOfAudio3, mIdOfAudio2, mIdOfAudio4, mIdOfAudio1, mIdOfAudio5}, 345 new int[] {1, 2, 3, 4, 5}); 346 c = mContentResolver.query(membersUri, null, null, null, null); 347 c.close(); 348 349 // create another playlist 350 values.clear(); 351 values.put(Playlists.NAME, "My favourites 2"); 352 values.put(Playlists.DATA, ""); 353 values.put(Playlists.DATE_ADDED, dateAdded); 354 values.put(Playlists.DATE_MODIFIED, dateModified); 355 // insert 356 uri = mContentResolver.insert(Playlists.EXTERNAL_CONTENT_URI, values); 357 assertNotNull(uri); 358 c = mContentResolver.query(uri, null, null, null, null); 359 c.moveToFirst(); 360 playlist2Id = c.getLong(c.getColumnIndex(Playlists._ID)); 361 c.close(); 362 363 // insert audio into 2nd playlist 364 Uri members2Uri = Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 365 playlist2Id); 366 Uri audio2Uri = insertPlaylistItem(members2Uri, mIdOfAudio1, 1); 367 368 c = mContentResolver.query(membersUri, null, null, null, null); 369 int allcolscount = c.getCount(); 370 c.close(); 371 c = mContentResolver.query(membersUri, 372 new String[] { Members.AUDIO_ID, Members.PLAY_ORDER }, null, null, null); 373 int somecolscount = c.getCount(); 374 c.close(); 375 assertEquals("Different count depending on columns", allcolscount, somecolscount); 376 377 // check that the audio exists in both playlist 378 c = mContentResolver.query(membersUri, null, null, null, null); 379 assertEquals(5, c.getCount()); 380 int cnt = 0; 381 int colidx = c.getColumnIndex(Members.AUDIO_ID); 382 assertTrue(colidx >= 0); 383 while(c.moveToNext()) { 384 if (c.getLong(colidx) == mIdOfAudio1) { 385 cnt++; 386 } 387 } 388 assertEquals(1, cnt); 389 c.close(); 390 c = mContentResolver.query(members2Uri, null, null, null, null); 391 assertEquals(1, c.getCount()); 392 cnt = 0; 393 while(c.moveToNext()) { 394 if (c.getLong(colidx) == mIdOfAudio1) { 395 cnt++; 396 } 397 } 398 assertEquals(1, cnt); 399 c.close(); 400 401 // delete the members 402 result = mContentResolver.delete(membersUri, null, null); 403 assertEquals(5, result); 404 result = mContentResolver.delete(members2Uri, null, null); 405 assertEquals(1, result); 406 407 // insert again, then verify that deleting the audio entry cleans up its playlist member 408 // entry as well 409 membersUri = Members.getContentUri(MediaStoreAudioTestHelper.EXTERNAL_VOLUME_NAME, 410 playlistId); 411 audioUri = insertPlaylistItem(membersUri, mIdOfAudio1, 1); 412 assertNotNull(audioUri); 413 // Query members of the playlist 414 c = mContentResolver.query(membersUri, 415 new String[] { Members.AUDIO_ID, Members.PLAYLIST_ID}, null, null, null); 416 colidx = c.getColumnIndex(Members.AUDIO_ID); 417 cnt = 0; 418 // The song should appear only once, for the playlist we used when inserting it 419 while(c.moveToNext()) { 420 if (c.getLong(colidx) == mIdOfAudio1) { 421 cnt++; 422 assertEquals(playlistId, c.getLong(c.getColumnIndex(Members.PLAYLIST_ID))); 423 } 424 } 425 assertEquals(1, cnt); 426 c.close(); 427 mContentResolver.delete(Media.EXTERNAL_CONTENT_URI, 428 Media._ID + "=" + mIdOfAudio1, null); 429 // Query members of the playlist 430 c = mContentResolver.query(membersUri, 431 new String[] { Members.AUDIO_ID, Members.PLAYLIST_ID}, null, null, null); 432 colidx = c.getColumnIndex(Members.AUDIO_ID); 433 cnt = 0; 434 // The song should no longer appear in the playlist 435 while(c.moveToNext()) { 436 if (c.getLong(colidx) == mIdOfAudio1) { 437 cnt++; 438 } 439 } 440 assertEquals(0, cnt); 441 c.close(); 442 443 } finally { 444 // delete the playlists 445 mContentResolver.delete(Playlists.EXTERNAL_CONTENT_URI, 446 Playlists._ID + "=" + playlistId, null); 447 if (playlist2Id >= 0) { 448 mContentResolver.delete(Playlists.EXTERNAL_CONTENT_URI, 449 Playlists._ID + "=" + playlist2Id, null); 450 } 451 } 452 } 453 454 public void testStoreAudioPlaylistsMembersInternal() { 455 ContentValues values = new ContentValues(); 456 values.put(Playlists.NAME, "My favourites"); 457 values.put(Playlists.DATA, "/data/data/android.provider.cts/files/my_favorites.pl"); 458 long dateAdded = System.currentTimeMillis(); 459 values.put(Playlists.DATE_ADDED, dateAdded); 460 long dateModified = System.currentTimeMillis(); 461 values.put(Playlists.DATE_MODIFIED, dateModified); 462 // insert 463 Uri uri = mContentResolver.insert(Playlists.INTERNAL_CONTENT_URI, values); 464 assertNotNull(uri); 465 466 try { 467 assertTrue(Pattern.matches("content://media/internal/audio/playlists/\\d+", 468 uri.toString())); 469 } finally { 470 // delete the playlists 471 assertEquals(1, mContentResolver.delete(uri, null, null)); 472 } 473 } 474 } 475