Home | History | Annotate | Download | only in app
      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     @SuppressWarnings("unused")
     58     private static final String TAG = "FilterUtils";
     59 
     60     public static final int CLUSTER_BY_ALBUM = 1;
     61     public static final int CLUSTER_BY_TIME = 2;
     62     public static final int CLUSTER_BY_LOCATION = 4;
     63     public static final int CLUSTER_BY_TAG = 8;
     64     public static final int CLUSTER_BY_SIZE = 16;
     65     public static final int CLUSTER_BY_FACE = 32;
     66 
     67     public static final int FILTER_IMAGE_ONLY = 1;
     68     public static final int FILTER_VIDEO_ONLY = 2;
     69     public static final int FILTER_ALL = 4;
     70 
     71     // These are indices of the return values of getAppliedFilters().
     72     // The _F suffix means "fixed".
     73     private static final int CLUSTER_TYPE = 0;
     74     private static final int FILTER_TYPE = 1;
     75     private static final int CLUSTER_TYPE_F = 2;
     76     private static final int FILTER_TYPE_F = 3;
     77     private static final int CLUSTER_CURRENT_TYPE = 4;
     78     private static final int FILTER_CURRENT_TYPE = 5;
     79 
     80     public static void setupMenuItems(GalleryActionBar actionBar, Path path, boolean inAlbum) {
     81         int[] result = new int[6];
     82         getAppliedFilters(path, result);
     83         int ctype = result[CLUSTER_TYPE];
     84         int ftype = result[FILTER_TYPE];
     85         int ftypef = result[FILTER_TYPE_F];
     86         int ccurrent = result[CLUSTER_CURRENT_TYPE];
     87         int fcurrent = result[FILTER_CURRENT_TYPE];
     88 
     89         setMenuItemApplied(actionBar, CLUSTER_BY_TIME,
     90                 (ctype & CLUSTER_BY_TIME) != 0, (ccurrent & CLUSTER_BY_TIME) != 0);
     91         setMenuItemApplied(actionBar, CLUSTER_BY_LOCATION,
     92                 (ctype & CLUSTER_BY_LOCATION) != 0, (ccurrent & CLUSTER_BY_LOCATION) != 0);
     93         setMenuItemApplied(actionBar, CLUSTER_BY_TAG,
     94                 (ctype & CLUSTER_BY_TAG) != 0, (ccurrent & CLUSTER_BY_TAG) != 0);
     95         setMenuItemApplied(actionBar, CLUSTER_BY_FACE,
     96                 (ctype & CLUSTER_BY_FACE) != 0, (ccurrent & CLUSTER_BY_FACE) != 0);
     97 
     98         actionBar.setClusterItemVisibility(CLUSTER_BY_ALBUM, !inAlbum || ctype == 0);
     99 
    100         setMenuItemApplied(actionBar, R.id.action_cluster_album, ctype == 0,
    101                 ccurrent == 0);
    102 
    103         // A filtering is available if it's not applied, and the old filtering
    104         // (if any) is not fixed.
    105         setMenuItemAppliedEnabled(actionBar, R.string.show_images_only,
    106                 (ftype & FILTER_IMAGE_ONLY) != 0,
    107                 (ftype & FILTER_IMAGE_ONLY) == 0 && ftypef == 0,
    108                 (fcurrent & FILTER_IMAGE_ONLY) != 0);
    109         setMenuItemAppliedEnabled(actionBar, R.string.show_videos_only,
    110                 (ftype & FILTER_VIDEO_ONLY) != 0,
    111                 (ftype & FILTER_VIDEO_ONLY) == 0 && ftypef == 0,
    112                 (fcurrent & FILTER_VIDEO_ONLY) != 0);
    113         setMenuItemAppliedEnabled(actionBar, R.string.show_all,
    114                 ftype == 0, ftype != 0 && ftypef == 0, fcurrent == 0);
    115     }
    116 
    117     // Gets the filters applied in the path.
    118     private static void getAppliedFilters(Path path, int[] result) {
    119         getAppliedFilters(path, result, false);
    120     }
    121 
    122     private static void getAppliedFilters(Path path, int[] result, boolean underCluster) {
    123         String[] segments = path.split();
    124         // Recurse into sub media sets.
    125         for (int i = 0; i < segments.length; i++) {
    126             if (segments[i].startsWith("{")) {
    127                 String[] sets = Path.splitSequence(segments[i]);
    128                 for (int j = 0; j < sets.length; j++) {
    129                     Path sub = Path.fromString(sets[j]);
    130                     getAppliedFilters(sub, result, underCluster);
    131                 }
    132             }
    133         }
    134 
    135         // update current selection
    136         if (segments[0].equals("cluster")) {
    137             // if this is a clustered album, set underCluster to true.
    138             if (segments.length == 4) {
    139                 underCluster = true;
    140             }
    141 
    142             int ctype = toClusterType(segments[2]);
    143             result[CLUSTER_TYPE] |= ctype;
    144             result[CLUSTER_CURRENT_TYPE] = ctype;
    145             if (underCluster) {
    146                 result[CLUSTER_TYPE_F] |= ctype;
    147             }
    148         }
    149     }
    150 
    151     private static int toClusterType(String s) {
    152         if (s.equals("time")) {
    153             return CLUSTER_BY_TIME;
    154         } else if (s.equals("location")) {
    155             return CLUSTER_BY_LOCATION;
    156         } else if (s.equals("tag")) {
    157             return CLUSTER_BY_TAG;
    158         } else if (s.equals("size")) {
    159             return CLUSTER_BY_SIZE;
    160         } else if (s.equals("face")) {
    161             return CLUSTER_BY_FACE;
    162         }
    163         return 0;
    164     }
    165 
    166     private static void setMenuItemApplied(
    167             GalleryActionBar model, int id, boolean applied, boolean updateTitle) {
    168         model.setClusterItemEnabled(id, !applied);
    169     }
    170 
    171     private static void setMenuItemAppliedEnabled(GalleryActionBar model, int id, boolean applied, boolean enabled, boolean updateTitle) {
    172         model.setClusterItemEnabled(id, enabled);
    173     }
    174 
    175     // Add a specified filter to the path.
    176     public static String newFilterPath(String base, int filterType) {
    177         int mediaType;
    178         switch (filterType) {
    179             case FILTER_IMAGE_ONLY:
    180                 mediaType = MediaObject.MEDIA_TYPE_IMAGE;
    181                 break;
    182             case FILTER_VIDEO_ONLY:
    183                 mediaType = MediaObject.MEDIA_TYPE_VIDEO;
    184                 break;
    185             default:  /* FILTER_ALL */
    186                 return base;
    187         }
    188 
    189         return "/filter/mediatype/" + mediaType + "/{" + base + "}";
    190     }
    191 
    192     // Add a specified clustering to the path.
    193     public static String newClusterPath(String base, int clusterType) {
    194         String kind;
    195         switch (clusterType) {
    196             case CLUSTER_BY_TIME:
    197                 kind = "time";
    198                 break;
    199             case CLUSTER_BY_LOCATION:
    200                 kind = "location";
    201                 break;
    202             case CLUSTER_BY_TAG:
    203                 kind = "tag";
    204                 break;
    205             case CLUSTER_BY_SIZE:
    206                 kind = "size";
    207                 break;
    208             case CLUSTER_BY_FACE:
    209                 kind = "face";
    210                 break;
    211             default: /* CLUSTER_BY_ALBUM */
    212                 return base;
    213         }
    214 
    215         return "/cluster/{" + base + "}/" + kind;
    216     }
    217 
    218     // Change the topmost clustering to the specified type.
    219     public static String switchClusterPath(String base, int clusterType) {
    220         return newClusterPath(removeOneClusterFromPath(base), clusterType);
    221     }
    222 
    223     // Remove the topmost clustering (if any) from the path.
    224     private static String removeOneClusterFromPath(String base) {
    225         boolean[] done = new boolean[1];
    226         return removeOneClusterFromPath(base, done);
    227     }
    228 
    229     private static String removeOneClusterFromPath(String base, boolean[] done) {
    230         if (done[0]) return base;
    231 
    232         String[] segments = Path.split(base);
    233         if (segments[0].equals("cluster")) {
    234             done[0] = true;
    235             return Path.splitSequence(segments[1])[0];
    236         }
    237 
    238         StringBuilder sb = new StringBuilder();
    239         for (int i = 0; i < segments.length; i++) {
    240             sb.append("/");
    241             if (segments[i].startsWith("{")) {
    242                 sb.append("{");
    243                 String[] sets = Path.splitSequence(segments[i]);
    244                 for (int j = 0; j < sets.length; j++) {
    245                     if (j > 0) {
    246                         sb.append(",");
    247                     }
    248                     sb.append(removeOneClusterFromPath(sets[j], done));
    249                 }
    250                 sb.append("}");
    251             } else {
    252                 sb.append(segments[i]);
    253             }
    254         }
    255         return sb.toString();
    256     }
    257 }
    258