| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.cooliris.media; |
| |
| import java.util.ArrayList; |
| |
| public class MediaSet { |
| public static final int TYPE_SMART = 0; |
| public static final int TYPE_FOLDER = 1; |
| public static final int TYPE_USERDEFINED = 2; |
| |
| public long mId; |
| public String mName; |
| |
| public boolean mFlagForDelete; |
| |
| public boolean mHasImages; |
| public boolean mHasVideos; |
| |
| // The type of the media set. A smart media set is an automatically |
| // generated media set. For example, the most recently |
| // viewed items media set is a media set that gets populated by the contents |
| // of a folder. A user defined media set |
| // is a set that is made by the user. This would typically correspond to |
| // media items belonging to an event. |
| public int mType; |
| |
| // The min and max date taken and added at timestamps. |
| public long mMinTimestamp = Long.MAX_VALUE; |
| public long mMaxTimestamp = 0; |
| public long mMinAddedTimestamp = Long.MAX_VALUE; |
| public long mMaxAddedTimestamp = 0; |
| |
| // The latitude and longitude of the min latitude point. |
| public double mMinLatLatitude = LocationMediaFilter.LAT_MAX; |
| public double mMinLatLongitude; |
| // The latitude and longitude of the max latitude point. |
| public double mMaxLatLatitude = LocationMediaFilter.LAT_MIN; |
| public double mMaxLatLongitude; |
| // The latitude and longitude of the min longitude point. |
| public double mMinLonLatitude; |
| public double mMinLonLongitude = LocationMediaFilter.LON_MAX; |
| // The latitude and longitude of the max longitude point. |
| public double mMaxLonLatitude; |
| public double mMaxLonLongitude = LocationMediaFilter.LON_MIN; |
| |
| // Reverse geocoding the latitude, longitude and getting an address or |
| // location. |
| public String mReverseGeocodedLocation; |
| // Set to true if at least one item in the set has a valid latitude and |
| // longitude. |
| public boolean mLatLongDetermined = false; |
| public boolean mReverseGeocodedLocationComputed = false; |
| public boolean mReverseGeocodedLocationRequestMade = false; |
| |
| public String mTitleString; |
| public String mTruncTitleString; |
| public String mNoCountTitleString; |
| |
| public String mEditUri = null; |
| public long mPicasaAlbumId = Shared.INVALID; |
| public boolean mIsLocal = true; |
| |
| public DataSource mDataSource; |
| public boolean mSyncPending = false; |
| |
| private ArrayList<MediaItem> mItems; |
| private LongSparseArray<MediaItem> mItemsLookup; |
| private LongSparseArray<MediaItem> mItemsLookupVideo; |
| public int mNumItemsLoaded = 0; |
| // mNumExpectedItems is preset to how many items are expected to be in the |
| // set as it is used to visually |
| // display the number of items in the set and we don't want this display to |
| // keep changing as items get loaded. |
| private int mNumExpectedItems = 0; |
| private boolean mNumExpectedItemsCountAccurate = false; |
| private int mCurrentLocation = 0; |
| |
| public MediaSet() { |
| this(null); |
| } |
| |
| public MediaSet(DataSource dataSource) { |
| mItems = new ArrayList<MediaItem>(16); |
| mItemsLookup = new LongSparseArray<MediaItem>(); |
| mItemsLookup.clear(); |
| mItemsLookupVideo = new LongSparseArray<MediaItem>(); |
| mItemsLookupVideo.clear(); |
| mDataSource = dataSource; |
| // TODO(Venkat): Can we move away from this dummy item setup? |
| MediaItem item = new MediaItem(); |
| item.mId = Shared.INVALID; |
| item.mParentMediaSet = this; |
| mItems.add(item); |
| mNumExpectedItems = 16; |
| } |
| |
| /** |
| * @return underlying ArrayList of MediaItems. Use only for iteration (read |
| * operations) on the ArrayList. |
| */ |
| public ArrayList<MediaItem> getItems() { |
| return mItems; |
| } |
| |
| public void setNumExpectedItems(int numExpectedItems) { |
| mItems.ensureCapacity(numExpectedItems); |
| mNumExpectedItems = numExpectedItems; |
| mNumExpectedItemsCountAccurate = true; |
| } |
| |
| public int getNumExpectedItems() { |
| return mNumExpectedItems; |
| } |
| |
| public boolean setContainsValidItems() { |
| if (mNumExpectedItems == 0) |
| return false; |
| return true; |
| } |
| |
| public void updateNumExpectedItems() { |
| mNumExpectedItems = mNumItemsLoaded; |
| mNumExpectedItemsCountAccurate = true; |
| } |
| |
| public int getNumItems() { |
| return mItems.size(); |
| } |
| |
| public void clear() { |
| mItems.clear(); |
| MediaItem item = new MediaItem(); |
| item.mId = Shared.INVALID; |
| item.mParentMediaSet = this; |
| mItems.add(item); |
| mNumExpectedItems = 16; |
| refresh(); |
| mItemsLookup.clear(); |
| mItemsLookupVideo.clear(); |
| } |
| |
| /** |
| * Generates the label for the MediaSet. |
| */ |
| public void generateTitle(final boolean truncateTitle) { |
| if (mName == null) { |
| mName = ""; |
| } |
| String size = (mNumExpectedItemsCountAccurate) ? " (" + mNumExpectedItems + ")" : ""; |
| mTitleString = mName + size; |
| if (truncateTitle) { |
| int length = mName.length(); |
| mTruncTitleString = (length > 16) ? mName.substring(0, 12) + "..." + mName.substring(length - 4, length) + size : mName |
| + size; |
| mNoCountTitleString = mName; |
| } else { |
| mTruncTitleString = mTitleString; |
| } |
| } |
| |
| /** |
| * Adds a MediaItem to this set, and increments the load count. |
| * Additionally, it also recomputes the location bounds and time range of |
| * the media set. |
| */ |
| public void addItem(final MediaItem itemToAdd) { |
| // Important to not set the parentMediaSet in here as temporary |
| // MediaSet's are occasionally |
| // created and we do not want the MediaItem updated as a result of that. |
| if (itemToAdd == null) { |
| return; |
| } |
| final LongSparseArray<MediaItem> lookup = (itemToAdd.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup |
| : mItemsLookupVideo; |
| MediaItem lookupItem = lookup.get(itemToAdd.mId); |
| if (lookupItem != null && !lookupItem.mFilePath.equals(itemToAdd.mFilePath)) { |
| lookupItem = null; |
| } |
| final MediaItem item = (lookupItem == null) ? itemToAdd : lookupItem; |
| item.mFlagForDelete = false; |
| if (mItems.size() == 0) { |
| mItems.add(item); |
| } else if (mItems.get(0).mId == -1L) { |
| mItems.set(0, item); |
| } else { |
| if (mItems.size() > mCurrentLocation) { |
| mItems.set(mCurrentLocation, item); |
| } else { |
| mItems.add(mCurrentLocation, item); |
| } |
| } |
| if (item.mId != Shared.INVALID) { |
| if (lookupItem == null) { |
| lookup.put(item.mId, item); |
| } |
| ++mNumItemsLoaded; |
| ++mCurrentLocation; |
| } |
| if (item.isDateTakenValid()) { |
| long dateTaken = item.mDateTakenInMs; |
| if (dateTaken < mMinTimestamp) { |
| mMinTimestamp = dateTaken; |
| } |
| if (dateTaken > mMaxTimestamp) { |
| mMaxTimestamp = dateTaken; |
| } |
| } else if (item.isDateAddedValid()) { |
| long dateAdded = item.mDateAddedInSec * 1000; |
| if (dateAdded < mMinAddedTimestamp) { |
| mMinAddedTimestamp = dateAdded; |
| } |
| if (dateAdded > mMaxAddedTimestamp) { |
| mMaxAddedTimestamp = dateAdded; |
| } |
| } |
| |
| // Determining the latitude longitude bounds of the set and setting the |
| // location string. |
| if (!item.isLatLongValid()) { |
| return; |
| } |
| double itemLatitude = item.mLatitude; |
| double itemLongitude = item.mLongitude; |
| if (mMinLatLatitude > itemLatitude) { |
| mMinLatLatitude = itemLatitude; |
| mMinLatLongitude = itemLongitude; |
| mLatLongDetermined = true; |
| } |
| if (mMaxLatLatitude < itemLatitude) { |
| mMaxLatLatitude = itemLatitude; |
| mMaxLatLongitude = itemLongitude; |
| mLatLongDetermined = true; |
| } |
| if (mMinLonLongitude > itemLongitude) { |
| mMinLonLatitude = itemLatitude; |
| mMinLonLongitude = itemLongitude; |
| mLatLongDetermined = true; |
| } |
| if (mMaxLonLongitude < itemLongitude) { |
| mMaxLonLatitude = itemLatitude; |
| mMaxLonLongitude = itemLongitude; |
| mLatLongDetermined = true; |
| } |
| } |
| |
| /** |
| * Removes a MediaItem if present in the MediaSet. |
| * |
| * @return true if the item was removed, false if removal failed or item was |
| * not present in the set. |
| */ |
| public boolean removeItem(final MediaItem itemToRemove) { |
| synchronized (mItems) { |
| if (mItems.remove(itemToRemove)) { |
| --mNumExpectedItems; |
| --mNumItemsLoaded; |
| --mCurrentLocation; |
| final LongSparseArray<MediaItem> lookup = (itemToRemove.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup |
| : mItemsLookupVideo; |
| lookup.remove(itemToRemove.mId); |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| public void removeDuplicate(final MediaItem itemToRemove) { |
| synchronized (mItems) { |
| int numItems = mItems.size(); |
| boolean foundItem = false; |
| for (int i = 0; i < numItems; ++i) { |
| final MediaItem item = mItems.get(i); |
| if (item == itemToRemove) { |
| if (foundItem == false) { |
| foundItem = true; |
| } else { |
| mItems.remove(i); |
| --mNumExpectedItems; |
| --mNumItemsLoaded; |
| --mCurrentLocation; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @return true if this MediaSet contains the argument MediaItem. |
| */ |
| public boolean lookupContainsItem(final MediaItem item) { |
| final LongSparseArray<MediaItem> lookupTable = (item.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) ? mItemsLookup |
| : mItemsLookupVideo; |
| MediaItem lookUp = lookupTable.get(item.mId); |
| if (lookUp != null && lookUp.mFilePath.equals(item.mFilePath)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * @return true if the title string is truncated. |
| */ |
| public boolean isTruncated() { |
| return (mTitleString != null && !mTitleString.equals(mTruncTitleString)); |
| } |
| |
| /** |
| * @return true if timestamps are available for this set. |
| */ |
| public boolean areTimestampsAvailable() { |
| return (mMinTimestamp < Long.MAX_VALUE && mMaxTimestamp > 0); |
| } |
| |
| /** |
| * @return true if the added timestamps are available for this set. |
| */ |
| public boolean areAddedTimestampsAvailable() { |
| return (mMinAddedTimestamp < Long.MAX_VALUE && mMaxAddedTimestamp > 0); |
| } |
| |
| /** |
| * @return true if this set of items corresponds to Picassa items. |
| */ |
| public boolean isPicassaSet() { |
| // 2 cases:- |
| // 1. This set is just a Picassa Album, and all its items are therefore |
| // from Picassa. |
| // 2. This set is a random collection of items and each item is a |
| // Picassa item. |
| if (isPicassaAlbum()) { |
| return true; |
| } |
| int numItems = mItems.size(); |
| for (int i = 0; i < numItems; i++) { |
| if (!mItems.get(i).isPicassaItem()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * @return true if this set is a Picassa album. |
| */ |
| public boolean isPicassaAlbum() { |
| return (mPicasaAlbumId != Shared.INVALID); |
| } |
| |
| public void refresh() { |
| mNumItemsLoaded = 0; |
| mCurrentLocation = 0; |
| final ArrayList<MediaItem> items = mItems; |
| final int numItems = items.size(); |
| for (int i = 0; i < numItems; ++i) { |
| MediaItem item = items.get(i); |
| item.mFlagForDelete = true; |
| } |
| } |
| |
| public void checkForDeletedItems() { |
| final ArrayList<MediaItem> items = mItems; |
| final ArrayList<MediaItem> itemsToDelete = new ArrayList<MediaItem>(); |
| synchronized (items) { |
| final int numItems = items.size(); |
| for (int i = 0; i < numItems; ++i) { |
| MediaItem item = items.get(i); |
| if (item.mFlagForDelete) { |
| itemsToDelete.add(item); |
| } |
| } |
| } |
| final int numItemsToDelete = itemsToDelete.size(); |
| for (int i = 0; i < numItemsToDelete; ++i) { |
| removeItem(itemsToDelete.get(i)); |
| } |
| } |
| } |