1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.mail.ui; 18 19 import android.app.Fragment; 20 import android.app.FragmentTransaction; 21 import android.appwidget.AppWidgetManager; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.database.DataSetObservable; 27 import android.database.DataSetObserver; 28 import android.os.Bundle; 29 import android.support.v7.app.ActionBarActivity; 30 import android.view.DragEvent; 31 import android.view.View; 32 import android.view.View.OnClickListener; 33 import android.widget.Button; 34 import android.widget.ListView; 35 36 import com.android.bitmap.BitmapCache; 37 import com.android.mail.R; 38 import com.android.mail.bitmap.ContactResolver; 39 import com.android.mail.providers.Account; 40 import com.android.mail.providers.Folder; 41 import com.android.mail.providers.FolderWatcher; 42 import com.android.mail.utils.LogTag; 43 import com.android.mail.utils.LogUtils; 44 import com.android.mail.utils.MailObservable; 45 import com.android.mail.utils.Utils; 46 import com.android.mail.utils.VeiledAddressMatcher; 47 import com.android.mail.widget.WidgetProvider; 48 49 import java.util.ArrayList; 50 51 /** 52 * This activity displays the list of available folders for the current account. 53 */ 54 public class FolderSelectionActivity extends ActionBarActivity implements OnClickListener, 55 DialogInterface.OnClickListener, FolderChangeListener, ControllableActivity, 56 FolderSelector { 57 public static final String EXTRA_ACCOUNT_SHORTCUT = "account-shortcut"; 58 59 private static final String LOG_TAG = LogTag.getLogTag(); 60 61 private static final int CONFIGURE = 0; 62 63 private static final int VIEW = 1; 64 65 private Account mAccount; 66 private Folder mSelectedFolder; 67 private boolean mConfigureShortcut; 68 protected boolean mConfigureWidget; 69 private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; 70 private int mMode = -1; 71 /** Empty placeholder for communicating to the consumer of the drawer observer. */ 72 private final DataSetObservable mFolderOrAccountObservers = 73 new MailObservable("FolderOrAccount"); 74 75 private final AccountController mAccountController = new AccountController() { 76 @Override 77 public void registerAccountObserver(DataSetObserver observer) { 78 // Do nothing 79 } 80 81 @Override 82 public void unregisterAccountObserver(DataSetObserver observer) { 83 // Do nothing 84 } 85 86 @Override 87 public Account getAccount() { 88 return mAccount; 89 } 90 91 @Override 92 public void registerAllAccountObserver(DataSetObserver observer) { 93 // Do nothing 94 } 95 96 @Override 97 public void unregisterAllAccountObserver(DataSetObserver observer) { 98 // Do nothing 99 } 100 101 @Override 102 public Account[] getAllAccounts() { 103 return new Account[]{mAccount}; 104 } 105 106 @Override 107 public VeiledAddressMatcher getVeiledAddressMatcher() { 108 return null; 109 } 110 111 @Override 112 public void switchToDefaultInboxOrChangeAccount(Account account) { 113 // Never gets called, so do nothing here. 114 LogUtils.wtf(LOG_TAG,"FolderSelectionActivity.switchToDefaultInboxOrChangeAccount() " + 115 "called when NOT expected."); 116 } 117 118 @Override 119 public void registerFolderOrAccountChangedObserver(final DataSetObserver observer) { 120 mFolderOrAccountObservers.registerObserver(observer); 121 } 122 123 @Override 124 public void unregisterFolderOrAccountChangedObserver(final DataSetObserver observer) { 125 mFolderOrAccountObservers.unregisterObserver(observer); 126 } 127 128 /** 129 * Since there is no drawer to wait for, notifyChanged to the observers. 130 */ 131 @Override 132 public void closeDrawer(final boolean hasNewFolderOrAccount, 133 Account account, Folder folder) { 134 mFolderOrAccountObservers.notifyChanged(); 135 } 136 137 @Override 138 public void setFolderWatcher(FolderWatcher watcher) { 139 // Unsupported. 140 } 141 142 @Override 143 public boolean isDrawerPullEnabled() { 144 // Unsupported 145 return false; 146 } 147 148 @Override 149 public int getFolderListViewChoiceMode() { 150 return ListView.CHOICE_MODE_NONE; 151 } 152 }; 153 154 @Override 155 public void onCreate(Bundle icicle) { 156 super.onCreate(icicle); 157 158 setContentView(R.layout.folders_activity); 159 160 final Intent intent = getIntent(); 161 final String action = intent.getAction(); 162 mConfigureShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(action); 163 mConfigureWidget = AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action); 164 if (!mConfigureShortcut && !mConfigureWidget) { 165 LogUtils.wtf(LOG_TAG, "unexpected intent: %s", intent); 166 } 167 if (mConfigureShortcut || mConfigureWidget) { 168 mMode = CONFIGURE; 169 } else { 170 mMode = VIEW; 171 } 172 173 if (mConfigureWidget) { 174 mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 175 AppWidgetManager.INVALID_APPWIDGET_ID); 176 if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { 177 LogUtils.wtf(LOG_TAG, "invalid widgetId"); 178 } 179 } 180 181 mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT_SHORTCUT); 182 final Button firstButton = (Button) findViewById(R.id.first_button); 183 firstButton.setVisibility(View.VISIBLE); 184 // TODO(mindyp) disable the manage folders buttons until we have a manage folders screen. 185 if (mMode == VIEW) { 186 firstButton.setEnabled(false); 187 } 188 firstButton.setOnClickListener(this); 189 createFolderListFragment(FolderListFragment.ofTopLevelTree(mAccount.folderListUri, 190 getExcludedFolderTypes())); 191 } 192 193 /** 194 * Create a Fragment showing this folder and its children. 195 */ 196 private void createFolderListFragment(Fragment fragment) { 197 final FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); 198 fragmentTransaction.replace(R.id.content_pane, fragment); 199 fragmentTransaction.commitAllowingStateLoss(); 200 } 201 202 /** 203 * Gets an {@link ArrayList} of canonical names of any folders to exclude from displaying. 204 * By default, this list is empty. 205 * 206 * @return An {@link ArrayList} of folder canonical names 207 */ 208 protected ArrayList<Integer> getExcludedFolderTypes() { 209 return new ArrayList<Integer>(); 210 } 211 212 @Override 213 protected void onResume() { 214 super.onResume(); 215 216 // TODO: (mindyp) Make sure we're operating on the same account as 217 // before. If the user switched accounts, switch back. 218 } 219 220 @Override 221 public void onClick(View v) { 222 final int id = v.getId(); 223 if (id == R.id.first_button) { 224 if (mMode == CONFIGURE) { 225 doCancel(); 226 } else { 227 // TODO (mindyp): open manage folders screen. 228 } 229 } 230 } 231 232 private void doCancel() { 233 setResult(RESULT_CANCELED); 234 finish(); 235 } 236 237 /** 238 * Create a widget for the specified account and folder 239 */ 240 protected void createWidget(int id, Account account, Folder selectedFolder) { 241 WidgetProvider.updateWidget(this, id, account, selectedFolder.type, 242 selectedFolder.capabilities, selectedFolder.folderUri.fullUri, 243 selectedFolder.conversationListUri, selectedFolder.name); 244 final Intent result = new Intent(); 245 result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id); 246 setResult(RESULT_OK, result); 247 finish(); 248 } 249 250 @Override 251 public void onClick(DialogInterface dialog, int which) { 252 if (which == DialogInterface.BUTTON_POSITIVE) { 253 // The only dialog that is 254 createWidget(mAppWidgetId, mAccount, mSelectedFolder); 255 } else { 256 doCancel(); 257 } 258 } 259 260 @Override 261 public void onFolderChanged(Folder folder, final boolean force) { 262 if (!folder.equals(mSelectedFolder)) { 263 mSelectedFolder = folder; 264 Intent resultIntent = new Intent(); 265 266 if (mConfigureShortcut) { 267 /* 268 * Create the shortcut Intent based on it with the additional 269 * information that we have in this activity: name of the 270 * account, calculate the human readable name of the folder and 271 * use it as the shortcut name, etc... 272 */ 273 final Intent clickIntent = Utils.createViewFolderIntent(this, 274 mSelectedFolder.folderUri.fullUri, mAccount); 275 resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, clickIntent); 276 resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, 277 Intent.ShortcutIconResource.fromContext(this, 278 R.mipmap.ic_launcher_shortcut_folder)); 279 /** 280 * Note: Email1 created shortcuts using R.mipmap#ic_launcher_email 281 * so don't delete that resource until we have an upgrade/migration solution 282 */ 283 284 final CharSequence humanFolderName = mSelectedFolder.name; 285 286 resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, humanFolderName); 287 288 // Now ask the user what name they want for this shortcut. Pass 289 // the 290 // shortcut intent that we just created, the user can modify the 291 // folder in 292 // ShortcutNameActivity. 293 final Intent shortcutNameIntent = new Intent(this, ShortcutNameActivity.class); 294 shortcutNameIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY 295 | Intent.FLAG_ACTIVITY_FORWARD_RESULT); 296 shortcutNameIntent.putExtra(ShortcutNameActivity.EXTRA_FOLDER_CLICK_INTENT, 297 resultIntent); 298 shortcutNameIntent.putExtra(ShortcutNameActivity.EXTRA_SHORTCUT_NAME, 299 humanFolderName); 300 301 startActivity(shortcutNameIntent); 302 finish(); 303 } else if (mConfigureWidget) { 304 createWidget(mAppWidgetId, mAccount, mSelectedFolder); 305 } 306 } 307 } 308 309 @Override 310 public Context getActivityContext() { 311 return this; 312 } 313 314 @Override 315 public ViewMode getViewMode() { 316 return null; 317 } 318 319 @Override 320 public ConversationListCallbacks getListHandler() { 321 return null; 322 } 323 324 @Override 325 public FolderChangeListener getFolderChangeListener() { 326 return this; 327 } 328 329 @Override 330 public ConversationSelectionSet getSelectedSet() { 331 return null; 332 } 333 334 private Folder mNavigatedFolder; 335 @Override 336 public void onFolderSelected(Folder folder) { 337 if (folder.hasChildren && !folder.equals(mNavigatedFolder)) { 338 mNavigatedFolder = folder; 339 // Replace this fragment with a new FolderListFragment 340 // showing this folder's children if we are not already looking 341 // at the child view for this folder. 342 createFolderListFragment(FolderListFragment.ofTree(folder)); 343 return; 344 } 345 onFolderChanged(folder, false /* force */); 346 } 347 348 @Override 349 public FolderSelector getFolderSelector() { 350 return this; 351 } 352 353 @Override 354 public boolean supportsDrag(DragEvent event, Folder folder) { 355 return false; 356 } 357 358 @Override 359 public void handleDrop(DragEvent event, Folder folder) { 360 // Do nothing. 361 } 362 363 @Override 364 public void onUndoAvailable(ToastBarOperation undoOp) { 365 // Do nothing. 366 } 367 368 @Override 369 public Folder getHierarchyFolder() { 370 return null; 371 } 372 373 @Override 374 public ConversationUpdater getConversationUpdater() { 375 return null; 376 } 377 378 @Override 379 public ErrorListener getErrorListener() { 380 return null; 381 } 382 383 @Override 384 public void setPendingToastOperation(ToastBarOperation op) { 385 // Do nothing. 386 } 387 388 @Override 389 public ToastBarOperation getPendingToastOperation() { 390 return null; 391 } 392 393 @Override 394 public FolderController getFolderController() { 395 return null; 396 } 397 398 @Override 399 public void onAnimationEnd(AnimatedAdapter animatedAdapter) { 400 } 401 402 @Override 403 public AccountController getAccountController() { 404 return mAccountController; 405 } 406 407 @Override 408 public void onFooterViewErrorActionClick(Folder folder, int errorStatus) { 409 // Unsupported 410 } 411 412 @Override 413 public void onFooterViewLoadMoreClick(Folder folder) { 414 // Unsupported 415 } 416 417 @Override 418 public void startDragMode() { 419 // Unsupported 420 } 421 422 @Override 423 public void stopDragMode() { 424 // Unsupported 425 } 426 427 @Override 428 public RecentFolderController getRecentFolderController() { 429 // Unsupported 430 return null; 431 } 432 433 @Override 434 public DrawerController getDrawerController() { 435 // Unsupported 436 return null; 437 } 438 439 @Override 440 public KeyboardNavigationController getKeyboardNavigationController() { 441 // Unsupported 442 return null; 443 } 444 445 @Override 446 public boolean isAccessibilityEnabled() { 447 // Unsupported 448 return true; 449 } 450 451 @Override 452 public ConversationListHelper getConversationListHelper() { 453 // Unsupported 454 return null; 455 } 456 457 @Override 458 public FragmentLauncher getFragmentLauncher() { 459 // Unsupported 460 return null; 461 } 462 463 @Override 464 public ContactLoaderCallbacks getContactLoaderCallbacks() { 465 // Unsupported 466 return null; 467 } 468 469 @Override 470 public ContactResolver getContactResolver(ContentResolver resolver, BitmapCache bitmapCache) { 471 // Unsupported 472 return null; 473 } 474 475 @Override 476 public BitmapCache getSenderImageCache() { 477 // Unsupported 478 return null; 479 } 480 481 @Override 482 public void resetSenderImageCache() { 483 // Unsupported 484 } 485 486 @Override 487 public void showHelp(Account account, int viewMode) { 488 //Unsupported 489 } 490 } 491