| /* |
| * 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.app; |
| |
| import com.android.gallery3d.R; |
| import com.android.gallery3d.data.MediaObject; |
| import com.android.gallery3d.data.Path; |
| |
| // This class handles filtering and clustering. |
| // |
| // We allow at most only one filter operation at a time (Currently it |
| // doesn't make sense to use more than one). Also each clustering operation |
| // can be applied at most once. In addition, there is one more constraint |
| // ("fixed set constraint") described below. |
| // |
| // A clustered album (not including album set) and its base sets are fixed. |
| // For example, |
| // |
| // /cluster/{base_set}/time/7 |
| // |
| // This set and all sets inside base_set (recursively) are fixed because |
| // 1. We can not change this set to use another clustering condition (like |
| // changing "time" to "location"). |
| // 2. Neither can we change any set in the base_set. |
| // The reason is in both cases the 7th set may not exist in the new clustering. |
| // --------------------- |
| // newPath operation: create a new path based on a source path and put an extra |
| // condition on top of it: |
| // |
| // T = newFilterPath(S, filterType); |
| // T = newClusterPath(S, clusterType); |
| // |
| // Similar functions can be used to replace the current condition (if there is one). |
| // |
| // T = switchFilterPath(S, filterType); |
| // T = switchClusterPath(S, clusterType); |
| // |
| // For all fixed set in the path defined above, if some clusterType and |
| // filterType are already used, they cannot not be used as parameter for these |
| // functions. setupMenuItems() makes sure those types cannot be selected. |
| // |
| public class FilterUtils { |
| @SuppressWarnings("unused") |
| private static final String TAG = "FilterUtils"; |
| |
| public static final int CLUSTER_BY_ALBUM = 1; |
| public static final int CLUSTER_BY_TIME = 2; |
| public static final int CLUSTER_BY_LOCATION = 4; |
| public static final int CLUSTER_BY_TAG = 8; |
| public static final int CLUSTER_BY_SIZE = 16; |
| public static final int CLUSTER_BY_FACE = 32; |
| |
| public static final int FILTER_IMAGE_ONLY = 1; |
| public static final int FILTER_VIDEO_ONLY = 2; |
| public static final int FILTER_ALL = 4; |
| |
| // These are indices of the return values of getAppliedFilters(). |
| // The _F suffix means "fixed". |
| private static final int CLUSTER_TYPE = 0; |
| private static final int FILTER_TYPE = 1; |
| private static final int CLUSTER_TYPE_F = 2; |
| private static final int FILTER_TYPE_F = 3; |
| private static final int CLUSTER_CURRENT_TYPE = 4; |
| private static final int FILTER_CURRENT_TYPE = 5; |
| |
| public static void setupMenuItems(GalleryActionBar actionBar, Path path, boolean inAlbum) { |
| int[] result = new int[6]; |
| getAppliedFilters(path, result); |
| int ctype = result[CLUSTER_TYPE]; |
| int ftype = result[FILTER_TYPE]; |
| int ftypef = result[FILTER_TYPE_F]; |
| int ccurrent = result[CLUSTER_CURRENT_TYPE]; |
| int fcurrent = result[FILTER_CURRENT_TYPE]; |
| |
| setMenuItemApplied(actionBar, CLUSTER_BY_TIME, |
| (ctype & CLUSTER_BY_TIME) != 0, (ccurrent & CLUSTER_BY_TIME) != 0); |
| setMenuItemApplied(actionBar, CLUSTER_BY_LOCATION, |
| (ctype & CLUSTER_BY_LOCATION) != 0, (ccurrent & CLUSTER_BY_LOCATION) != 0); |
| setMenuItemApplied(actionBar, CLUSTER_BY_TAG, |
| (ctype & CLUSTER_BY_TAG) != 0, (ccurrent & CLUSTER_BY_TAG) != 0); |
| setMenuItemApplied(actionBar, CLUSTER_BY_FACE, |
| (ctype & CLUSTER_BY_FACE) != 0, (ccurrent & CLUSTER_BY_FACE) != 0); |
| |
| actionBar.setClusterItemVisibility(CLUSTER_BY_ALBUM, !inAlbum || ctype == 0); |
| |
| setMenuItemApplied(actionBar, R.id.action_cluster_album, ctype == 0, |
| ccurrent == 0); |
| |
| // A filtering is available if it's not applied, and the old filtering |
| // (if any) is not fixed. |
| setMenuItemAppliedEnabled(actionBar, R.string.show_images_only, |
| (ftype & FILTER_IMAGE_ONLY) != 0, |
| (ftype & FILTER_IMAGE_ONLY) == 0 && ftypef == 0, |
| (fcurrent & FILTER_IMAGE_ONLY) != 0); |
| setMenuItemAppliedEnabled(actionBar, R.string.show_videos_only, |
| (ftype & FILTER_VIDEO_ONLY) != 0, |
| (ftype & FILTER_VIDEO_ONLY) == 0 && ftypef == 0, |
| (fcurrent & FILTER_VIDEO_ONLY) != 0); |
| setMenuItemAppliedEnabled(actionBar, R.string.show_all, |
| ftype == 0, ftype != 0 && ftypef == 0, fcurrent == 0); |
| } |
| |
| // Gets the filters applied in the path. |
| private static void getAppliedFilters(Path path, int[] result) { |
| getAppliedFilters(path, result, false); |
| } |
| |
| private static void getAppliedFilters(Path path, int[] result, boolean underCluster) { |
| String[] segments = path.split(); |
| // Recurse into sub media sets. |
| for (int i = 0; i < segments.length; i++) { |
| if (segments[i].startsWith("{")) { |
| String[] sets = Path.splitSequence(segments[i]); |
| for (int j = 0; j < sets.length; j++) { |
| Path sub = Path.fromString(sets[j]); |
| getAppliedFilters(sub, result, underCluster); |
| } |
| } |
| } |
| |
| // update current selection |
| if (segments[0].equals("cluster")) { |
| // if this is a clustered album, set underCluster to true. |
| if (segments.length == 4) { |
| underCluster = true; |
| } |
| |
| int ctype = toClusterType(segments[2]); |
| result[CLUSTER_TYPE] |= ctype; |
| result[CLUSTER_CURRENT_TYPE] = ctype; |
| if (underCluster) { |
| result[CLUSTER_TYPE_F] |= ctype; |
| } |
| } |
| } |
| |
| private static int toClusterType(String s) { |
| if (s.equals("time")) { |
| return CLUSTER_BY_TIME; |
| } else if (s.equals("location")) { |
| return CLUSTER_BY_LOCATION; |
| } else if (s.equals("tag")) { |
| return CLUSTER_BY_TAG; |
| } else if (s.equals("size")) { |
| return CLUSTER_BY_SIZE; |
| } else if (s.equals("face")) { |
| return CLUSTER_BY_FACE; |
| } |
| return 0; |
| } |
| |
| private static void setMenuItemApplied( |
| GalleryActionBar model, int id, boolean applied, boolean updateTitle) { |
| model.setClusterItemEnabled(id, !applied); |
| } |
| |
| private static void setMenuItemAppliedEnabled(GalleryActionBar model, int id, boolean applied, boolean enabled, boolean updateTitle) { |
| model.setClusterItemEnabled(id, enabled); |
| } |
| |
| // Add a specified filter to the path. |
| public static String newFilterPath(String base, int filterType) { |
| int mediaType; |
| switch (filterType) { |
| case FILTER_IMAGE_ONLY: |
| mediaType = MediaObject.MEDIA_TYPE_IMAGE; |
| break; |
| case FILTER_VIDEO_ONLY: |
| mediaType = MediaObject.MEDIA_TYPE_VIDEO; |
| break; |
| default: /* FILTER_ALL */ |
| return base; |
| } |
| |
| return "/filter/mediatype/" + mediaType + "/{" + base + "}"; |
| } |
| |
| // Add a specified clustering to the path. |
| public static String newClusterPath(String base, int clusterType) { |
| String kind; |
| switch (clusterType) { |
| case CLUSTER_BY_TIME: |
| kind = "time"; |
| break; |
| case CLUSTER_BY_LOCATION: |
| kind = "location"; |
| break; |
| case CLUSTER_BY_TAG: |
| kind = "tag"; |
| break; |
| case CLUSTER_BY_SIZE: |
| kind = "size"; |
| break; |
| case CLUSTER_BY_FACE: |
| kind = "face"; |
| break; |
| default: /* CLUSTER_BY_ALBUM */ |
| return base; |
| } |
| |
| return "/cluster/{" + base + "}/" + kind; |
| } |
| |
| // Change the topmost clustering to the specified type. |
| public static String switchClusterPath(String base, int clusterType) { |
| return newClusterPath(removeOneClusterFromPath(base), clusterType); |
| } |
| |
| // Remove the topmost clustering (if any) from the path. |
| private static String removeOneClusterFromPath(String base) { |
| boolean[] done = new boolean[1]; |
| return removeOneClusterFromPath(base, done); |
| } |
| |
| private static String removeOneClusterFromPath(String base, boolean[] done) { |
| if (done[0]) return base; |
| |
| String[] segments = Path.split(base); |
| if (segments[0].equals("cluster")) { |
| done[0] = true; |
| return Path.splitSequence(segments[1])[0]; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < segments.length; i++) { |
| sb.append("/"); |
| if (segments[i].startsWith("{")) { |
| sb.append("{"); |
| String[] sets = Path.splitSequence(segments[i]); |
| for (int j = 0; j < sets.length; j++) { |
| if (j > 0) { |
| sb.append(","); |
| } |
| sb.append(removeOneClusterFromPath(sets[j], done)); |
| } |
| sb.append("}"); |
| } else { |
| sb.append(segments[i]); |
| } |
| } |
| return sb.toString(); |
| } |
| } |