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.Context;
20 import android.net.Uri;
21 
22 import com.android.gallery3d.app.GalleryApp;
23 
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 
27 public 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 
ClusterAlbumSet(Path path, GalleryApp application, MediaSet baseSet, int kind)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
getSubMediaSet(int index)46     public MediaSet getSubMediaSet(int index) {
47         return mAlbums.get(index);
48     }
49 
50     @Override
getSubMediaSetCount()51     public int getSubMediaSetCount() {
52         return mAlbums.size();
53     }
54 
55     @Override
getName()56     public String getName() {
57         return mBaseSet.getName();
58     }
59 
60     @Override
reload()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
onContentDirty()75     public void onContentDirty() {
76         notifyContentChanged();
77     }
78 
updateClusters()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 
updateClustersContents()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