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 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 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 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 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 */ 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 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