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.content.Context; 21import android.content.DialogInterface; 22import android.database.Cursor; 23import android.net.Uri; 24 25import com.android.mail.R; 26import com.android.mail.providers.Account; 27import com.android.mail.providers.Conversation; 28import com.android.mail.providers.Folder; 29import com.android.mail.providers.UIProvider; 30import com.android.mail.providers.UIProvider.FolderType; 31import com.android.mail.ui.FolderSelectorAdapter.FolderRow; 32import com.android.mail.utils.Utils; 33import com.google.common.collect.ImmutableSet; 34 35import java.util.Arrays; 36import java.util.Collection; 37import java.util.HashMap; 38import java.util.HashSet; 39import java.util.List; 40 41/** 42 * Displays a folder selection dialog for the conversation provided. It allows 43 * the user to mark folders to assign that conversation to. 44 */ 45public class MultiFoldersSelectionDialog extends FolderSelectionDialog { 46 private final boolean mSingle; 47 private final HashMap<Uri, FolderOperation> mOperations; 48 49 /** 50 * Create a new {@link MultiFoldersSelectionDialog}. It is displayed when 51 * the {@link #show()} method is called. 52 * @param context 53 * @param account the current account 54 * @param updater 55 * @param target conversations that are impacted 56 * @param isBatch whether the dialog is shown during Contextual Action Bar 57 * (CAB) mode 58 * @param currentFolder the current folder that the 59 * {@link FolderListFragment} is showing 60 */ 61 public MultiFoldersSelectionDialog(final Context context, final Account account, 62 final ConversationUpdater updater, final Collection<Conversation> target, 63 final boolean isBatch, final Folder currentFolder) { 64 super(context, account, updater, target, isBatch, currentFolder); 65 mSingle = !account 66 .supportsCapability(UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV); 67 mOperations = new HashMap<Uri, FolderOperation>(); 68 69 mBuilder.setTitle(R.string.change_folders_selection_dialog_title); 70 mBuilder.setPositiveButton(R.string.ok, this); 71 } 72 73 @Override 74 protected void updateAdapterInBackground(Context context) { 75 Cursor foldersCursor = null; 76 try { 77 foldersCursor = context.getContentResolver().query( 78 !Utils.isEmpty(mAccount.fullFolderListUri) ? mAccount.fullFolderListUri 79 : mAccount.folderListUri, UIProvider.FOLDERS_PROJECTION, null, null, 80 null); 81 /** All the folders that this conversations is assigned to. */ 82 final HashSet<String> checked = new HashSet<String>(); 83 for (final Conversation conversation : mTarget) { 84 final List<Folder> rawFolders = conversation.getRawFolders(); 85 if (rawFolders != null && rawFolders.size() > 0) { 86 // Parse the raw folders and get all the uris. 87 checked.addAll(Arrays.asList(Folder.getUriArray(rawFolders))); 88 } else { 89 // There are no folders for this conversation, so it must 90 // belong to the folder we are currently looking at. 91 checked.add(mCurrentFolder.folderUri.fullUri.toString()); 92 } 93 } 94 // TODO(mindyp) : bring this back in UR8 when Email providers 95 // will have divided folder sections. 96 /* final String[] headers = mContext.getResources() 97 .getStringArray(R.array.moveto_folder_sections); 98 // Currently, the number of adapters are assumed to match the 99 // number of headers in the string array. 100 mAdapter.addSection(new SystemFolderSelectorAdapter(mContext, 101 foldersCursor, checked, R.layout.multi_folders_view, null)); 102 103 // TODO(mindyp): we currently do not support frequently moved to 104 // folders, at headers[1]; need to define what that means.*/ 105 mAdapter.addSection(new AddableFolderSelectorAdapter(context, 106 AddableFolderSelectorAdapter.filterFolders(foldersCursor, 107 ImmutableSet.of(FolderType.INBOX_SECTION)), checked, 108 R.layout.multi_folders_view, null)); 109 mBuilder.setAdapter(mAdapter, MultiFoldersSelectionDialog.this); 110 } finally { 111 if (foldersCursor != null) { 112 foldersCursor.close(); 113 } 114 } 115 } 116 117 @Override 118 protected void onListItemClick(int position) { 119 final Object item = mAdapter.getItem(position); 120 if (item instanceof FolderRow) { 121 update((FolderRow) item); 122 } 123 } 124 125 /** 126 * Call this to update the state of folders as a result of them being 127 * selected / de-selected. 128 * 129 * @param row The item being updated. 130 */ 131 private final void update(FolderSelectorAdapter.FolderRow row) { 132 final boolean add = !row.isPresent(); 133 if (mSingle) { 134 if (!add) { 135 // This would remove the check on a single radio button, so just 136 // return. 137 return; 138 } 139 // Clear any other checked items. 140 for (int i = 0, size = mAdapter.getCount(); i < size; i++) { 141 final Object item = mAdapter.getItem(i); 142 if (item instanceof FolderRow) { 143 ((FolderRow)item).setIsPresent(false); 144 final Folder folder = ((FolderRow)item).getFolder(); 145 mOperations.put(folder.folderUri.fullUri, 146 new FolderOperation(folder, false)); 147 } 148 } 149 } 150 row.setIsPresent(add); 151 mAdapter.notifyDataSetChanged(); 152 final Folder folder = row.getFolder(); 153 mOperations.put(folder.folderUri.fullUri, new FolderOperation(folder, add)); 154 } 155 156 @Override 157 public void onClick(DialogInterface dialog, int which) { 158 switch (which) { 159 case DialogInterface.BUTTON_POSITIVE: 160 if (mUpdater != null) { 161 mUpdater.assignFolder(mOperations.values(), mTarget, mBatch, 162 true /* showUndo */, false /* isMoveTo */); 163 } 164 break; 165 case DialogInterface.BUTTON_NEGATIVE: 166 break; 167 default: 168 break; 169 } 170 } 171} 172