• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.providers.media.photopicker.data;
18 
19 import android.content.Intent;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.provider.MediaStore;
23 
24 import androidx.lifecycle.LiveData;
25 import androidx.lifecycle.MutableLiveData;
26 
27 import com.android.providers.media.photopicker.data.model.Item;
28 
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 
35 /**
36  * A class that tracks Selection
37  */
38 public class Selection {
39     // The list of selected items.
40     private Map<Uri, Item> mSelectedItems = new HashMap<>();
41     private MutableLiveData<Integer> mSelectedItemSize = new MutableLiveData<>();
42     // The list of selected items for preview. This needs to be saved separately so that if activity
43     // gets killed, we will still have deselected items for preview.
44     private List<Item> mSelectedItemsForPreview = new ArrayList<>();
45     private boolean mSelectMultiple = false;
46     private int mMaxSelectionLimit = 1;
47     // This is set to false when max selection limit is reached.
48     private boolean mIsSelectionAllowed = true;
49 
50     /**
51      * @return {@link #mSelectedItems} - A {@link List} of selected {@link Item}
52      */
getSelectedItems()53     public List<Item> getSelectedItems() {
54         return Collections.unmodifiableList(new ArrayList<>(mSelectedItems.values()));
55     }
56 
57     /**
58      * @return {@link LiveData} of count of selected items in {@link #mSelectedItems}
59      */
getSelectedItemCount()60     public LiveData<Integer> getSelectedItemCount() {
61         if (mSelectedItemSize.getValue() == null) {
62             mSelectedItemSize.setValue(mSelectedItems.size());
63         }
64         return mSelectedItemSize;
65     }
66 
67     /**
68      * Add the selected {@code item} into {@link #mSelectedItems}.
69      */
addSelectedItem(Item item)70     public void addSelectedItem(Item item) {
71         mSelectedItems.put(item.getContentUri(), item);
72         mSelectedItemSize.postValue(mSelectedItems.size());
73         updateSelectionAllowed();
74     }
75 
76     /**
77      * Clears {@link #mSelectedItems} and sets the selected item as given {@code item}
78      */
setSelectedItem(Item item)79     public void setSelectedItem(Item item) {
80         mSelectedItems.clear();
81         mSelectedItems.put(item.getContentUri(), item);
82         mSelectedItemSize.postValue(mSelectedItems.size());
83         updateSelectionAllowed();
84     }
85 
86     /**
87      * Remove the {@code item} from the selected item list {@link #mSelectedItems}.
88      *
89      * @param item the item to be removed from the selected item list
90      */
removeSelectedItem(Item item)91     public void removeSelectedItem(Item item) {
92         mSelectedItems.remove(item.getContentUri());
93         mSelectedItemSize.postValue(mSelectedItems.size());
94         updateSelectionAllowed();
95     }
96 
97     /**
98      * Clear all selected items
99      */
clearSelectedItems()100     public void clearSelectedItems() {
101         mSelectedItems.clear();
102         mSelectedItemSize.postValue(mSelectedItems.size());
103         updateSelectionAllowed();
104     }
105 
106     /**
107      * @return {@code true} if give {@code item} is present in selected items
108      *         {@link #mSelectedItems}, {@code false} otherwise
109      */
isItemSelected(Item item)110     public boolean isItemSelected(Item item) {
111         return mSelectedItems.containsKey(item.getContentUri());
112     }
113 
updateSelectionAllowed()114     private void updateSelectionAllowed() {
115         final int size = mSelectedItems.size();
116         if (size >= mMaxSelectionLimit) {
117             if (mIsSelectionAllowed) {
118                 mIsSelectionAllowed = false;
119             }
120         } else {
121             // size < mMaxSelectionLimit
122             if (!mIsSelectionAllowed) {
123                 mIsSelectionAllowed = true;
124             }
125         }
126     }
127 
128     /**
129      * @return returns whether more items can be selected or not. {@code true} if the number of
130      *         selected items is lower than or equal to {@code mMaxLimit}, {@code false} otherwise.
131      */
isSelectionAllowed()132     public boolean isSelectionAllowed() {
133         return mIsSelectionAllowed;
134     }
135 
136     /**
137      * Prepares current selected items for previewing all selected items in multi-select preview.
138      * The method also sorts the selected items by {@link Item#compareTo} method which sorts based
139      * on dateTaken values.
140      */
prepareSelectedItemsForPreviewAll()141     public void prepareSelectedItemsForPreviewAll() {
142         mSelectedItemsForPreview = new ArrayList<>(mSelectedItems.values());
143         mSelectedItemsForPreview.sort(Collections.reverseOrder(Item::compareTo));
144     }
145 
146     /**
147      * Sets the given {@code item} as the item for previewing. This method will be used while
148      * previewing on long press.
149      */
prepareItemForPreviewOnLongPress(Item item)150     public void prepareItemForPreviewOnLongPress(Item item) {
151         mSelectedItemsForPreview = Collections.singletonList(item);
152     }
153 
154     /**
155      * @return {@link #mSelectedItemsForPreview} - selected items for preview.
156      */
getSelectedItemsForPreview()157     public List<Item> getSelectedItemsForPreview() {
158         return Collections.unmodifiableList(mSelectedItemsForPreview);
159     }
160 
161     /**
162      * Parse values from {@code intent} and set corresponding fields
163      */
parseSelectionValuesFromIntent(Intent intent)164     public void parseSelectionValuesFromIntent(Intent intent) {
165         final Bundle extras = intent.getExtras();
166         final boolean isExtraPickImagesMaxSet =
167                 extras != null && extras.containsKey(MediaStore.EXTRA_PICK_IMAGES_MAX);
168 
169         // Support Intent.EXTRA_ALLOW_MULTIPLE flag only for ACTION_GET_CONTENT
170         if (intent.getAction() != null && intent.getAction().equals(
171                 Intent.ACTION_GET_CONTENT)) {
172             if (isExtraPickImagesMaxSet) {
173                 throw new IllegalArgumentException("EXTRA_PICK_IMAGES_MAX is not supported for "
174                         + "ACTION_GET_CONTENT");
175             }
176 
177             mSelectMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
178             if (mSelectMultiple) {
179                 mMaxSelectionLimit = MediaStore.getPickImagesMaxLimit();
180             }
181             return;
182         }
183 
184         // Check EXTRA_PICK_IMAGES_MAX value only if the flag is set.
185         if (isExtraPickImagesMaxSet) {
186             final int extraMax = intent.getIntExtra(MediaStore.EXTRA_PICK_IMAGES_MAX,
187                     /* defaultValue */ -1);
188             // Multi selection max limit should always be greater than 1 and less than or equal
189             // to PICK_IMAGES_MAX_LIMIT.
190             if (extraMax <= 1 || extraMax > MediaStore.getPickImagesMaxLimit()) {
191                 throw new IllegalArgumentException("Invalid EXTRA_PICK_IMAGES_MAX value");
192             }
193             mSelectMultiple = true;
194             mMaxSelectionLimit = extraMax;
195         }
196     }
197 
198     /**
199      * Return whether supports multiple select {@link #mSelectMultiple} or not
200      */
canSelectMultiple()201     public boolean canSelectMultiple() {
202         return mSelectMultiple;
203     }
204 
205     /**
206      * Return maximum limit of items that can be selected
207      */
getMaxSelectionLimit()208     public int getMaxSelectionLimit() {
209         return mMaxSelectionLimit;
210     }
211 }