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