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.ui;
18 
19 import com.android.gallery3d.app.AbstractGalleryActivity;
20 import com.android.gallery3d.data.DataManager;
21 import com.android.gallery3d.data.MediaItem;
22 import com.android.gallery3d.data.MediaSet;
23 import com.android.gallery3d.data.Path;
24 
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.Set;
28 
29 public class SelectionManager {
30     @SuppressWarnings("unused")
31     private static final String TAG = "SelectionManager";
32 
33     public static final int ENTER_SELECTION_MODE = 1;
34     public static final int LEAVE_SELECTION_MODE = 2;
35     public static final int SELECT_ALL_MODE = 3;
36 
37     private Set<Path> mClickedSet;
38     private MediaSet mSourceMediaSet;
39     private SelectionListener mListener;
40     private DataManager mDataManager;
41     private boolean mInverseSelection;
42     private boolean mIsAlbumSet;
43     private boolean mInSelectionMode;
44     private boolean mAutoLeave = true;
45     private int mTotal;
46 
47     public interface SelectionListener {
onSelectionModeChange(int mode)48         public void onSelectionModeChange(int mode);
onSelectionChange(Path path, boolean selected)49         public void onSelectionChange(Path path, boolean selected);
50     }
51 
SelectionManager(AbstractGalleryActivity activity, boolean isAlbumSet)52     public SelectionManager(AbstractGalleryActivity activity, boolean isAlbumSet) {
53         mDataManager = activity.getDataManager();
54         mClickedSet = new HashSet<Path>();
55         mIsAlbumSet = isAlbumSet;
56         mTotal = -1;
57     }
58 
59     // Whether we will leave selection mode automatically once the number of
60     // selected items is down to zero.
setAutoLeaveSelectionMode(boolean enable)61     public void setAutoLeaveSelectionMode(boolean enable) {
62         mAutoLeave = enable;
63     }
64 
setSelectionListener(SelectionListener listener)65     public void setSelectionListener(SelectionListener listener) {
66         mListener = listener;
67     }
68 
selectAll()69     public void selectAll() {
70         mInverseSelection = true;
71         mClickedSet.clear();
72         mTotal = -1;
73         enterSelectionMode();
74         if (mListener != null) mListener.onSelectionModeChange(SELECT_ALL_MODE);
75     }
76 
deSelectAll()77     public void deSelectAll() {
78         leaveSelectionMode();
79         mInverseSelection = false;
80         mClickedSet.clear();
81     }
82 
inSelectAllMode()83     public boolean inSelectAllMode() {
84         return mInverseSelection;
85     }
86 
inSelectionMode()87     public boolean inSelectionMode() {
88         return mInSelectionMode;
89     }
90 
enterSelectionMode()91     public void enterSelectionMode() {
92         if (mInSelectionMode) return;
93 
94         mInSelectionMode = true;
95         if (mListener != null) mListener.onSelectionModeChange(ENTER_SELECTION_MODE);
96     }
97 
leaveSelectionMode()98     public void leaveSelectionMode() {
99         if (!mInSelectionMode) return;
100 
101         mInSelectionMode = false;
102         mInverseSelection = false;
103         mClickedSet.clear();
104         if (mListener != null) mListener.onSelectionModeChange(LEAVE_SELECTION_MODE);
105     }
106 
isItemSelected(Path itemId)107     public boolean isItemSelected(Path itemId) {
108         return mInverseSelection ^ mClickedSet.contains(itemId);
109     }
110 
getTotalCount()111     private int getTotalCount() {
112         if (mSourceMediaSet == null) return -1;
113 
114         if (mTotal < 0) {
115             mTotal = mIsAlbumSet
116                     ? mSourceMediaSet.getSubMediaSetCount()
117                     : mSourceMediaSet.getMediaItemCount();
118         }
119         return mTotal;
120     }
121 
getSelectedCount()122     public int getSelectedCount() {
123         int count = mClickedSet.size();
124         if (mInverseSelection) {
125             count = getTotalCount() - count;
126         }
127         return count;
128     }
129 
toggle(Path path)130     public void toggle(Path path) {
131         if (mClickedSet.contains(path)) {
132             mClickedSet.remove(path);
133         } else {
134             enterSelectionMode();
135             mClickedSet.add(path);
136         }
137 
138         // Convert to inverse selection mode if everything is selected.
139         int count = getSelectedCount();
140         if (count == getTotalCount()) {
141             selectAll();
142         }
143 
144         if (mListener != null) mListener.onSelectionChange(path, isItemSelected(path));
145         if (count == 0 && mAutoLeave) {
146             leaveSelectionMode();
147         }
148     }
149 
expandMediaSet(ArrayList<Path> items, MediaSet set, int maxSelection)150     private static boolean expandMediaSet(ArrayList<Path> items, MediaSet set, int maxSelection) {
151         int subCount = set.getSubMediaSetCount();
152         for (int i = 0; i < subCount; i++) {
153             if (!expandMediaSet(items, set.getSubMediaSet(i), maxSelection)) {
154                 return false;
155             }
156         }
157         int total = set.getMediaItemCount();
158         int batch = 50;
159         int index = 0;
160 
161         while (index < total) {
162             int count = index + batch < total
163                     ? batch
164                     : total - index;
165             ArrayList<MediaItem> list = set.getMediaItem(index, count);
166             if (list != null
167                     && list.size() > (maxSelection - items.size())) {
168                 return false;
169             }
170             for (MediaItem item : list) {
171                 items.add(item.getPath());
172             }
173             index += batch;
174         }
175         return true;
176     }
177 
getSelected(boolean expandSet)178     public ArrayList<Path> getSelected(boolean expandSet) {
179         return getSelected(expandSet, Integer.MAX_VALUE);
180     }
181 
getSelected(boolean expandSet, int maxSelection)182     public ArrayList<Path> getSelected(boolean expandSet, int maxSelection) {
183         ArrayList<Path> selected = new ArrayList<Path>();
184         if (mIsAlbumSet) {
185             if (mInverseSelection) {
186                 int total = getTotalCount();
187                 for (int i = 0; i < total; i++) {
188                     MediaSet set = mSourceMediaSet.getSubMediaSet(i);
189                     Path id = set.getPath();
190                     if (!mClickedSet.contains(id)) {
191                         if (expandSet) {
192                             if (!expandMediaSet(selected, set, maxSelection)) {
193                                 return null;
194                             }
195                         } else {
196                             selected.add(id);
197                             if (selected.size() > maxSelection) {
198                                 return null;
199                             }
200                         }
201                     }
202                 }
203             } else {
204                 for (Path id : mClickedSet) {
205                     if (expandSet) {
206                         if (!expandMediaSet(selected, mDataManager.getMediaSet(id),
207                                 maxSelection)) {
208                             return null;
209                         }
210                     } else {
211                         selected.add(id);
212                         if (selected.size() > maxSelection) {
213                             return null;
214                         }
215                     }
216                 }
217             }
218         } else {
219             if (mInverseSelection) {
220                 int total = getTotalCount();
221                 int index = 0;
222                 while (index < total) {
223                     int count = Math.min(total - index, MediaSet.MEDIAITEM_BATCH_FETCH_COUNT);
224                     ArrayList<MediaItem> list = mSourceMediaSet.getMediaItem(index, count);
225                     for (MediaItem item : list) {
226                         Path id = item.getPath();
227                         if (!mClickedSet.contains(id)) {
228                             selected.add(id);
229                             if (selected.size() > maxSelection) {
230                                 return null;
231                             }
232                         }
233                     }
234                     index += count;
235                 }
236             } else {
237                 for (Path id : mClickedSet) {
238                     selected.add(id);
239                     if (selected.size() > maxSelection) {
240                         return null;
241                     }
242                 }
243             }
244         }
245         return selected;
246     }
247 
setSourceMediaSet(MediaSet set)248     public void setSourceMediaSet(MediaSet set) {
249         mSourceMediaSet = set;
250         mTotal = -1;
251     }
252 }
253