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 18package com.android.mail.ui; 19 20import android.app.AlertDialog; 21import android.app.LoaderManager; 22import android.content.Context; 23import android.content.CursorLoader; 24import android.content.DialogInterface; 25import android.content.Loader; 26import android.database.Cursor; 27import android.net.Uri; 28import android.os.Bundle; 29 30import com.android.mail.R; 31import com.android.mail.providers.Conversation; 32import com.android.mail.providers.Folder; 33import com.android.mail.providers.UIProvider; 34import com.android.mail.providers.UIProvider.FolderType; 35import com.android.mail.ui.FolderSelectorAdapter.FolderRow; 36import com.android.mail.utils.Utils; 37import com.google.common.collect.ImmutableSet; 38 39import java.util.Arrays; 40import java.util.HashMap; 41import java.util.HashSet; 42import java.util.List; 43import java.util.Map; 44import 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 */ 50public 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 108 final HashSet<String> checked = new HashSet<String>(); 109 for (final Conversation conversation : mTarget) { 110 final List<Folder> rawFolders = conversation.getRawFolders(); 111 if (rawFolders != null && rawFolders.size() > 0) { 112 // Parse the raw folders and get all the uris. 113 checked.addAll(Arrays.asList(Folder.getUriArray(rawFolders))); 114 } else { 115 // There are no folders for this conversation, so it must 116 // belong to the folder we are currently looking at. 117 checked.add(mCurrentFolder.folderUri.fullUri.toString()); 118 } 119 } 120 final Set<String> originalChecked = ImmutableSet.copyOf(checked); 121 for (final Map.Entry<Uri, FolderOperation> entry : mOperations.entrySet()) { 122 if (entry.getValue().mAdd) { 123 checked.add(entry.getKey().toString()); 124 } else { 125 checked.remove(entry.getKey().toString()); 126 } 127 } 128 mAdapter.clearSections(); 129 // TODO(mindyp) : bring this back in UR8 when Email providers 130 // will have divided folder sections. 131 /* final String[] headers = mContext.getResources() 132 .getStringArray(R.array.moveto_folder_sections); 133 // Currently, the number of adapters are assumed to match the 134 // number of headers in the string array. 135 mAdapter.addSection(new SystemFolderSelectorAdapter(mContext, 136 foldersCursor, checked, R.layout.multi_folders_view, null)); 137 138 // TODO(mindyp): we currently do not support frequently moved to 139 // folders, at headers[1]; need to define what that means.*/ 140 141 Cursor c = AddableFolderSelectorAdapter.filterFolders(data, 142 ImmutableSet.of(FolderType.INBOX_SECTION), originalChecked, 143 true /* includeOnlyInitiallySelected */); 144 if (c.getCount() > 0) { 145 mAdapter.addSection(new AddableFolderSelectorAdapter(context, c, 146 checked, R.layout.multi_folders_view)); 147 } 148 149 c = AddableFolderSelectorAdapter.filterFolders(data, 150 ImmutableSet.of(FolderType.INBOX_SECTION), originalChecked, 151 false /* includeOnlyInitiallySelected */); 152 if (c.getCount() > 0) { 153 mAdapter.addSection(new AddableFolderSelectorAdapter(context, c, 154 checked, R.layout.multi_folders_view)); 155 } 156 157 dialog.getListView().setAdapter(mAdapter); 158 } 159 160 @Override 161 public void onLoaderReset(Loader<Cursor> loader) { 162 mAdapter.clearSections(); 163 } 164 }); 165 } 166 167 @Override 168 public void onSaveInstanceState(Bundle outState) { 169 super.onSaveInstanceState(outState); 170 outState.putParcelableArray(SAVESTATE_OPERATIONS_TAG, 171 mOperations.values().toArray(new FolderOperation[mOperations.size()])); 172 } 173 174 @Override 175 protected void onListItemClick(int position) { 176 final Object item = mAdapter.getItem(position); 177 if (item instanceof FolderRow) { 178 update((FolderRow) item); 179 } 180 } 181 182 /** 183 * Call this to update the state of folders as a result of them being 184 * selected / de-selected. 185 * 186 * @param row The item being updated. 187 */ 188 private void update(FolderSelectorAdapter.FolderRow row) { 189 final boolean add = !row.isSelected(); 190 if (mSingle) { 191 if (!add) { 192 // This would remove the check on a single radio button, so just 193 // return. 194 return; 195 } 196 // Clear any other checked items. 197 for (int i = 0, size = mAdapter.getCount(); i < size; i++) { 198 final Object item = mAdapter.getItem(i); 199 if (item instanceof FolderRow) { 200 ((FolderRow)item).setIsSelected(false); 201 final Folder folder = ((FolderRow)item).getFolder(); 202 mOperations.put(folder.folderUri.fullUri, 203 new FolderOperation(folder, false)); 204 } 205 } 206 } 207 row.setIsSelected(add); 208 mAdapter.notifyDataSetChanged(); 209 final Folder folder = row.getFolder(); 210 mOperations.put(folder.folderUri.fullUri, new FolderOperation(folder, add)); 211 } 212 213 @Override 214 public void onClick(DialogInterface dialog, int which) { 215 switch (which) { 216 case DialogInterface.BUTTON_POSITIVE: 217 getConversationUpdater().assignFolder(mOperations.values(), mTarget, mBatch, 218 true /* showUndo */, false /* isMoveTo */); 219 break; 220 case DialogInterface.BUTTON_NEGATIVE: 221 break; 222 default: 223 break; 224 } 225 } 226} 227