blob: c4e4b3b3b5278eccaec9191c13b722a6c13b47c8 [file] [log] [blame]
/*
* Copyright (C) 2010 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.android.gallery3d.data;
import android.util.Log;
import java.util.Comparator;
import java.util.TreeMap;
import java.util.ArrayList;
import java.util.SortedMap;
// MergeAlbum merges items from two or more MediaSets. It uses a Comparator to
// determine the order of items. The items are assumed to be sorted in the input
// media sets (with the same order that the Comparator uses).
//
// This only handles MediaItems, not SubMediaSets.
public class MergeAlbum extends MediaSet implements MediaSet.MediaSetListener {
private static final String TAG = "MergeAlbum";
private final long mUniqueId;
private final int mPageSize;
private final Comparator<MediaItem> mComparator;
private final MediaSet[] mSources;
private final int mItemId;
private ArrayList<MediaSet> mSets;
private int mSize; // caches mSets.size()
private FetchCache[] mFetcher;
// mIndex maps global position to the position of each underlying media sets.
private TreeMap<Integer, int[]> mIndex;
public MergeAlbum(long uniqueId, int pageSize, Comparator<MediaItem> comparator,
MediaSet[] sources, int itemId) {
mUniqueId = uniqueId;
mPageSize = pageSize;
mComparator = comparator;
mSources = sources;
mItemId = itemId;
updateData();
}
public void updateData() {
ArrayList<MediaSet> matches = new ArrayList<MediaSet>();
// Find sources that have a sub media set with specified mItemId.
for (MediaSet set : mSources) {
for (int i = 0, n = set.getSubMediaSetCount(); i < n; i++) {
MediaSet subset = set.getSubMediaSet(i);
int itemId = DataManager.extractItemId(subset.getUniqueId());
if (itemId == mItemId) {
matches.add(subset);
}
}
}
// If the matches doesn't change, we don't need to update.
if (mSets != null) {
int n = matches.size();
if (n == mSize) {
int i;
for (i = 0; i < n; i++) {
if (matches.get(i) != mSets.get(i)) break;
}
if (i == n) return;
}
}
mSets = matches;
mSize = matches.size();
mFetcher = new FetchCache[mSize];
for (int i = 0; i < mSize; i++) {
MediaSet s = mSets.get(i);
s.setContentListener(this);
mFetcher[i] = new FetchCache(s, mPageSize);
}
mIndex = new TreeMap<Integer, int[]>();
mIndex.put(0, new int[mSize]);
}
private void invalidateCache() {
for (int i = 0; i < mSize; i++) {
mFetcher[i].invalidate();
}
mIndex.clear();
mIndex.put(0, new int[mSize]);
}
public long getUniqueId() {
return mUniqueId;
}
public String getName() {
return TAG;
}
public int getMediaItemCount() {
return getTotalMediaItemCount();
}
public ArrayList<MediaItem> getMediaItem(int start, int count) {
// First find the nearest mark position <= start.
SortedMap<Integer, int[]> head = mIndex.headMap(start + 1);
int markPos = head.lastKey();
int[] subPos = (int []) head.get(markPos).clone();
MediaItem[] slot = new MediaItem[mSize];
// fill all slots
for (int i = 0; i < mSize; i++) {
slot[i] = mFetcher[i].getItem(subPos[i]);
}
ArrayList<MediaItem> result = new ArrayList<MediaItem>();
for (int i = markPos; i < start + count; i++) {
int k = -1; // k points to the best slot up to now.
for (int j = 0; j < mSize; j++) {
if (slot[j] != null) {
if (k == -1 || mComparator.compare(slot[j], slot[k]) < 0) {
k = j;
}
}
}
// If we don't have anything, all streams are exhausted.
if (k == -1) break;
// Pick the best slot and refill it.
subPos[k]++;
if (i >= start) {
result.add(slot[k]);
}
slot[k] = mFetcher[k].getItem(subPos[k]);
// Periodically leave a mark in the index, so we can come back later.
if ((i + 1) % mPageSize == 0) {
mIndex.put(i + 1, (int[]) subPos.clone());
}
}
return result;
}
public int getTotalMediaItemCount() {
int count = 0;
for (MediaSet set : mSets) {
count += set.getTotalMediaItemCount();
}
return count;
}
public void reload() {
for (MediaSet set : mSets) {
set.reload();
}
}
public void onContentChanged() {
invalidateCache();
updateData();
if (mListener != null) {
mListener.onContentChanged();
}
}
}
class FetchCache {
private static final String TAG = "FetchCache";
private MediaSet mBaseSet;
private int mPageSize;
private ArrayList<MediaItem> mCache;
private int mStartPos;
FetchCache(MediaSet baseSet, int pageSize) {
mBaseSet = baseSet;
mPageSize = pageSize;
}
void invalidate() {
mCache = null;
}
MediaItem getItem(int index) {
boolean needLoading = false;
if (mCache == null) {
needLoading = true;
} else if (index < mStartPos || index >= mStartPos + mPageSize) {
needLoading = true;
}
if (needLoading) {
mCache = mBaseSet.getMediaItem(index, mPageSize);
mStartPos = index;
}
if (index < mStartPos || index >= mStartPos + mCache.size()) {
return null;
}
return mCache.get(index - mStartPos);
}
}