• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app;
18 
19 import com.android.gallery3d.R;
20 import com.android.gallery3d.data.MediaObject;
21 import com.android.gallery3d.data.Path;
22 
23 // This class handles filtering and clustering.
24 //
25 // We allow at most only one filter operation at a time (Currently it
26 // doesn't make sense to use more than one). Also each clustering operation
27 // can be applied at most once. In addition, there is one more constraint
28 // ("fixed set constraint") described below.
29 //
30 // A clustered album (not including album set) and its base sets are fixed.
31 // For example,
32 //
33 // /cluster/{base_set}/time/7
34 //
35 // This set and all sets inside base_set (recursively) are fixed because
36 // 1. We can not change this set to use another clustering condition (like
37 //    changing "time" to "location").
38 // 2. Neither can we change any set in the base_set.
39 // The reason is in both cases the 7th set may not exist in the new clustering.
40 // ---------------------
41 // newPath operation: create a new path based on a source path and put an extra
42 // condition on top of it:
43 //
44 // T = newFilterPath(S, filterType);
45 // T = newClusterPath(S, clusterType);
46 //
47 // Similar functions can be used to replace the current condition (if there is one).
48 //
49 // T = switchFilterPath(S, filterType);
50 // T = switchClusterPath(S, clusterType);
51 //
52 // For all fixed set in the path defined above, if some clusterType and
53 // filterType are already used, they cannot not be used as parameter for these
54 // functions. setupMenuItems() makes sure those types cannot be selected.
55 //
56 public class FilterUtils {
57     private static final String TAG = "FilterUtils";
58 
59     public static final int CLUSTER_BY_ALBUM = 1;
60     public static final int CLUSTER_BY_TIME = 2;
61     public static final int CLUSTER_BY_LOCATION = 4;
62     public static final int CLUSTER_BY_TAG = 8;
63     public static final int CLUSTER_BY_SIZE = 16;
64     public static final int CLUSTER_BY_FACE = 32;
65 
66     public static final int FILTER_IMAGE_ONLY = 1;
67     public static final int FILTER_VIDEO_ONLY = 2;
68     public static final int FILTER_ALL = 4;
69 
70     // These are indices of the return values of getAppliedFilters().
71     // The _F suffix means "fixed".
72     private static final int CLUSTER_TYPE = 0;
73     private static final int FILTER_TYPE = 1;
74     private static final int CLUSTER_TYPE_F = 2;
75     private static final int FILTER_TYPE_F = 3;
76     private static final int CLUSTER_CURRENT_TYPE = 4;
77     private static final int FILTER_CURRENT_TYPE = 5;
78 
setupMenuItems(GalleryActionBar model, Path path, boolean inAlbum)79     public static void setupMenuItems(GalleryActionBar model, Path path, boolean inAlbum) {
80         int[] result = new int[6];
81         getAppliedFilters(path, result);
82         int ctype = result[CLUSTER_TYPE];
83         int ftype = result[FILTER_TYPE];
84         int ftypef = result[FILTER_TYPE_F];
85         int ccurrent = result[CLUSTER_CURRENT_TYPE];
86         int fcurrent = result[FILTER_CURRENT_TYPE];
87 
88         setMenuItemApplied(model, CLUSTER_BY_TIME,
89                 (ctype & CLUSTER_BY_TIME) != 0, (ccurrent & CLUSTER_BY_TIME) != 0);
90         setMenuItemApplied(model, CLUSTER_BY_LOCATION,
91                 (ctype & CLUSTER_BY_LOCATION) != 0, (ccurrent & CLUSTER_BY_LOCATION) != 0);
92         setMenuItemApplied(model, CLUSTER_BY_TAG,
93                 (ctype & CLUSTER_BY_TAG) != 0, (ccurrent & CLUSTER_BY_TAG) != 0);
94         setMenuItemApplied(model, CLUSTER_BY_FACE,
95                 (ctype & CLUSTER_BY_FACE) != 0, (ccurrent & CLUSTER_BY_FACE) != 0);
96 
97         model.setClusterItemVisibility(CLUSTER_BY_ALBUM, !inAlbum || ctype == 0);
98 
99         setMenuItemApplied(model, R.id.action_cluster_album, ctype == 0,
100                 ccurrent == 0);
101 
102         // A filtering is available if it's not applied, and the old filtering
103         // (if any) is not fixed.
104         setMenuItemAppliedEnabled(model, R.string.show_images_only,
105                 (ftype & FILTER_IMAGE_ONLY) != 0,
106                 (ftype & FILTER_IMAGE_ONLY) == 0 && ftypef == 0,
107                 (fcurrent & FILTER_IMAGE_ONLY) != 0);
108         setMenuItemAppliedEnabled(model, R.string.show_videos_only,
109                 (ftype & FILTER_VIDEO_ONLY) != 0,
110                 (ftype & FILTER_VIDEO_ONLY) == 0 && ftypef == 0,
111                 (fcurrent & FILTER_VIDEO_ONLY) != 0);
112         setMenuItemAppliedEnabled(model, R.string.show_all,
113                 ftype == 0, ftype != 0 && ftypef == 0, fcurrent == 0);
114     }
115 
116     // Gets the filters applied in the path.
getAppliedFilters(Path path, int[] result)117     private static void getAppliedFilters(Path path, int[] result) {
118         getAppliedFilters(path, result, false);
119     }
120 
getAppliedFilters(Path path, int[] result, boolean underCluster)121     private static void getAppliedFilters(Path path, int[] result, boolean underCluster) {
122         String[] segments = path.split();
123         // Recurse into sub media sets.
124         for (int i = 0; i < segments.length; i++) {
125             if (segments[i].startsWith("{")) {
126                 String[] sets = Path.splitSequence(segments[i]);
127                 for (int j = 0; j < sets.length; j++) {
128                     Path sub = Path.fromString(sets[j]);
129                     getAppliedFilters(sub, result, underCluster);
130                 }
131             }
132         }
133 
134         // update current selection
135         if (segments[0].equals("cluster")) {
136             // if this is a clustered album, set underCluster to true.
137             if (segments.length == 4) {
138                 underCluster = true;
139             }
140 
141             int ctype = toClusterType(segments[2]);
142             result[CLUSTER_TYPE] |= ctype;
143             result[CLUSTER_CURRENT_TYPE] = ctype;
144             if (underCluster) {
145                 result[CLUSTER_TYPE_F] |= ctype;
146             }
147         }
148     }
149 
toClusterType(String s)150     private static int toClusterType(String s) {
151         if (s.equals("time")) {
152             return CLUSTER_BY_TIME;
153         } else if (s.equals("location")) {
154             return CLUSTER_BY_LOCATION;
155         } else if (s.equals("tag")) {
156             return CLUSTER_BY_TAG;
157         } else if (s.equals("size")) {
158             return CLUSTER_BY_SIZE;
159         } else if (s.equals("face")) {
160             return CLUSTER_BY_FACE;
161         }
162         return 0;
163     }
164 
setMenuItemApplied( GalleryActionBar model, int id, boolean applied, boolean updateTitle)165     private static void setMenuItemApplied(
166             GalleryActionBar model, int id, boolean applied, boolean updateTitle) {
167         model.setClusterItemEnabled(id, !applied);
168     }
169 
setMenuItemAppliedEnabled(GalleryActionBar model, int id, boolean applied, boolean enabled, boolean updateTitle)170     private static void setMenuItemAppliedEnabled(GalleryActionBar model, int id, boolean applied, boolean enabled, boolean updateTitle) {
171         model.setClusterItemEnabled(id, enabled);
172     }
173 
174     // Add a specified filter to the path.
newFilterPath(String base, int filterType)175     public static String newFilterPath(String base, int filterType) {
176         int mediaType;
177         switch (filterType) {
178             case FILTER_IMAGE_ONLY:
179                 mediaType = MediaObject.MEDIA_TYPE_IMAGE;
180                 break;
181             case FILTER_VIDEO_ONLY:
182                 mediaType = MediaObject.MEDIA_TYPE_VIDEO;
183                 break;
184             default:  /* FILTER_ALL */
185                 return base;
186         }
187 
188         return "/filter/mediatype/" + mediaType + "/{" + base + "}";
189     }
190 
191     // Add a specified clustering to the path.
newClusterPath(String base, int clusterType)192     public static String newClusterPath(String base, int clusterType) {
193         String kind;
194         switch (clusterType) {
195             case CLUSTER_BY_TIME:
196                 kind = "time";
197                 break;
198             case CLUSTER_BY_LOCATION:
199                 kind = "location";
200                 break;
201             case CLUSTER_BY_TAG:
202                 kind = "tag";
203                 break;
204             case CLUSTER_BY_SIZE:
205                 kind = "size";
206                 break;
207             case CLUSTER_BY_FACE:
208                 kind = "face";
209                 break;
210             default: /* CLUSTER_BY_ALBUM */
211                 return base;
212         }
213 
214         return "/cluster/{" + base + "}/" + kind;
215     }
216 
217     // Change the topmost clustering to the specified type.
switchClusterPath(String base, int clusterType)218     public static String switchClusterPath(String base, int clusterType) {
219         return newClusterPath(removeOneClusterFromPath(base), clusterType);
220     }
221 
222     // Remove the topmost clustering (if any) from the path.
removeOneClusterFromPath(String base)223     private static String removeOneClusterFromPath(String base) {
224         boolean[] done = new boolean[1];
225         return removeOneClusterFromPath(base, done);
226     }
227 
removeOneClusterFromPath(String base, boolean[] done)228     private static String removeOneClusterFromPath(String base, boolean[] done) {
229         if (done[0]) return base;
230 
231         String[] segments = Path.split(base);
232         if (segments[0].equals("cluster")) {
233             done[0] = true;
234             return Path.splitSequence(segments[1])[0];
235         }
236 
237         StringBuilder sb = new StringBuilder();
238         for (int i = 0; i < segments.length; i++) {
239             sb.append("/");
240             if (segments[i].startsWith("{")) {
241                 sb.append("{");
242                 String[] sets = Path.splitSequence(segments[i]);
243                 for (int j = 0; j < sets.length; j++) {
244                     if (j > 0) {
245                         sb.append(",");
246                     }
247                     sb.append(removeOneClusterFromPath(sets[j], done));
248                 }
249                 sb.append("}");
250             } else {
251                 sb.append(segments[i]);
252             }
253         }
254         return sb.toString();
255     }
256 }
257