diff options
Diffstat (limited to 'src/com/android/gallery3d/app/FilterUtils.java')
-rw-r--r-- | src/com/android/gallery3d/app/FilterUtils.java | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/app/FilterUtils.java b/src/com/android/gallery3d/app/FilterUtils.java new file mode 100644 index 000000000..9b8ea2d62 --- /dev/null +++ b/src/com/android/gallery3d/app/FilterUtils.java @@ -0,0 +1,296 @@ +/* + * 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 { + 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 model, 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(model, CLUSTER_BY_TIME, + (ctype & CLUSTER_BY_TIME) != 0, (ccurrent & CLUSTER_BY_TIME) != 0); + setMenuItemApplied(model, CLUSTER_BY_LOCATION, + (ctype & CLUSTER_BY_LOCATION) != 0, (ccurrent & CLUSTER_BY_LOCATION) != 0); + setMenuItemApplied(model, CLUSTER_BY_TAG, + (ctype & CLUSTER_BY_TAG) != 0, (ccurrent & CLUSTER_BY_TAG) != 0); + setMenuItemApplied(model, CLUSTER_BY_FACE, + (ctype & CLUSTER_BY_FACE) != 0, (ccurrent & CLUSTER_BY_FACE) != 0); + + model.setClusterItemVisibility(CLUSTER_BY_ALBUM, !inAlbum || ctype == 0); + + setMenuItemApplied(model, 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(model, R.string.show_images_only, + (ftype & FILTER_IMAGE_ONLY) != 0, + (ftype & FILTER_IMAGE_ONLY) == 0 && ftypef == 0, + (fcurrent & FILTER_IMAGE_ONLY) != 0); + setMenuItemAppliedEnabled(model, R.string.show_videos_only, + (ftype & FILTER_VIDEO_ONLY) != 0, + (ftype & FILTER_VIDEO_ONLY) == 0 && ftypef == 0, + (fcurrent & FILTER_VIDEO_ONLY) != 0); + setMenuItemAppliedEnabled(model, 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 filter to the specified type. + public static String switchFilterPath(String base, int filterType) { + return newFilterPath(removeOneFilterFromPath(base), filterType); + } + + // 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(); + } + + // Remove the topmost filter (if any) from the path. + private static String removeOneFilterFromPath(String base) { + boolean[] done = new boolean[1]; + return removeOneFilterFromPath(base, done); + } + + private static String removeOneFilterFromPath(String base, boolean[] done) { + if (done[0]) return base; + + String[] segments = Path.split(base); + if (segments[0].equals("filter") && segments[1].equals("mediatype")) { + done[0] = true; + return Path.splitSequence(segments[3])[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(removeOneFilterFromPath(sets[j], done)); + } + sb.append("}"); + } else { + sb.append(segments[i]); + } + } + return sb.toString(); + } +} |