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
17package com.android.gallery3d.data;
18
19import android.content.Context;
20import android.net.Uri;
21
22import com.android.gallery3d.app.GalleryApp;
23
24import java.util.ArrayList;
25import java.util.HashSet;
26
27public class ClusterAlbumSet extends MediaSet implements ContentListener {
28    @SuppressWarnings("unused")
29    private static final String TAG = "ClusterAlbumSet";
30    private GalleryApp mApplication;
31    private MediaSet mBaseSet;
32    private int mKind;
33    private ArrayList<ClusterAlbum> mAlbums = new ArrayList<ClusterAlbum>();
34    private boolean mFirstReloadDone;
35
36    public ClusterAlbumSet(Path path, GalleryApp application,
37            MediaSet baseSet, int kind) {
38        super(path, INVALID_DATA_VERSION);
39        mApplication = application;
40        mBaseSet = baseSet;
41        mKind = kind;
42        baseSet.addContentListener(this);
43    }
44
45    @Override
46    public MediaSet getSubMediaSet(int index) {
47        return mAlbums.get(index);
48    }
49
50    @Override
51    public int getSubMediaSetCount() {
52        return mAlbums.size();
53    }
54
55    @Override
56    public String getName() {
57        return mBaseSet.getName();
58    }
59
60    @Override
61    public long reload() {
62        if (mBaseSet.reload() > mDataVersion) {
63            if (mFirstReloadDone) {
64                updateClustersContents();
65            } else {
66                updateClusters();
67                mFirstReloadDone = true;
68            }
69            mDataVersion = nextVersionNumber();
70        }
71        return mDataVersion;
72    }
73
74    @Override
75    public void onContentDirty() {
76        notifyContentChanged();
77    }
78
79    private void updateClusters() {
80        mAlbums.clear();
81        Clustering clustering;
82        Context context = mApplication.getAndroidContext();
83        switch (mKind) {
84            case ClusterSource.CLUSTER_ALBUMSET_TIME:
85                clustering = new TimeClustering(context);
86                break;
87            case ClusterSource.CLUSTER_ALBUMSET_LOCATION:
88                clustering = new LocationClustering(context);
89                break;
90            case ClusterSource.CLUSTER_ALBUMSET_TAG:
91                clustering = new TagClustering(context);
92                break;
93            case ClusterSource.CLUSTER_ALBUMSET_FACE:
94                clustering = new FaceClustering(context);
95                break;
96            default: /* CLUSTER_ALBUMSET_SIZE */
97                clustering = new SizeClustering(context);
98                break;
99        }
100
101        clustering.run(mBaseSet);
102        int n = clustering.getNumberOfClusters();
103        DataManager dataManager = mApplication.getDataManager();
104        for (int i = 0; i < n; i++) {
105            Path childPath;
106            String childName = clustering.getClusterName(i);
107            if (mKind == ClusterSource.CLUSTER_ALBUMSET_TAG) {
108                childPath = mPath.getChild(Uri.encode(childName));
109            } else if (mKind == ClusterSource.CLUSTER_ALBUMSET_SIZE) {
110                long minSize = ((SizeClustering) clustering).getMinSize(i);
111                childPath = mPath.getChild(minSize);
112            } else {
113                childPath = mPath.getChild(i);
114            }
115
116            ClusterAlbum album;
117            synchronized (DataManager.LOCK) {
118                album = (ClusterAlbum) dataManager.peekMediaObject(childPath);
119                if (album == null) {
120                    album = new ClusterAlbum(childPath, dataManager, this);
121                }
122            }
123            album.setMediaItems(clustering.getCluster(i));
124            album.setName(childName);
125            album.setCoverMediaItem(clustering.getClusterCover(i));
126            mAlbums.add(album);
127        }
128    }
129
130    private void updateClustersContents() {
131        final HashSet<Path> existing = new HashSet<Path>();
132        mBaseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() {
133            @Override
134            public void consume(int index, MediaItem item) {
135                existing.add(item.getPath());
136            }
137        });
138
139        int n = mAlbums.size();
140
141        // The loop goes backwards because we may remove empty albums from
142        // mAlbums.
143        for (int i = n - 1; i >= 0; i--) {
144            ArrayList<Path> oldPaths = mAlbums.get(i).getMediaItems();
145            ArrayList<Path> newPaths = new ArrayList<Path>();
146            int m = oldPaths.size();
147            for (int j = 0; j < m; j++) {
148                Path p = oldPaths.get(j);
149                if (existing.contains(p)) {
150                    newPaths.add(p);
151                }
152            }
153            mAlbums.get(i).setMediaItems(newPaths);
154            if (newPaths.isEmpty()) {
155                mAlbums.remove(i);
156            }
157        }
158    }
159}
160