Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2010 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 com.android.gallery3d.data;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentResolver;
     21 import android.database.Cursor;
     22 import android.database.sqlite.SQLiteDatabase;
     23 import android.database.sqlite.SQLiteQueryBuilder;
     24 import android.net.Uri;
     25 import android.os.Looper;
     26 import android.test.AndroidTestCase;
     27 import android.test.mock.MockContentProvider;
     28 import android.test.mock.MockContentResolver;
     29 import android.test.suitebuilder.annotation.MediumTest;
     30 import android.util.Log;
     31 
     32 import java.util.concurrent.CountDownLatch;
     33 import java.util.concurrent.TimeUnit;
     34 
     35 public class LocalDataTest extends AndroidTestCase {
     36     @SuppressWarnings("unused")
     37     private static final String TAG = "LocalDataTest";
     38     private static final long DEFAULT_TIMEOUT = 1000; // one second
     39 
     40     @MediumTest
     41     public void testLocalAlbum() throws Exception {
     42         new TestZeroImage().run();
     43         new TestOneImage().run();
     44         new TestMoreImages().run();
     45         new TestZeroVideo().run();
     46         new TestOneVideo().run();
     47         new TestMoreVideos().run();
     48         new TestDeleteOneImage().run();
     49         new TestDeleteOneAlbum().run();
     50     }
     51 
     52     abstract class TestLocalAlbumBase {
     53         private boolean mIsImage;
     54         protected GalleryAppStub mApp;
     55         protected LocalAlbumSet mAlbumSet;
     56 
     57         TestLocalAlbumBase(boolean isImage) {
     58             mIsImage = isImage;
     59         }
     60 
     61         public void run() throws Exception {
     62             SQLiteDatabase db = SQLiteDatabase.create(null);
     63             prepareData(db);
     64             mApp = newGalleryContext(db, Looper.getMainLooper());
     65             Path.clearAll();
     66             Path path = Path.fromString(
     67                     mIsImage ? "/local/image" : "/local/video");
     68             mAlbumSet = new LocalAlbumSet(path, mApp);
     69             mAlbumSet.reload();
     70             verifyResult();
     71         }
     72 
     73         abstract void prepareData(SQLiteDatabase db);
     74         abstract void verifyResult() throws Exception;
     75     }
     76 
     77     abstract class TestLocalImageAlbum extends TestLocalAlbumBase {
     78         TestLocalImageAlbum() {
     79             super(true);
     80         }
     81     }
     82 
     83     abstract class TestLocalVideoAlbum extends TestLocalAlbumBase {
     84         TestLocalVideoAlbum() {
     85             super(false);
     86         }
     87     }
     88 
     89     class TestZeroImage extends TestLocalImageAlbum {
     90         @Override
     91         public void prepareData(SQLiteDatabase db) {
     92             createImageTable(db);
     93         }
     94 
     95         @Override
     96         public void verifyResult() {
     97             assertEquals(0, mAlbumSet.getMediaItemCount());
     98             assertEquals(0, mAlbumSet.getSubMediaSetCount());
     99             assertEquals(0, mAlbumSet.getTotalMediaItemCount());
    100          }
    101     }
    102 
    103     class TestOneImage extends TestLocalImageAlbum {
    104         @Override
    105         public void prepareData(SQLiteDatabase db) {
    106             createImageTable(db);
    107             insertImageData(db);
    108         }
    109 
    110         @Override
    111         public void verifyResult() {
    112             assertEquals(0, mAlbumSet.getMediaItemCount());
    113             assertEquals(1, mAlbumSet.getSubMediaSetCount());
    114             assertEquals(1, mAlbumSet.getTotalMediaItemCount());
    115             MediaSet sub = mAlbumSet.getSubMediaSet(0);
    116             assertEquals(1, sub.getMediaItemCount());
    117             assertEquals(0, sub.getSubMediaSetCount());
    118             LocalMediaItem item = (LocalMediaItem) sub.getMediaItem(0, 1).get(0);
    119             assertEquals(1, item.id);
    120             assertEquals("IMG_0072", item.caption);
    121             assertEquals("image/jpeg", item.mimeType);
    122             assertEquals(12.0, item.latitude);
    123             assertEquals(34.0, item.longitude);
    124             assertEquals(0xD000, item.dateTakenInMs);
    125             assertEquals(1280395646L, item.dateAddedInSec);
    126             assertEquals(1275934796L, item.dateModifiedInSec);
    127             assertEquals("/mnt/sdcard/DCIM/100CANON/IMG_0072.JPG", item.filePath);
    128         }
    129     }
    130 
    131     class TestMoreImages extends TestLocalImageAlbum {
    132         @Override
    133         public void prepareData(SQLiteDatabase db) {
    134             // Albums are sorted by names, and items are sorted by
    135             // dateTimeTaken (descending)
    136             createImageTable(db);
    137             // bucket 0xB000
    138             insertImageData(db, 1000, 0xB000, "second");  // id 1
    139             insertImageData(db, 2000, 0xB000, "second");  // id 2
    140             // bucket 0xB001
    141             insertImageData(db, 3000, 0xB001, "first");   // id 3
    142         }
    143 
    144         @Override
    145         public void verifyResult() {
    146             assertEquals(0, mAlbumSet.getMediaItemCount());
    147             assertEquals(2, mAlbumSet.getSubMediaSetCount());
    148             assertEquals(3, mAlbumSet.getTotalMediaItemCount());
    149 
    150             MediaSet first = mAlbumSet.getSubMediaSet(0);
    151             assertEquals(1, first.getMediaItemCount());
    152             LocalMediaItem item = (LocalMediaItem) first.getMediaItem(0, 1).get(0);
    153             assertEquals(3, item.id);
    154             assertEquals(3000L, item.dateTakenInMs);
    155 
    156             MediaSet second = mAlbumSet.getSubMediaSet(1);
    157             assertEquals(2, second.getMediaItemCount());
    158             item = (LocalMediaItem) second.getMediaItem(0, 1).get(0);
    159             assertEquals(2, item.id);
    160             assertEquals(2000L, item.dateTakenInMs);
    161             item = (LocalMediaItem) second.getMediaItem(1, 1).get(0);
    162             assertEquals(1, item.id);
    163             assertEquals(1000L, item.dateTakenInMs);
    164         }
    165     }
    166 
    167     class OnContentDirtyLatch implements ContentListener {
    168         private CountDownLatch mLatch = new CountDownLatch(1);
    169 
    170         public void onContentDirty() {
    171             mLatch.countDown();
    172         }
    173 
    174         public boolean isOnContentDirtyBeCalled(long timeout)
    175                 throws InterruptedException {
    176             return mLatch.await(timeout, TimeUnit.MILLISECONDS);
    177         }
    178     }
    179 
    180     class TestDeleteOneAlbum extends TestLocalImageAlbum {
    181         @Override
    182         public void prepareData(SQLiteDatabase db) {
    183             // Albums are sorted by names, and items are sorted by
    184             // dateTimeTaken (descending)
    185             createImageTable(db);
    186             // bucket 0xB000
    187             insertImageData(db, 1000, 0xB000, "second");  // id 1
    188             insertImageData(db, 2000, 0xB000, "second");  // id 2
    189             // bucket 0xB001
    190             insertImageData(db, 3000, 0xB001, "first");   // id 3
    191         }
    192 
    193         @Override
    194         public void verifyResult() throws Exception {
    195             MediaSet sub = mAlbumSet.getSubMediaSet(1);  // "second"
    196             assertEquals(2, mAlbumSet.getSubMediaSetCount());
    197             OnContentDirtyLatch latch = new OnContentDirtyLatch();
    198             sub.addContentListener(latch);
    199             assertTrue((sub.getSupportedOperations() & MediaSet.SUPPORT_DELETE) != 0);
    200             sub.delete();
    201             mAlbumSet.fakeChange();
    202             latch.isOnContentDirtyBeCalled(DEFAULT_TIMEOUT);
    203             mAlbumSet.reload();
    204             assertEquals(1, mAlbumSet.getSubMediaSetCount());
    205         }
    206     }
    207 
    208     class TestDeleteOneImage extends TestLocalImageAlbum {
    209 
    210         @Override
    211         public void prepareData(SQLiteDatabase db) {
    212             createImageTable(db);
    213             insertImageData(db);
    214         }
    215 
    216         @Override
    217         public void verifyResult() {
    218             MediaSet sub = mAlbumSet.getSubMediaSet(0);
    219             LocalMediaItem item = (LocalMediaItem) sub.getMediaItem(0, 1).get(0);
    220             assertEquals(1, sub.getMediaItemCount());
    221             assertTrue((sub.getSupportedOperations() & MediaSet.SUPPORT_DELETE) != 0);
    222             sub.delete();
    223             sub.reload();
    224             assertEquals(0, sub.getMediaItemCount());
    225         }
    226     }
    227 
    228     static void createImageTable(SQLiteDatabase db) {
    229         // This is copied from MediaProvider
    230         db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
    231                 "_id INTEGER PRIMARY KEY," +
    232                 "_data TEXT," +
    233                 "_size INTEGER," +
    234                 "_display_name TEXT," +
    235                 "mime_type TEXT," +
    236                 "title TEXT," +
    237                 "date_added INTEGER," +
    238                 "date_modified INTEGER," +
    239                 "description TEXT," +
    240                 "picasa_id TEXT," +
    241                 "isprivate INTEGER," +
    242                 "latitude DOUBLE," +
    243                 "longitude DOUBLE," +
    244                 "datetaken INTEGER," +
    245                 "orientation INTEGER," +
    246                 "mini_thumb_magic INTEGER," +
    247                 "bucket_id TEXT," +
    248                 "bucket_display_name TEXT" +
    249                ");");
    250     }
    251 
    252     static void insertImageData(SQLiteDatabase db) {
    253         insertImageData(db, 0xD000, 0xB000, "name");
    254     }
    255 
    256     static void insertImageData(SQLiteDatabase db, long dateTaken,
    257             int bucketId, String bucketName) {
    258         db.execSQL("INSERT INTO images (title, mime_type, latitude, longitude, "
    259                 + "datetaken, date_added, date_modified, bucket_id, "
    260                 + "bucket_display_name, _data, orientation) "
    261                 + "VALUES ('IMG_0072', 'image/jpeg', 12, 34, "
    262                 + dateTaken + ", 1280395646, 1275934796, '" + bucketId + "', "
    263                 + "'" + bucketName + "', "
    264                 + "'/mnt/sdcard/DCIM/100CANON/IMG_0072.JPG', 0)");
    265     }
    266 
    267     class TestZeroVideo extends TestLocalVideoAlbum {
    268         @Override
    269         public void prepareData(SQLiteDatabase db) {
    270             createVideoTable(db);
    271         }
    272 
    273         @Override
    274         public void verifyResult() {
    275             assertEquals(0, mAlbumSet.getMediaItemCount());
    276             assertEquals(0, mAlbumSet.getSubMediaSetCount());
    277             assertEquals(0, mAlbumSet.getTotalMediaItemCount());
    278         }
    279     }
    280 
    281     class TestOneVideo extends TestLocalVideoAlbum {
    282         @Override
    283         public void prepareData(SQLiteDatabase db) {
    284             createVideoTable(db);
    285             insertVideoData(db);
    286         }
    287 
    288         @Override
    289         public void verifyResult() {
    290             assertEquals(0, mAlbumSet.getMediaItemCount());
    291             assertEquals(1, mAlbumSet.getSubMediaSetCount());
    292             assertEquals(1, mAlbumSet.getTotalMediaItemCount());
    293             MediaSet sub = mAlbumSet.getSubMediaSet(0);
    294             assertEquals(1, sub.getMediaItemCount());
    295             assertEquals(0, sub.getSubMediaSetCount());
    296             LocalMediaItem item = (LocalMediaItem) sub.getMediaItem(0, 1).get(0);
    297             assertEquals(1, item.id);
    298             assertEquals("VID_20100811_051413", item.caption);
    299             assertEquals("video/mp4", item.mimeType);
    300             assertEquals(11.0, item.latitude);
    301             assertEquals(22.0, item.longitude);
    302             assertEquals(0xD000, item.dateTakenInMs);
    303             assertEquals(1281503663L, item.dateAddedInSec);
    304             assertEquals(1281503662L, item.dateModifiedInSec);
    305             assertEquals("/mnt/sdcard/DCIM/Camera/VID_20100811_051413.3gp",
    306                     item.filePath);
    307         }
    308     }
    309 
    310     class TestMoreVideos extends TestLocalVideoAlbum {
    311         @Override
    312         public void prepareData(SQLiteDatabase db) {
    313             // Albums are sorted by names, and items are sorted by
    314             // dateTimeTaken (descending)
    315             createVideoTable(db);
    316             // bucket 0xB002
    317             insertVideoData(db, 1000, 0xB000, "second");  // id 1
    318             insertVideoData(db, 2000, 0xB000, "second");  // id 2
    319             // bucket 0xB001
    320             insertVideoData(db, 3000, 0xB001, "first");   // id 3
    321         }
    322 
    323         @Override
    324         public void verifyResult() {
    325             assertEquals(0, mAlbumSet.getMediaItemCount());
    326             assertEquals(2, mAlbumSet.getSubMediaSetCount());
    327             assertEquals(3, mAlbumSet.getTotalMediaItemCount());
    328 
    329             MediaSet first = mAlbumSet.getSubMediaSet(0);
    330             assertEquals(1, first.getMediaItemCount());
    331             LocalMediaItem item = (LocalMediaItem) first.getMediaItem(0, 1).get(0);
    332             assertEquals(3, item.id);
    333             assertEquals(3000L, item.dateTakenInMs);
    334 
    335             MediaSet second = mAlbumSet.getSubMediaSet(1);
    336             assertEquals(2, second.getMediaItemCount());
    337             item = (LocalMediaItem) second.getMediaItem(0, 1).get(0);
    338             assertEquals(2, item.id);
    339             assertEquals(2000L, item.dateTakenInMs);
    340             item = (LocalMediaItem) second.getMediaItem(1, 1).get(0);
    341             assertEquals(1, item.id);
    342             assertEquals(1000L, item.dateTakenInMs);
    343         }
    344     }
    345 
    346     static void createVideoTable(SQLiteDatabase db) {
    347         db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
    348                    "_id INTEGER PRIMARY KEY," +
    349                    "_data TEXT NOT NULL," +
    350                    "_display_name TEXT," +
    351                    "_size INTEGER," +
    352                    "mime_type TEXT," +
    353                    "date_added INTEGER," +
    354                    "date_modified INTEGER," +
    355                    "title TEXT," +
    356                    "duration INTEGER," +
    357                    "artist TEXT," +
    358                    "album TEXT," +
    359                    "resolution TEXT," +
    360                    "description TEXT," +
    361                    "isprivate INTEGER," +   // for YouTube videos
    362                    "tags TEXT," +           // for YouTube videos
    363                    "category TEXT," +       // for YouTube videos
    364                    "language TEXT," +       // for YouTube videos
    365                    "mini_thumb_data TEXT," +
    366                    "latitude DOUBLE," +
    367                    "longitude DOUBLE," +
    368                    "datetaken INTEGER," +
    369                    "mini_thumb_magic INTEGER" +
    370                    ");");
    371         db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
    372         db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
    373     }
    374 
    375     static void insertVideoData(SQLiteDatabase db) {
    376         insertVideoData(db, 0xD000, 0xB000, "name");
    377     }
    378 
    379     static void insertVideoData(SQLiteDatabase db, long dateTaken,
    380             int bucketId, String bucketName) {
    381         db.execSQL("INSERT INTO video (title, mime_type, latitude, longitude, "
    382                 + "datetaken, date_added, date_modified, bucket_id, "
    383                 + "bucket_display_name, _data, duration) "
    384                 + "VALUES ('VID_20100811_051413', 'video/mp4', 11, 22, "
    385                 + dateTaken + ", 1281503663, 1281503662, '" + bucketId + "', "
    386                 + "'" + bucketName + "', "
    387                 + "'/mnt/sdcard/DCIM/Camera/VID_20100811_051413.3gp', 2964)");
    388     }
    389 
    390     static GalleryAppStub newGalleryContext(SQLiteDatabase db, Looper mainLooper) {
    391         MockContentResolver cr = new MockContentResolver();
    392         ContentProvider cp = new DbContentProvider(db, cr);
    393         cr.addProvider("media", cp);
    394         return new GalleryAppMock(null, cr, mainLooper);
    395     }
    396 }
    397 
    398 class DbContentProvider extends MockContentProvider {
    399     private static final String TAG = "DbContentProvider";
    400     private SQLiteDatabase mDatabase;
    401     private ContentResolver mContentResolver;
    402 
    403     DbContentProvider(SQLiteDatabase db, ContentResolver cr) {
    404         mDatabase = db;
    405         mContentResolver = cr;
    406     }
    407 
    408     @Override
    409     public Cursor query(Uri uri, String[] projection,
    410             String selection, String[] selectionArgs, String sortOrder) {
    411         // This is a simplified version extracted from MediaProvider.
    412 
    413         String tableName = getTableName(uri);
    414         if (tableName == null) return null;
    415 
    416         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    417         qb.setTables(tableName);
    418 
    419         String groupBy = null;
    420         String limit = uri.getQueryParameter("limit");
    421 
    422         if (uri.getQueryParameter("distinct") != null) {
    423             qb.setDistinct(true);
    424         }
    425 
    426         Log.v(TAG, "query = " + qb.buildQuery(projection, selection,
    427                 selectionArgs, groupBy, null, sortOrder, limit));
    428 
    429         if (selectionArgs != null) {
    430             for (String s : selectionArgs) {
    431                 Log.v(TAG, "  selectionArgs = " + s);
    432             }
    433         }
    434 
    435         Cursor c = qb.query(mDatabase, projection, selection,
    436                 selectionArgs, groupBy, null, sortOrder, limit);
    437 
    438         return c;
    439     }
    440 
    441     @Override
    442     public int delete(Uri uri, String whereClause, String[] whereArgs) {
    443         Log.v(TAG, "delete " + uri + "," + whereClause + "," + whereArgs[0]);
    444         String tableName = getTableName(uri);
    445         if (tableName == null) return 0;
    446         int count = mDatabase.delete(tableName, whereClause, whereArgs);
    447         mContentResolver.notifyChange(uri, null);
    448         return count;
    449     }
    450 
    451     private String getTableName(Uri uri) {
    452         String uriString = uri.toString();
    453         if (uriString.startsWith("content://media/external/images/media")) {
    454             return "images";
    455         } else if (uriString.startsWith("content://media/external/video/media")) {
    456             return "video";
    457         } else {
    458             return null;
    459         }
    460     }
    461 }
    462