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    private Folder mNavigatedFolder;
349    @Override
350    public void onFolderSelected(Folder folder) {
351        if (folder.hasChildren && !folder.equals(mNavigatedFolder)) {
352            mNavigatedFolder = folder;
353            // Replace this fragment with a new FolderListFragment
354            // showing this folder's children if we are not already looking
355            // at the child view for this folder.
356            createFolderListFragment(FolderListFragment.ofTree(folder));
357            return;
358        }
359        onFolderChanged(folder, false /* force */);
360    }
361
362    @Override
363    public FolderSelector getFolderSelector() {
364        return this;
365    }
366
367    @Override
368    public boolean supportsDrag(DragEvent event, Folder folder) {
369        return false;
370    }
371
372    @Override
373    public void handleDrop(DragEvent event, Folder folder) {
374        // Do nothing.
375    }
376
377    @Override
378    public void onUndoAvailable(ToastBarOperation undoOp) {
379        // Do nothing.
380    }
381
382    @Override
383    public Folder getHierarchyFolder() {
384        return null;
385    }
386
387    @Override
388    public ConversationUpdater getConversationUpdater() {
389        return null;
390    }
391
392    @Override
393    public ErrorListener getErrorListener() {
394        return null;
395    }
396
397    @Override
398    public void setPendingToastOperation(ToastBarOperation op) {
399        // Do nothing.
400    }
401
402    @Override
403    public ToastBarOperation getPendingToastOperation() {
404        return null;
405    }
406
407    @Override
408    public FolderController getFolderController() {
409        return null;
410    }
411
412    @Override
413    public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
414    }
415
416    @Override
417    public AccountController getAccountController() {
418        return mAccountController;
419    }
420
421    @Override
422    public void onFooterViewErrorActionClick(Folder folder, int errorStatus) {
423        // Unsupported
424    }
425
426    @Override
427    public void onFooterViewLoadMoreClick(Folder folder) {
428        // Unsupported
429    }
430
431    @Override
432    public void startDragMode() {
433        // Unsupported
434    }
435
436    @Override
437    public void stopDragMode() {
438        // Unsupported
439    }
440
441    @Override
442    public RecentFolderController getRecentFolderController() {
443        // Unsupported
444        return null;
445    }
446
447    @Override
448    public UpOrBackController getUpOrBackController() {
449        // Unsupported
450        return null;
451    }
452
453    @Override
454    public boolean isAccessibilityEnabled() {
455        // Unsupported
456        return true;
457    }
458
459    @Override
460    public ConversationListHelper getConversationListHelper() {
461        // Unsupported
462        return null;
463    }
464
465    @Override
466    public FragmentLauncher getFragmentLauncher() {
467        // Unsupported
468        return null;
469    }
470}
471