FolderSelectionActivity.java revision 405a344937675f57fc9c6988b2b124410a270f13
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.Fragment;
20import android.app.FragmentTransaction;
21import android.appwidget.AppWidgetManager;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.database.DataSetObservable;
27import android.database.DataSetObserver;
28import android.os.Bundle;
29import android.support.v7.app.ActionBarActivity;
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.bitmap.BitmapCache;
37import com.android.mail.R;
38import com.android.mail.bitmap.ContactResolver;
39import com.android.mail.providers.Account;
40import com.android.mail.providers.Folder;
41import com.android.mail.providers.FolderWatcher;
42import com.android.mail.providers.SearchRecentSuggestionsProvider;
43import com.android.mail.utils.LogTag;
44import com.android.mail.utils.LogUtils;
45import com.android.mail.utils.MailObservable;
46import com.android.mail.utils.Utils;
47import com.android.mail.utils.VeiledAddressMatcher;
48import com.android.mail.widget.WidgetProvider;
49
50import java.util.ArrayList;
51
52/**
53 * This activity displays the list of available folders for the current account.
54 */
55public class FolderSelectionActivity extends ActionBarActivity implements OnClickListener,
56        DialogInterface.OnClickListener, ControllableActivity,
57        FolderSelector {
58    public static final String EXTRA_ACCOUNT_SHORTCUT = "account-shortcut";
59
60    private static final String LOG_TAG = LogTag.getLogTag();
61
62    private static final int CONFIGURE = 0;
63
64    private static final int VIEW = 1;
65
66    private Account mAccount;
67    private Folder mSelectedFolder;
68    private boolean mConfigureShortcut;
69    protected boolean mConfigureWidget;
70    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
71    private int mMode = -1;
72    /** Empty placeholder for communicating to the consumer of the drawer observer. */
73    private final DataSetObservable mFolderOrAccountObservers =
74            new MailObservable("FolderOrAccount");
75
76    private final AccountController mAccountController = new AccountController() {
77        @Override
78        public void registerAccountObserver(DataSetObserver observer) {
79            // Do nothing
80        }
81
82        @Override
83        public void unregisterAccountObserver(DataSetObserver observer) {
84            // Do nothing
85        }
86
87        @Override
88        public Account getAccount() {
89            return mAccount;
90        }
91
92        @Override
93        public void registerAllAccountObserver(DataSetObserver observer) {
94            // Do nothing
95        }
96
97        @Override
98        public void unregisterAllAccountObserver(DataSetObserver observer) {
99            // Do nothing
100        }
101
102        @Override
103        public Account[] getAllAccounts() {
104            return new Account[]{mAccount};
105        }
106
107        @Override
108        public VeiledAddressMatcher getVeiledAddressMatcher() {
109            return null;
110        }
111
112        @Override
113        public void switchToDefaultInboxOrChangeAccount(Account account) {
114            // Never gets called, so do nothing here.
115            LogUtils.wtf(LOG_TAG,"FolderSelectionActivity.switchToDefaultInboxOrChangeAccount() " +
116                    "called when NOT expected.");
117        }
118
119        @Override
120        public void registerFolderOrAccountChangedObserver(final DataSetObserver observer) {
121            mFolderOrAccountObservers.registerObserver(observer);
122        }
123
124        @Override
125        public void unregisterFolderOrAccountChangedObserver(final DataSetObserver observer) {
126            mFolderOrAccountObservers.unregisterObserver(observer);
127        }
128
129        /**
130         * Since there is no drawer to wait for, notifyChanged to the observers.
131         */
132        @Override
133        public void closeDrawer(final boolean hasNewFolderOrAccount,
134                Account account, Folder folder) {
135            mFolderOrAccountObservers.notifyChanged();
136        }
137
138        @Override
139        public void setFolderWatcher(FolderWatcher watcher) {
140            // Unsupported.
141        }
142
143        @Override
144        public boolean isDrawerPullEnabled() {
145            // Unsupported
146            return false;
147        }
148
149        @Override
150        public int getFolderListViewChoiceMode() {
151            return ListView.CHOICE_MODE_NONE;
152        }
153    };
154
155    @Override
156    public void onCreate(Bundle icicle) {
157        super.onCreate(icicle);
158
159        setContentView(R.layout.folders_activity);
160
161        final Intent intent = getIntent();
162        final String action = intent.getAction();
163        mConfigureShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(action);
164        mConfigureWidget = AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action);
165        if (!mConfigureShortcut && !mConfigureWidget) {
166            LogUtils.wtf(LOG_TAG, "unexpected intent: %s", intent);
167        }
168        if (mConfigureShortcut || mConfigureWidget) {
169            mMode = CONFIGURE;
170        } else {
171            mMode = VIEW;
172        }
173
174        if (mConfigureWidget) {
175            mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
176                    AppWidgetManager.INVALID_APPWIDGET_ID);
177            if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
178                LogUtils.wtf(LOG_TAG, "invalid widgetId");
179            }
180        }
181
182        mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT_SHORTCUT);
183        final Button firstButton = (Button) findViewById(R.id.first_button);
184        firstButton.setVisibility(View.VISIBLE);
185        // TODO(mindyp) disable the manage folders buttons until we have a manage folders screen.
186        if (mMode == VIEW) {
187            firstButton.setEnabled(false);
188        }
189        firstButton.setOnClickListener(this);
190        createFolderListFragment(FolderListFragment.ofTopLevelTree(mAccount.folderListUri,
191                getExcludedFolderTypes()));
192    }
193
194    /**
195     * Create a Fragment showing this folder and its children.
196     */
197    private void createFolderListFragment(Fragment fragment) {
198        final FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
199        fragmentTransaction.replace(R.id.content_pane, fragment);
200        fragmentTransaction.commitAllowingStateLoss();
201    }
202
203    /**
204     * Gets an {@link ArrayList} of canonical names of any folders to exclude from displaying.
205     * By default, this list is empty.
206     *
207     * @return An {@link ArrayList} of folder canonical names
208     */
209    protected ArrayList<Integer> getExcludedFolderTypes() {
210        return new ArrayList<Integer>();
211    }
212
213    @Override
214    protected void onResume() {
215        super.onResume();
216
217        // TODO: (mindyp) Make sure we're operating on the same account as
218        // before. If the user switched accounts, switch back.
219    }
220
221    @Override
222    public void onClick(View v) {
223        final int id = v.getId();
224        if (id == R.id.first_button) {
225            if (mMode == CONFIGURE) {
226                doCancel();
227            } else {
228                // TODO (mindyp): open manage folders screen.
229            }
230        }
231    }
232
233    private void doCancel() {
234        setResult(RESULT_CANCELED);
235        finish();
236    }
237
238    /**
239     * Create a widget for the specified account and folder
240     */
241    protected void createWidget(int id, Account account, Folder selectedFolder) {
242        WidgetProvider.updateWidget(this, id, account, selectedFolder.type,
243                selectedFolder.capabilities, selectedFolder.folderUri.fullUri,
244                selectedFolder.conversationListUri, selectedFolder.name);
245        final Intent result = new Intent();
246        result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id);
247        setResult(RESULT_OK, result);
248        finish();
249    }
250
251    @Override
252    public void onClick(DialogInterface dialog, int which) {
253        if (which == DialogInterface.BUTTON_POSITIVE) {
254            // The only dialog that is
255            createWidget(mAppWidgetId, mAccount, mSelectedFolder);
256        } else {
257            doCancel();
258        }
259    }
260
261    private 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 ConversationSelectionSet getSelectedSet() {
326        return null;
327    }
328
329    private Folder mNavigatedFolder;
330    @Override
331    public void onFolderSelected(Folder folder) {
332        if (folder.hasChildren && !folder.equals(mNavigatedFolder)) {
333            mNavigatedFolder = folder;
334            // Replace this fragment with a new FolderListFragment
335            // showing this folder's children if we are not already looking
336            // at the child view for this folder.
337            createFolderListFragment(FolderListFragment.ofTree(folder));
338            return;
339        }
340        onFolderChanged(folder, false /* force */);
341    }
342
343    @Override
344    public FolderSelector getFolderSelector() {
345        return this;
346    }
347
348    @Override
349    public boolean supportsDrag(DragEvent event, Folder folder) {
350        return false;
351    }
352
353    @Override
354    public void handleDrop(DragEvent event, Folder folder) {
355        // Do nothing.
356    }
357
358    @Override
359    public void onUndoAvailable(ToastBarOperation undoOp) {
360        // Do nothing.
361    }
362
363    @Override
364    public Folder getHierarchyFolder() {
365        return null;
366    }
367
368    @Override
369    public ConversationUpdater getConversationUpdater() {
370        return null;
371    }
372
373    @Override
374    public ErrorListener getErrorListener() {
375        return null;
376    }
377
378    @Override
379    public void setPendingToastOperation(ToastBarOperation op) {
380        // Do nothing.
381    }
382
383    @Override
384    public ToastBarOperation getPendingToastOperation() {
385        return null;
386    }
387
388    @Override
389    public FolderController getFolderController() {
390        return null;
391    }
392
393    @Override
394    public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
395    }
396
397    @Override
398    public AccountController getAccountController() {
399        return mAccountController;
400    }
401
402    @Override
403    public void onFooterViewErrorActionClick(Folder folder, int errorStatus) {
404        // Unsupported
405    }
406
407    @Override
408    public void onFooterViewLoadMoreClick(Folder folder) {
409        // Unsupported
410    }
411
412    @Override
413    public void startDragMode() {
414        // Unsupported
415    }
416
417    @Override
418    public void stopDragMode() {
419        // Unsupported
420    }
421
422    @Override
423    public RecentFolderController getRecentFolderController() {
424        // Unsupported
425        return null;
426    }
427
428    @Override
429    public DrawerController getDrawerController() {
430        // Unsupported
431        return null;
432    }
433
434    @Override
435    public KeyboardNavigationController getKeyboardNavigationController() {
436        // Unsupported
437        return null;
438    }
439
440    @Override
441    public boolean isAccessibilityEnabled() {
442        // Unsupported
443        return true;
444    }
445
446    @Override
447    public ConversationListHelper getConversationListHelper() {
448        // Unsupported
449        return null;
450    }
451
452    @Override
453    public FragmentLauncher getFragmentLauncher() {
454        // Unsupported
455        return null;
456    }
457
458    @Override
459    public ContactLoaderCallbacks getContactLoaderCallbacks() {
460        // Unsupported
461        return null;
462    }
463
464    @Override
465    public ContactResolver getContactResolver(ContentResolver resolver, BitmapCache bitmapCache) {
466        // Unsupported
467        return null;
468    }
469
470    @Override
471    public BitmapCache getSenderImageCache() {
472        // Unsupported
473        return null;
474    }
475
476    @Override
477    public void resetSenderImageCache() {
478        // Unsupported
479    }
480
481    @Override
482    public void showHelp(Account account, int viewMode) {
483        // Unsupported
484    }
485}
486