• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google Inc.
3  * Licensed to The Android Open Source Project.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.mail.ui;
19 
20 import com.android.mail.R;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Sets;
23 
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.app.DialogFragment;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnClickListener;
30 import android.os.Bundle;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.AdapterView;
35 import android.widget.AdapterView.OnItemClickListener;
36 import android.widget.BaseAdapter;
37 import android.widget.CheckedTextView;
38 import android.widget.ListView;
39 
40 
41 import java.lang.ref.WeakReference;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.List;
45 import java.util.Set;
46 
47 /**
48  * A {@link DialogFragment} that shows multiple values, and lets you select 0 to
49  * {@link #MAX_SELECTED_VALUES} of them.
50  */
51 public abstract class LimitedMultiSelectDialogFragment extends DialogFragment {
52     public interface LimitedMultiSelectDialogListener {
onSelectionChanged(Set<String> selectedValues)53         void onSelectionChanged(Set<String> selectedValues);
54     }
55 
56     private static final String ARG_ENTRIES = "entries";
57     private static final String ARG_ENTRY_VALUES = "entryValues";
58     private static final String ARG_SELECTED_VALUES = "selectedValues";
59 
60     private WeakReference<LimitedMultiSelectDialogListener> mListener = null;
61 
62     // Public no-args constructor needed for fragment re-instantiation
LimitedMultiSelectDialogFragment()63     public LimitedMultiSelectDialogFragment() {}
64     /**
65      * Populates the arguments on a {@link LimitedMultiSelectDialogFragment}.
66      */
populateArguments(final LimitedMultiSelectDialogFragment fragment, final ArrayList<String> entries, final ArrayList<String> entryValues, final ArrayList<String> selectedValues)67     protected static void populateArguments(final LimitedMultiSelectDialogFragment fragment,
68             final ArrayList<String> entries, final ArrayList<String> entryValues,
69             final ArrayList<String> selectedValues) {
70         final Bundle args = new Bundle(3);
71         args.putStringArrayList(ARG_ENTRIES, entries);
72         args.putStringArrayList(ARG_ENTRY_VALUES, entryValues);
73         args.putStringArrayList(ARG_SELECTED_VALUES, selectedValues);
74         fragment.setArguments(args);
75     }
76 
77     @Override
onCreateDialog(final Bundle savedInstanceState)78     public Dialog onCreateDialog(final Bundle savedInstanceState) {
79         final List<String> selectedValuesList =
80                 getArguments().getStringArrayList(ARG_SELECTED_VALUES);
81         final Set<String> selectedValues = Sets.newHashSet(selectedValuesList);
82 
83         final List<String> entryValues = getArguments().getStringArrayList(ARG_ENTRY_VALUES);
84 
85         final LimitedMultiSelectAdapter adapter = new LimitedMultiSelectAdapter(
86                 getActivity(), getArguments().getStringArrayList(ARG_ENTRIES), entryValues,
87                 getMaxSelectedValues());
88         adapter.setSelected(selectedValues);
89 
90         final ListView listView = new ListView(getActivity());
91         listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
92         listView.setAdapter(adapter);
93         listView.setOnItemClickListener(new OnItemClickListener() {
94             @Override
95             public void onItemClick(
96                     final AdapterView<?> parent, final View view, final int position,
97                     final long id) {
98                 final String entryValue = (String) parent.getItemAtPosition(position);
99 
100                 if (selectedValues.contains(entryValue)) {
101                     // Remove / uncheck
102                     selectedValues.remove(entryValue);
103                     adapter.removeSelected(entryValue);
104                 } else {
105                     // Add / check
106                     selectedValues.add(entryValue);
107                     adapter.addSelected(entryValue);
108                 }
109 
110                 getArguments().putStringArrayList(ARG_SELECTED_VALUES,
111                         new ArrayList<String>(selectedValues));
112 
113                 adapter.notifyDataSetChanged();
114             }
115         });
116 
117         // Set initial check states
118         for (final String value : selectedValues) {
119             for (int j = 0; j < entryValues.size(); j++) {
120                 if (entryValues.get(j).equals(value)) {
121                     listView.setItemChecked(j, true);
122                 }
123             }
124         }
125 
126         return new AlertDialog.Builder(getActivity()).setTitle(getDialogTitle())
127                 .setView(listView)
128                 .setPositiveButton(R.string.ok, new OnClickListener() {
129                     @Override
130                     public void onClick(final DialogInterface dialog, final int which) {
131                         if (mListener != null) {
132                             final LimitedMultiSelectDialogListener listener = mListener.get();
133                             if (listener != null) {
134                                 listener.onSelectionChanged(selectedValues);
135                             }
136                         }
137                     }
138                 })
139                 .setNegativeButton(R.string.cancel, new OnClickListener() {
140                     @Override
141                     public void onClick(final DialogInterface dialog, final int which) {
142                         dismiss();
143                     }
144                 })
145                 .create();
146     }
147 
148     public void setListener(final LimitedMultiSelectDialogListener listener) {
149         mListener = new WeakReference<LimitedMultiSelectDialogListener>(listener);
150     }
151 
152     private static class LimitedMultiSelectAdapter extends BaseAdapter {
153         private final Context mContext;
154 
155         private final List<String> mEntries;
156         private final List<String> mEntryValues;
157 
158         private final Set<String> mSelectedValues;
159 
160         private final int mMaxSelectedValues;
161 
162         public LimitedMultiSelectAdapter(final Context context, final List<String> entries,
163                 final List<String> entryValues, final int maxSelectedValues) {
164             mContext = context;
165 
166             mEntries = ImmutableList.copyOf(entries);
167             mEntryValues = ImmutableList.copyOf(entryValues);
168 
169             mSelectedValues = Sets.newHashSetWithExpectedSize(maxSelectedValues);
170 
171             mMaxSelectedValues = maxSelectedValues;
172 
173             if (mEntries.size() != mEntryValues.size()) {
174                 throw new IllegalArgumentException("Each entry must have a corresponding value");
175             }
176         }
177 
178         @Override
179         public int getCount() {
180             return mEntries.size();
181         }
182 
183         @Override
184         public String getItem(final int position) {
185             return mEntryValues.get(position);
186         }
187 
188         @Override
189         public long getItemId(final int position) {
190             return getItem(position).hashCode();
191         }
192 
193         @Override
194         public int getItemViewType(final int position) {
195             return 0;
196         }
197 
198         @Override
199         public View getView(final int position, final View convertView, final ViewGroup parent) {
200             final CheckedTextView checkedTextView;
201 
202             if (convertView == null) {
203                 checkedTextView = (CheckedTextView) LayoutInflater.from(mContext)
204                         .inflate(R.layout.select_dialog_multichoice_holo, null);
205             } else {
206                 checkedTextView = (CheckedTextView) convertView;
207             }
208 
209             checkedTextView.setText(mEntries.get(position));
210             checkedTextView.setEnabled(isEnabled(position));
211 
212             return checkedTextView;
213         }
214 
215         @Override
216         public int getViewTypeCount() {
217             return 1;
218         }
219 
220         @Override
221         public boolean hasStableIds() {
222             return true;
223         }
224 
225         @Override
226         public boolean isEmpty() {
227             return mEntries.isEmpty();
228         }
229 
230         @Override
231         public boolean areAllItemsEnabled() {
232             // If we have less than the maximum number selected, everything is
233             // enabled
234             if (mMaxSelectedValues > mSelectedValues.size()) {
235                 return true;
236             }
237 
238             return false;
239         }
240 
241         @Override
242         public boolean isEnabled(final int position) {
243             // If we have less than the maximum selected, everything is enabled
244             if (mMaxSelectedValues > mSelectedValues.size()) {
245                 return true;
246             }
247 
248             // If we have the maximum selected, only the selected rows are
249             // enabled
250             if (mSelectedValues.contains(getItem(position))) {
251                 return true;
252             }
253 
254             return false;
255         }
256 
257         public void setSelected(final Collection<String> selectedValues) {
258             mSelectedValues.clear();
259             mSelectedValues.addAll(selectedValues);
260             notifyDataSetChanged();
261         }
262 
263         public void addSelected(final String selectedValue) {
264             mSelectedValues.add(selectedValue);
265             notifyDataSetChanged();
266         }
267 
268         public void removeSelected(final String selectedValue) {
269             mSelectedValues.remove(selectedValue);
270             notifyDataSetChanged();
271         }
272     }
273 
274     /**
275      * Gets a unique String to be used as a tag for this Fragment.
276      */
277     protected abstract String getFragmentTag();
278 
279     /**
280      * Gets the maximum number of values that may be selected.
281      */
282     protected abstract int getMaxSelectedValues();
283 
284     /**
285      * Gets the title of the dialog.
286      */
287     protected abstract String getDialogTitle();
288 }
289