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 import android.provider.cts.R; 20 21 import android.content.ContentResolver; 22 import android.content.ContentUris; 23 import android.content.ContentValues; 24 import android.content.Context; 25 import android.database.Cursor; 26 import android.graphics.Bitmap; 27 import android.graphics.BitmapFactory; 28 import android.net.Uri; 29 import android.os.Environment; 30 import android.provider.MediaStore; 31 import android.provider.MediaStore.Images.Media; 32 import android.provider.MediaStore.Images.Thumbnails; 33 import android.provider.MediaStore.MediaColumns; 34 import android.test.InstrumentationTestCase; 35 36 import com.android.compatibility.common.util.FileCopyHelper; 37 38 import java.io.File; 39 import java.util.ArrayList; 40 import android.util.DisplayMetrics; 41 42 public class MediaStore_Images_ThumbnailsTest extends InstrumentationTestCase { 43 private ArrayList<Uri> mRowsAdded; 44 45 private Context mContext; 46 47 private ContentResolver mContentResolver; 48 49 private FileCopyHelper mHelper; 50 51 @Override 52 protected void tearDown() throws Exception { 53 for (Uri row : mRowsAdded) { 54 try { 55 mContentResolver.delete(row, null, null); 56 } catch (UnsupportedOperationException e) { 57 // There is no way to delete rows from table "thumbnails" of internals database. 58 // ignores the exception and make the loop goes on 59 } 60 } 61 62 mHelper.clear(); 63 super.tearDown(); 64 } 65 66 @Override 67 protected void setUp() throws Exception { 68 super.setUp(); 69 70 mContext = getInstrumentation().getTargetContext(); 71 mContentResolver = mContext.getContentResolver(); 72 73 mHelper = new FileCopyHelper(mContext); 74 mRowsAdded = new ArrayList<Uri>(); 75 } 76 77 public void testQueryInternalThumbnails() throws Exception { 78 Cursor c = Thumbnails.queryMiniThumbnails(mContentResolver, 79 Thumbnails.INTERNAL_CONTENT_URI, Thumbnails.MICRO_KIND, null); 80 int previousMicroKindCount = c.getCount(); 81 c.close(); 82 83 // add a thumbnail 84 String path = mHelper.copy(R.raw.scenery, "testThumbnails.jpg"); 85 ContentValues values = new ContentValues(); 86 values.put(Thumbnails.KIND, Thumbnails.MINI_KIND); 87 values.put(Thumbnails.DATA, path); 88 Uri uri = mContentResolver.insert(Thumbnails.INTERNAL_CONTENT_URI, values); 89 if (uri != null) { 90 mRowsAdded.add(uri); 91 } 92 93 // query with the uri of the thumbnail and the kind 94 c = Thumbnails.queryMiniThumbnails(mContentResolver, uri, Thumbnails.MINI_KIND, null); 95 c.moveToFirst(); 96 assertEquals(1, c.getCount()); 97 assertEquals(Thumbnails.MINI_KIND, c.getInt(c.getColumnIndex(Thumbnails.KIND))); 98 assertEquals(path, c.getString(c.getColumnIndex(Thumbnails.DATA))); 99 100 // query all thumbnails with other kind 101 c = Thumbnails.queryMiniThumbnails(mContentResolver, Thumbnails.INTERNAL_CONTENT_URI, 102 Thumbnails.MICRO_KIND, null); 103 assertEquals(previousMicroKindCount, c.getCount()); 104 c.close(); 105 106 // query without kind 107 c = Thumbnails.query(mContentResolver, uri, null); 108 assertEquals(1, c.getCount()); 109 c.moveToFirst(); 110 assertEquals(Thumbnails.MINI_KIND, c.getInt(c.getColumnIndex(Thumbnails.KIND))); 111 assertEquals(path, c.getString(c.getColumnIndex(Thumbnails.DATA))); 112 c.close(); 113 } 114 115 public void testQueryExternalMiniThumbnails() { 116 // insert the image by bitmap 117 BitmapFactory.Options opts = new BitmapFactory.Options(); 118 opts.inTargetDensity = DisplayMetrics.DENSITY_XHIGH; 119 Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery,opts); 120 String stringUrl = null; 121 try{ 122 stringUrl = Media.insertImage(mContentResolver, src, null, null); 123 } catch (UnsupportedOperationException e) { 124 // the tests will be aborted because the image will be put in sdcard 125 fail("There is no sdcard attached! " + e.getMessage()); 126 } 127 assertNotNull(stringUrl); 128 Uri stringUri = Uri.parse(stringUrl); 129 mRowsAdded.add(stringUri); 130 131 // get the original image id and path 132 Cursor c = mContentResolver.query(stringUri, 133 new String[]{ Media._ID, Media.DATA }, null, null, null); 134 c.moveToFirst(); 135 long imageId = c.getLong(c.getColumnIndex(Media._ID)); 136 String imagePath = c.getString(c.getColumnIndex(Media.DATA)); 137 c.close(); 138 139 assertTrue("image file does not exist", new File(imagePath).exists()); 140 141 String[] sizeProjection = new String[] { Thumbnails.WIDTH, Thumbnails.HEIGHT }; 142 c = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MINI_KIND, 143 sizeProjection); 144 assertEquals(1, c.getCount()); 145 assertTrue(c.moveToFirst()); 146 assertTrue(c.getLong(c.getColumnIndex(Thumbnails.WIDTH)) >= Math.min(src.getWidth(), 240)); 147 assertTrue(c.getLong(c.getColumnIndex(Thumbnails.HEIGHT)) >= Math.min(src.getHeight(), 240)); 148 c.close(); 149 c = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MICRO_KIND, 150 sizeProjection); 151 assertEquals(1, c.getCount()); 152 assertTrue(c.moveToFirst()); 153 assertEquals(50, c.getLong(c.getColumnIndex(Thumbnails.WIDTH))); 154 assertEquals(50, c.getLong(c.getColumnIndex(Thumbnails.HEIGHT))); 155 c.close(); 156 157 c = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MINI_KIND, 158 new String[] { Thumbnails._ID, Thumbnails.DATA, Thumbnails.IMAGE_ID}); 159 160 c.moveToNext(); 161 long img = c.getLong(c.getColumnIndex(Thumbnails.IMAGE_ID)); 162 assertEquals(imageId, img); 163 String thumbPath = c.getString(c.getColumnIndex(Thumbnails.DATA)); 164 assertTrue("thumbnail file does not exist", new File(thumbPath).exists()); 165 166 // deleting the image from the database also deletes the image file, and the 167 // corresponding entry in the thumbnail table, which in turn triggers deletion 168 // of the thumbnail file on disk 169 mContentResolver.delete(stringUri, null, null); 170 mRowsAdded.remove(stringUri); 171 172 assertFalse("image file should no longer exist", new File(imagePath).exists()); 173 174 Cursor c2 = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MINI_KIND, 175 new String[] { Thumbnails._ID, Thumbnails.DATA, Thumbnails.IMAGE_ID}); 176 assertEquals(0, c2.getCount()); 177 c2.close(); 178 179 assertFalse("thumbnail file should no longer exist", new File(thumbPath).exists()); 180 c.close(); 181 182 // insert image, then delete it via the files table 183 stringUrl = Media.insertImage(mContentResolver, src, null, null); 184 c = mContentResolver.query(Uri.parse(stringUrl), 185 new String[]{ Media._ID, Media.DATA}, null, null, null); 186 c.moveToFirst(); 187 imageId = c.getLong(c.getColumnIndex(Media._ID)); 188 imagePath = c.getString(c.getColumnIndex(Media.DATA)); 189 c.close(); 190 assertTrue("image file does not exist", new File(imagePath).exists()); 191 Uri fileuri = MediaStore.Files.getContentUri("external", imageId); 192 mContentResolver.delete(fileuri, null, null); 193 assertFalse("image file should no longer exist", new File(imagePath).exists()); 194 195 196 // insert image, then delete its thumbnail 197 stringUrl = Media.insertImage(mContentResolver, src, null, null); 198 c = mContentResolver.query(Uri.parse(stringUrl), 199 new String[]{ Media._ID, Media.DATA}, null, null, null); 200 c.moveToFirst(); 201 imageId = c.getLong(c.getColumnIndex(Media._ID)); 202 imagePath = c.getString(c.getColumnIndex(Media.DATA)); 203 c.close(); 204 c2 = Thumbnails.queryMiniThumbnail(mContentResolver, imageId, Thumbnails.MINI_KIND, 205 new String[] { Thumbnails._ID, Thumbnails.DATA, Thumbnails.IMAGE_ID}); 206 c2.moveToFirst(); 207 thumbPath = c2.getString(c2.getColumnIndex(Thumbnails.DATA)); 208 assertTrue("thumbnail file does not exist", new File(thumbPath).exists()); 209 210 Uri imguri = Uri.parse(stringUrl); 211 long imgid = ContentUris.parseId(imguri); 212 assertEquals(imgid, imageId); 213 mRowsAdded.add(imguri); 214 mContentResolver.delete(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, 215 MediaStore.Images.Thumbnails.IMAGE_ID + "=?", new String[]{ "" + imgid}); 216 assertFalse("thumbnail file should no longer exist", new File(thumbPath).exists()); 217 assertTrue("image file should still exist", new File(imagePath).exists()); 218 } 219 220 public void testGetContentUri() { 221 Cursor c = null; 222 assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("internal"), null, null, 223 null, null)); 224 c.close(); 225 assertNotNull(c = mContentResolver.query(Thumbnails.getContentUri("external"), null, null, 226 null, null)); 227 c.close(); 228 229 // can not accept any other volume names 230 String volume = "fakeVolume"; 231 assertNull(mContentResolver.query(Thumbnails.getContentUri(volume), null, null, null, 232 null)); 233 } 234 235 public void testStoreImagesMediaExternal() { 236 final String externalImgPath = Environment.getExternalStorageDirectory() + 237 "/testimage.jpg"; 238 final String externalImgPath2 = Environment.getExternalStorageDirectory() + 239 "/testimage1.jpg"; 240 ContentValues values = new ContentValues(); 241 values.put(Thumbnails.KIND, Thumbnails.FULL_SCREEN_KIND); 242 values.put(Thumbnails.IMAGE_ID, 0); 243 values.put(Thumbnails.HEIGHT, 480); 244 values.put(Thumbnails.WIDTH, 320); 245 values.put(Thumbnails.DATA, externalImgPath); 246 247 // insert 248 Uri uri = mContentResolver.insert(Thumbnails.EXTERNAL_CONTENT_URI, values); 249 assertNotNull(uri); 250 251 // query 252 Cursor c = mContentResolver.query(uri, null, null, null, null); 253 assertEquals(1, c.getCount()); 254 c.moveToFirst(); 255 long id = c.getLong(c.getColumnIndex(Thumbnails._ID)); 256 assertTrue(id > 0); 257 assertEquals(Thumbnails.FULL_SCREEN_KIND, c.getInt(c.getColumnIndex(Thumbnails.KIND))); 258 assertEquals(0, c.getLong(c.getColumnIndex(Thumbnails.IMAGE_ID))); 259 assertEquals(480, c.getInt(c.getColumnIndex(Thumbnails.HEIGHT))); 260 assertEquals(320, c.getInt(c.getColumnIndex(Thumbnails.WIDTH))); 261 assertEquals(externalImgPath, c.getString(c.getColumnIndex(Thumbnails.DATA))); 262 c.close(); 263 264 // update 265 values.clear(); 266 values.put(Thumbnails.KIND, Thumbnails.MICRO_KIND); 267 values.put(Thumbnails.IMAGE_ID, 1); 268 values.put(Thumbnails.HEIGHT, 50); 269 values.put(Thumbnails.WIDTH, 50); 270 values.put(Thumbnails.DATA, externalImgPath2); 271 assertEquals(1, mContentResolver.update(uri, values, null, null)); 272 273 // delete 274 assertEquals(1, mContentResolver.delete(uri, null, null)); 275 } 276 277 public void testThumbnailGenerationAndCleanup() throws Exception { 278 // insert an image 279 Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery); 280 Uri uri = Uri.parse(Media.insertImage(mContentResolver, src, "test", "test description")); 281 282 // query its thumbnail 283 Cursor c = mContentResolver.query( 284 Thumbnails.EXTERNAL_CONTENT_URI, 285 new String [] {Thumbnails.DATA}, 286 "image_id=?", 287 new String[] {uri.getLastPathSegment()}, 288 null /* sort */ 289 ); 290 assertTrue("couldn't find thumbnail", c.moveToNext()); 291 String path = c.getString(0); 292 c.close(); 293 assertTrue("thumbnail does not exist", new File(path).exists()); 294 295 // delete the source image and check that the thumbnail is gone too 296 mContentResolver.delete(uri, null /* where clause */, null /* where args */); 297 assertFalse("thumbnail still exists after source file delete", new File(path).exists()); 298 299 // insert again 300 uri = Uri.parse(Media.insertImage(mContentResolver, src, "test", "test description")); 301 302 // query its thumbnail again 303 c = mContentResolver.query( 304 Thumbnails.EXTERNAL_CONTENT_URI, 305 new String [] {Thumbnails.DATA}, 306 "image_id=?", 307 new String[] {uri.getLastPathSegment()}, 308 null /* sortOrder */ 309 ); 310 assertTrue("couldn't find thumbnail", c.moveToNext()); 311 path = c.getString(0); 312 c.close(); 313 assertTrue("thumbnail does not exist", new File(path).exists()); 314 315 // update the media type 316 ContentValues values = new ContentValues(); 317 values.put("media_type", 0); 318 assertEquals("unexpected number of updated rows", 319 1, mContentResolver.update(uri, values, null /* where */, null /* where args */)); 320 321 // image was marked as regular file in the database, which should have deleted its thumbnail 322 323 // query its thumbnail again 324 c = mContentResolver.query( 325 Thumbnails.EXTERNAL_CONTENT_URI, 326 new String [] {Thumbnails.DATA}, 327 "image_id=?", 328 new String[] {uri.getLastPathSegment()}, 329 null /* sort */ 330 ); 331 if (c != null) { 332 assertFalse("thumbnail entry exists for non-thumbnail file", c.moveToNext()); 333 c.close(); 334 } 335 assertFalse("thumbnail remains after source file type change", new File(path).exists()); 336 337 // check source no longer exists as image 338 c = mContentResolver.query(uri, 339 null /* projection */, null /* where */, null /* where args */, null /* sort */); 340 assertFalse("source entry should be gone", c.moveToNext()); 341 c.close(); 342 343 // check source still exists as file 344 Uri fileUri = ContentUris.withAppendedId( 345 MediaStore.Files.getContentUri("external"), 346 Long.valueOf(uri.getLastPathSegment())); 347 c = mContentResolver.query(fileUri, 348 null /* projection */, null /* where */, null /* where args */, null /* sort */); 349 assertTrue("source entry is gone", c.moveToNext()); 350 String sourcePath = c.getString(c.getColumnIndex("_data")); 351 c.close(); 352 353 // clean up 354 mContentResolver.delete(uri, null /* where */, null /* where args */); 355 new File(sourcePath).delete(); 356 } 357 358 public void testStoreImagesMediaInternal() { 359 // can not insert any data, so other operations can not be tested 360 try { 361 mContentResolver.insert(Thumbnails.INTERNAL_CONTENT_URI, new ContentValues()); 362 fail("Should throw UnsupportedOperationException when inserting into internal " 363 + "database"); 364 } catch (UnsupportedOperationException e) { 365 // expected 366 } 367 } 368 369 public void testThumbnailOrderedQuery() throws Exception { 370 Bitmap src = BitmapFactory.decodeResource(mContext.getResources(), R.raw.scenery); 371 Uri url[] = new Uri[3]; 372 try{ 373 for (int i = 0; i < url.length; i++) { 374 url[i] = Uri.parse(Media.insertImage(mContentResolver, src, null, null)); 375 long origId = Long.parseLong(url[i].getLastPathSegment()); 376 Bitmap foo = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, 377 origId, Thumbnails.MICRO_KIND, null); 378 assertNotNull(foo); 379 } 380 381 // remove one of the images, its thumbnail, and the thumbnail cache 382 Cursor c = mContentResolver.query(url[1], null, null, null, null); 383 assertEquals(1, c.getCount()); 384 c.moveToFirst(); 385 String path = c.getString(c.getColumnIndex(MediaColumns.DATA)); 386 long id = c.getLong(c.getColumnIndex(MediaColumns._ID)); 387 c.close(); 388 assertTrue(new File(path).delete()); 389 390 long removedId = Long.parseLong(url[1].getLastPathSegment()); 391 long remainingId1 = Long.parseLong(url[0].getLastPathSegment()); 392 long remainingId2 = Long.parseLong(url[2].getLastPathSegment()); 393 c = mContentResolver.query( 394 MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, null, 395 Thumbnails.IMAGE_ID + "=?", new String[] { Long.toString(removedId) }, null); 396 assertTrue(c.getCount() > 0); // one or more thumbnail kinds 397 while (c.moveToNext()) { 398 path = c.getString(c.getColumnIndex(MediaColumns.DATA)); 399 assertTrue(new File(path).delete()); 400 } 401 c.close(); 402 403 File thumbdir = new File(path).getParentFile(); 404 File[] list = thumbdir.listFiles(); 405 for (int i = 0; i < list.length; i++) { 406 if (list[i].getName().startsWith(".thumbdata")) { 407 assertTrue(list[i].delete()); 408 } 409 } 410 411 // check if a thumbnail is still being returned for the image that was removed 412 Bitmap foo = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, 413 removedId, Thumbnails.MICRO_KIND, null); 414 assertNull(foo); 415 416 for (String order: new String[] { " ASC", " DESC" }) { 417 c = mContentResolver.query( 418 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, 419 MediaColumns._ID + order); 420 while (c.moveToNext()) { 421 id = c.getLong(c.getColumnIndex(MediaColumns._ID)); 422 foo = MediaStore.Images.Thumbnails.getThumbnail( 423 mContentResolver, id, 424 MediaStore.Images.Thumbnails.MICRO_KIND, null); 425 if (id == removedId) { 426 assertNull("unexpected bitmap with" + order + " ordering", foo); 427 } else if (id == remainingId1 || id == remainingId2) { 428 assertNotNull("missing bitmap with" + order + " ordering", foo); 429 } 430 } 431 c.close(); 432 } 433 } catch (UnsupportedOperationException e) { 434 // the tests will be aborted because the image will be put in sdcard 435 fail("There is no sdcard attached! " + e.getMessage()); 436 } 437 } 438 } 439