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