1 /*
2  * Copyright (C) 2012 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 android.app.AlertDialog;
21 import android.app.LoaderManager;
22 import android.content.Context;
23 import android.content.CursorLoader;
24 import android.content.DialogInterface;
25 import android.content.Loader;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.Bundle;
29 
30 import com.android.mail.R;
31 import com.android.mail.providers.Conversation;
32 import com.android.mail.providers.Folder;
33 import com.android.mail.providers.UIProvider;
34 import com.android.mail.providers.UIProvider.FolderType;
35 import com.android.mail.ui.FolderSelectorAdapter.FolderRow;
36 import com.android.mail.utils.Utils;
37 import com.google.common.collect.ImmutableSet;
38 
39 import java.util.Arrays;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 
46 /**
47  * Displays a folder selection dialog for the conversation provided. It allows
48  * the user to mark folders to assign that conversation to.
49  */
50 public class MultiFoldersSelectionDialog extends FolderSelectionDialog {
51     private boolean mSingle;
52     private final HashMap<Uri, FolderOperation> mOperations;
53 
MultiFoldersSelectionDialog()54     public MultiFoldersSelectionDialog() {
55         mOperations = new HashMap<Uri, FolderOperation>();
56     }
57 
58     private static final int FOLDER_LOADER_ID = 0;
59     private static final String FOLDER_QUERY_URI_TAG = "folderQueryUri";
60 
61     private static final String SAVESTATE_OPERATIONS_TAG = "operations";
62 
63     @Override
onCreate(Bundle savedInstanceState)64     public void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66 
67         mSingle = !mAccount
68                 .supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
69         mTitleId = R.string.change_folders_selection_dialog_title;
70 
71         if (savedInstanceState != null) {
72             final FolderOperation[] savedOps = (FolderOperation[])
73                     savedInstanceState.getParcelableArray(SAVESTATE_OPERATIONS_TAG);
74             for (final FolderOperation op : savedOps) {
75                 mOperations.put(op.mFolder.folderUri.fullUri, op);
76             }
77         }
78 
79         final Bundle args = new Bundle(1);
80         args.putParcelable(FOLDER_QUERY_URI_TAG, !Utils.isEmpty(mAccount.fullFolderListUri) ?
81                 mAccount.fullFolderListUri : mAccount.folderListUri);
82         final Context loaderContext = getActivity().getApplicationContext();
83         getLoaderManager().initLoader(FOLDER_LOADER_ID, args,
84                 new LoaderManager.LoaderCallbacks<Cursor>() {
85                     @Override
86                     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
87                         final Uri queryUri = args.getParcelable(FOLDER_QUERY_URI_TAG);
88                         return new CursorLoader(loaderContext, queryUri,
89                                 UIProvider.FOLDERS_PROJECTION, null, null, null);
90                     }
91 
92                     @Override
93                     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
94                         final Context context = getActivity();
95                         if (data == null || context == null) {
96                             return;
97                         }
98                         final AlertDialog dialog = (AlertDialog) getDialog();
99                         if (dialog == null) {
100                             // This could happen if the dialog is dismissed just before the
101                             // load finishes.
102                             return;
103                         }
104                         // The number of view types changes here, so we have to reset the listview's
105                         // adapter.
106                         dialog.getListView().setAdapter(null);
107                         dialog.getListView().setDivider(null);
108 
109                         final HashSet<String> checked = new HashSet<String>();
110                         for (final Conversation conversation : mTarget) {
111                             final List<Folder> rawFolders = conversation.getRawFolders();
112                             if (rawFolders != null && rawFolders.size() > 0) {
113                                 // Parse the raw folders and get all the uris.
114                                 checked.addAll(Arrays.asList(Folder.getUriArray(rawFolders)));
115                             } else {
116                                 // There are no folders for this conversation, so it must
117                                 // belong to the folder we are currently looking at.
118                                 checked.add(mCurrentFolder.folderUri.fullUri.toString());
119                             }
120                         }
121                         final Set<String> originalChecked = ImmutableSet.copyOf(checked);
122                         for (final Map.Entry<Uri, FolderOperation> entry : mOperations.entrySet()) {
123                             if (entry.getValue().mAdd) {
124                                 checked.add(entry.getKey().toString());
125                             } else {
126                                 checked.remove(entry.getKey().toString());
127                             }
128                         }
129                         mAdapter.clearSections();
130                         // TODO(mindyp) : bring this back in UR8 when Email providers
131                         // will have divided folder sections.
132                         /* final String[] headers = mContext.getResources()
133                                 .getStringArray(R.array.moveto_folder_sections);
134                          // Currently, the number of adapters are assumed to match the
135                          // number of headers in the string array.
136                          mAdapter.addSection(new SystemFolderSelectorAdapter(mContext,
137                          foldersCursor, checked, R.layout.multi_folders_view, null));
138 
139                         // TODO(mindyp): we currently do not support frequently moved to
140                         // folders, at headers[1]; need to define what that means.*/
141 
142                         Cursor c = AddableFolderSelectorAdapter.filterFolders(data,
143                                 ImmutableSet.of(FolderType.INBOX_SECTION), originalChecked,
144                                 true /* includeOnlyInitiallySelected */);
145                         if (c.getCount() > 0) {
146                             mAdapter.addSection(new AddableFolderSelectorAdapter(context, c,
147                                     checked, R.layout.multi_folders_view));
148                         }
149 
150                         c = AddableFolderSelectorAdapter.filterFolders(data,
151                                 ImmutableSet.of(FolderType.INBOX_SECTION), originalChecked,
152                                 false /* includeOnlyInitiallySelected */);
153                         if (c.getCount() > 0) {
154                             mAdapter.addSection(new AddableFolderSelectorAdapter(context, c,
155                                     checked, R.layout.multi_folders_view));
156                         }
157 
158                         dialog.getListView().setAdapter(mAdapter);
159                     }
160 
161                     @Override
162                     public void onLoaderReset(Loader<Cursor> loader) {
163                         mAdapter.clearSections();
164                     }
165                 });
166     }
167 
168     @Override
onSaveInstanceState(Bundle outState)169     public void onSaveInstanceState(Bundle outState) {
170         super.onSaveInstanceState(outState);
171         outState.putParcelableArray(SAVESTATE_OPERATIONS_TAG,
172                 mOperations.values().toArray(new FolderOperation[mOperations.size()]));
173     }
174 
175     @Override
onListItemClick(int position)176     protected void onListItemClick(int position) {
177         final Object item = mAdapter.getItem(position);
178         if (item instanceof FolderRow) {
179             update((FolderRow) item);
180         }
181     }
182 
183     /**
184      * Call this to update the state of folders as a result of them being
185      * selected / de-selected.
186      *
187      * @param row The item being updated.
188      */
update(FolderSelectorAdapter.FolderRow row)189     private void update(FolderSelectorAdapter.FolderRow row) {
190         final boolean add = !row.isSelected();
191         if (mSingle) {
192             if (!add) {
193                 // This would remove the check on a single radio button, so just
194                 // return.
195                 return;
196             }
197             // Clear any other checked items.
198             for (int i = 0, size = mAdapter.getCount(); i < size; i++) {
199                 final Object item = mAdapter.getItem(i);
200                 if (item instanceof FolderRow) {
201                    ((FolderRow)item).setIsSelected(false);
202                    final Folder folder = ((FolderRow)item).getFolder();
203                    mOperations.put(folder.folderUri.fullUri,
204                            new FolderOperation(folder, false));
205                 }
206             }
207         }
208         row.setIsSelected(add);
209         mAdapter.notifyDataSetChanged();
210         final Folder folder = row.getFolder();
211         mOperations.put(folder.folderUri.fullUri, new FolderOperation(folder, add));
212     }
213 
214     @Override
onClick(DialogInterface dialog, int which)215     public void onClick(DialogInterface dialog, int which) {
216         switch (which) {
217             case DialogInterface.BUTTON_POSITIVE:
218                 getConversationUpdater().assignFolder(mOperations.values(), mTarget, mBatch,
219                         true /* showUndo */, false /* isMoveTo */);
220                 break;
221             case DialogInterface.BUTTON_NEGATIVE:
222                 break;
223             default:
224                 break;
225         }
226     }
227 }
228