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