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