Home | History | Annotate | Download | only in media
      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 com.cooliris.media;
     18 
     19 import java.util.ArrayList;
     20 
     21 public class MediaSet {
     22     public static final int TYPE_SMART = 0;
     23     public static final int TYPE_FOLDER = 1;
     24     public static final int TYPE_USERDEFINED = 2;
     25 
     26     public long mId;
     27     public String mName;
     28 
     29     public boolean mFlagForDelete;
     30 
     31     public boolean mHasImages;
     32     public boolean mHasVideos;
     33 
     34     // The type of the media set. A smart media set is an automatically
     35     // generated media set. For example, the most recently
     36     // viewed items media set is a media set that gets populated by the contents
     37     // of a folder. A user defined media set
     38     // is a set that is made by the user. This would typically correspond to
     39     // media items belonging to an event.
     40     public int mType;
     41 
     42     // The min and max date taken and added at timestamps.
     43     public long mMinTimestamp = Long.MAX_VALUE;
     44     public long mMaxTimestamp = 0;
     45     public long mMinAddedTimestamp = Long.MAX_VALUE;
     46     public long mMaxAddedTimestamp = 0;
     47 
     48     // The latitude and longitude of the min latitude point.
     49     public double mMinLatLatitude = LocationMediaFilter.LAT_MAX;
     50     public double mMinLatLongitude;
     51     // The latitude and longitude of the max latitude point.
     52     public double mMaxLatLatitude = LocationMediaFilter.LAT_MIN;
     53     public double mMaxLatLongitude;
     54     // The latitude and longitude of the min longitude point.
     55     public double mMinLonLatitude;
     56     public double mMinLonLongitude = LocationMediaFilter.LON_MAX;
     57     // The latitude and longitude of the max longitude point.
     58     public double mMaxLonLatitude;
     59     public double mMaxLonLongitude = LocationMediaFilter.LON_MIN;
     60 
     61     // Reverse geocoding the latitude, longitude and getting an address or
     62     // location.
     63     public String mReverseGeocodedLocation;
     64     // Set to true if at least one item in the set has a valid latitude and
     65     // longitude.
     66     public boolean mLatLongDetermined = false;
     67     public boolean mReverseGeocodedLocationComputed = false;
     68     public boolean mReverseGeocodedLocationRequestMade = false;
     69 
     70     public String mTitleString;
     71     public String mTruncTitleString;
     72     public String mNoCountTitleString;
     73 
     74     public String mEditUri = null;
     75     public long mPicasaAlbumId = Shared.INVALID;
     76     public boolean mIsLocal = true;
     77 
     78     public DataSource mDataSource;
     79     public boolean mSyncPending = false;
     80 
     81     private ArrayList<MediaItem> mItems;
     82     private LongSparseArray<MediaItem> mItemsLookup;
     83     private LongSparseArray<MediaItem> mItemsLookupVideo;
     84     public int mNumItemsLoaded = 0;
     85     // mNumExpectedItems is preset to how many items are expected to be in the
     86     // set as it is used to visually
     87     // display the number of items in the set and we don't want this display to
     88     // keep changing as items get loaded.
     89     private int mNumExpectedItems = 0;
     90     private boolean mNumExpectedItemsCountAccurate = false;
     91     private int mCurrentLocation = 0;
     92 
     93     public MediaSet() {
     94         this(null);
     95     }
     96 
     97     public MediaSet(DataSource dataSource) {
     98         mItems = new ArrayList<MediaItem>(16);
     99         mItemsLookup = new LongSparseArray<MediaItem>();
    100         mItemsLookup.clear();
    101         mItemsLookupVideo = new LongSparseArray<MediaItem>();
    102         mItemsLookupVideo.clear();
    103         mDataSource = dataSource;
    104         // TODO(Venkat): Can we move away from this dummy item setup?
    105         MediaItem item = new MediaItem();
    106         item.mId = Shared.INVALID;
    107         item.mParentMediaSet = this;
    108         mItems.add(item);
    109         mNumExpectedItems = 16;
    110     }
    111 
    112     /**
    113      * @return underlying ArrayList of MediaItems. Use only for iteration (read
    114      *         operations) on the ArrayList.
    115      */
    116     public ArrayList<MediaItem> getItems() {
    117         return mItems;
    118     }
    119 
    120     public void setNumExpectedItems(int numExpectedItems) {
    121         mItems.ensureCapacity(numExpectedItems);
    122         mNumExpectedItems = numExpectedItems;
    123         mNumExpectedItemsCountAccurate = true;
    124     }
    125 
    126     public int getNumExpectedItems() {
    127         return mNumExpectedItems;
    128     }
    129 
    130     public boolean setContainsValidItems() {
    131         if (mNumExpectedItems == 0)
    132             return false;
    133         return true;
    134     }
    135 
    136     public void updateNumExpectedItems() {
    137         mNumExpectedItems = mNumItemsLoaded;
    138         mNumExpectedItemsCountAccurate = true;
    139     }
    140 
    141     public int getNumItems() {
    142         return mItems.size();
    143     }
    144 
    145     public void clear() {
    146         mItems.clear();
    147         MediaItem item = new MediaItem();
    148         item.mId = Shared.INVALID;
    149         item.mParentMediaSet = this;
    150         mItems.add(item);
    151         mNumExpectedItems = 16;
    152         refresh();
    153         mItemsLookup.clear();
    154         mItemsLookupVideo.clear();
    155     }
    156 
    157     /**
    158      * Generates the label for the MediaSet.
    159      */
    160     public void generateTitle(final boolean truncateTitle) {
    161         if (mName == null) {
    162             mName = "";
    163         }
    164         String size = (mNumExpectedItemsCountAccurate) ? "  (" + mNumExpectedItems + ")" : "";
    165         mTitleString = mName + size;
    166         if (truncateTitle) {
    167             int length = mName.length();
    168             mTruncTitleString = (length > 16) ? mName.substring(0, 12) + "..." + mName.substring(length - 4, length) + size : mName
    169                     + size;
    170             mNoCountTitleString = mName;
    171         } else {
    172             mTruncTitleString = mTitleString;
    173         }
    174     }
    175 
    176     /**
    177      * Adds a MediaItem to this set, and increments the load count.
    178      * Additionally, it also recomputes the location bounds and time range of
    179      * the media set.
    180      */
    181     public void addItem(final MediaItem itemToAdd) {
    182         // Important to not set the parentMediaSet in here as temporary
    183         // MediaSet's are occasionally
    184         // created and we do not want the MediaItem updated as a result of that.
    185         if (itemToAdd == null) {
    186             return;
    187         }
    188         final LongSparseArray<MediaItem> lookup = (itemToAdd.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup
    189                 : mItemsLookupVideo;
    190         MediaItem lookupItem = lookup.get(itemToAdd.mId);
    191         if (lookupItem != null && !lookupItem.mFilePath.equals(itemToAdd.mFilePath)) {
    192             lookupItem = null;
    193         }
    194         final MediaItem item = (lookupItem == null) ? itemToAdd : lookupItem;
    195         item.mFlagForDelete = false;
    196         if (mItems.size() == 0) {
    197             mItems.add(item);
    198         } else if (mItems.get(0).mId == -1L) {
    199             mItems.set(0, item);
    200         } else {
    201             if (mItems.size() > mCurrentLocation) {
    202                 mItems.set(mCurrentLocation, item);
    203             } else {
    204                 mItems.add(mCurrentLocation, item);
    205             }
    206         }
    207         if (item.mId != Shared.INVALID) {
    208             if (lookupItem == null) {
    209                 lookup.put(item.mId, item);
    210             }
    211             ++mNumItemsLoaded;
    212             ++mCurrentLocation;
    213         }
    214         if (item.isDateTakenValid()) {
    215             long dateTaken = item.mDateTakenInMs;
    216             if (dateTaken < mMinTimestamp) {
    217                 mMinTimestamp = dateTaken;
    218             }
    219             if (dateTaken > mMaxTimestamp) {
    220                 mMaxTimestamp = dateTaken;
    221             }
    222         } else if (item.isDateAddedValid()) {
    223             long dateAdded = item.mDateAddedInSec * 1000;
    224             if (dateAdded < mMinAddedTimestamp) {
    225                 mMinAddedTimestamp = dateAdded;
    226             }
    227             if (dateAdded > mMaxAddedTimestamp) {
    228                 mMaxAddedTimestamp = dateAdded;
    229             }
    230         }
    231 
    232         // Determining the latitude longitude bounds of the set and setting the
    233         // location string.
    234         if (!item.isLatLongValid()) {
    235             return;
    236         }
    237         double itemLatitude = item.mLatitude;
    238         double itemLongitude = item.mLongitude;
    239         if (mMinLatLatitude > itemLatitude) {
    240             mMinLatLatitude = itemLatitude;
    241             mMinLatLongitude = itemLongitude;
    242             mLatLongDetermined = true;
    243         }
    244         if (mMaxLatLatitude < itemLatitude) {
    245             mMaxLatLatitude = itemLatitude;
    246             mMaxLatLongitude = itemLongitude;
    247             mLatLongDetermined = true;
    248         }
    249         if (mMinLonLongitude > itemLongitude) {
    250             mMinLonLatitude = itemLatitude;
    251             mMinLonLongitude = itemLongitude;
    252             mLatLongDetermined = true;
    253         }
    254         if (mMaxLonLongitude < itemLongitude) {
    255             mMaxLonLatitude = itemLatitude;
    256             mMaxLonLongitude = itemLongitude;
    257             mLatLongDetermined = true;
    258         }
    259     }
    260 
    261     /**
    262      * Removes a MediaItem if present in the MediaSet.
    263      *
    264      * @return true if the item was removed, false if removal failed or item was
    265      *         not present in the set.
    266      */
    267     public boolean removeItem(final MediaItem itemToRemove) {
    268         synchronized (mItems) {
    269             if (mItems.remove(itemToRemove)) {
    270                 --mNumExpectedItems;
    271                 --mNumItemsLoaded;
    272                 --mCurrentLocation;
    273                 final LongSparseArray<MediaItem> lookup = (itemToRemove.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup
    274                         : mItemsLookupVideo;
    275                 lookup.remove(itemToRemove.mId);
    276                 return true;
    277             }
    278             return false;
    279         }
    280     }
    281 
    282     public void removeDuplicate(final MediaItem itemToRemove) {
    283         synchronized (mItems) {
    284             int numItems = mItems.size();
    285             boolean foundItem = false;
    286             for (int i = 0; i < numItems; ++i) {
    287                 final MediaItem item = mItems.get(i);
    288                 if (item == itemToRemove) {
    289                     if (foundItem == false) {
    290                         foundItem = true;
    291                     } else {
    292                         mItems.remove(i);
    293                         --mNumExpectedItems;
    294                         --mNumItemsLoaded;
    295                         --mCurrentLocation;
    296                         break;
    297                     }
    298                 }
    299             }
    300         }
    301     }
    302 
    303     /**
    304      * @return true if this MediaSet contains the argument MediaItem.
    305      */
    306     public boolean lookupContainsItem(final MediaItem item) {
    307         final LongSparseArray<MediaItem> lookupTable = (item.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup
    308                 : mItemsLookupVideo;
    309         MediaItem lookUp = lookupTable.get(item.mId);
    310         if (lookUp != null && lookUp.mFilePath.equals(item.mFilePath)) {
    311             return true;
    312         } else {
    313             return false;
    314         }
    315     }
    316 
    317     /**
    318      * @return true if the title string is truncated.
    319      */
    320     public boolean isTruncated() {
    321         return (mTitleString != null && !mTitleString.equals(mTruncTitleString));
    322     }
    323 
    324     /**
    325      * @return true if timestamps are available for this set.
    326      */
    327     public boolean areTimestampsAvailable() {
    328         return (mMinTimestamp < Long.MAX_VALUE && mMaxTimestamp > 0);
    329     }
    330 
    331     /**
    332      * @return true if the added timestamps are available for this set.
    333      */
    334     public boolean areAddedTimestampsAvailable() {
    335         return (mMinAddedTimestamp < Long.MAX_VALUE && mMaxAddedTimestamp > 0);
    336     }
    337 
    338     /**
    339      * @return true if this set of items corresponds to Picassa items.
    340      */
    341     public boolean isPicassaSet() {
    342         // 2 cases:-
    343         // 1. This set is just a Picassa Album, and all its items are therefore
    344         // from Picassa.
    345         // 2. This set is a random collection of items and each item is a
    346         // Picassa item.
    347         if (isPicassaAlbum()) {
    348             return true;
    349         }
    350         int numItems = mItems.size();
    351         for (int i = 0; i < numItems; i++) {
    352             if (!mItems.get(i).isPicassaItem()) {
    353                 return false;
    354             }
    355         }
    356         return true;
    357     }
    358 
    359     /**
    360      * @return true if this set is a Picassa album.
    361      */
    362     public boolean isPicassaAlbum() {
    363         return (mPicasaAlbumId != Shared.INVALID);
    364     }
    365 
    366     public void refresh() {
    367         mNumItemsLoaded = 0;
    368         mCurrentLocation = 0;
    369         final ArrayList<MediaItem> items = mItems;
    370         final int numItems = items.size();
    371         for (int i = 0; i < numItems; ++i) {
    372             MediaItem item = items.get(i);
    373             item.mFlagForDelete = true;
    374         }
    375     }
    376 
    377     public void checkForDeletedItems() {
    378         final ArrayList<MediaItem> items = mItems;
    379         final ArrayList<MediaItem> itemsToDelete = new ArrayList<MediaItem>();
    380         synchronized (items) {
    381             final int numItems = items.size();
    382             for (int i = 0; i < numItems; ++i) {
    383                 MediaItem item = items.get(i);
    384                 if (item.mFlagForDelete) {
    385                     itemsToDelete.add(item);
    386                 }
    387             }
    388         }
    389         final int numItemsToDelete = itemsToDelete.size();
    390         for (int i = 0; i < numItemsToDelete; ++i) {
    391             removeItem(itemsToDelete.get(i));
    392         }
    393     }
    394 }
    395