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