ShortcutPickerFragment.java revision 5675ea88d3cc4ba9934d2a54fee008fd324d711f
1/*
2 * Copyright (C) 2011 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.email.activity;
18
19import com.android.email.R;
20import com.android.emailcommon.Logging;
21import com.android.emailcommon.provider.EmailContent.Account;
22import com.android.emailcommon.provider.EmailContent.AccountColumns;
23import com.android.emailcommon.provider.EmailContent.MailboxColumns;
24import com.android.emailcommon.provider.HostAuth;
25import com.android.emailcommon.provider.Mailbox;
26
27import android.app.Activity;
28import android.app.ListFragment;
29import android.app.LoaderManager.LoaderCallbacks;
30import android.content.Context;
31import android.content.CursorLoader;
32import android.content.Intent;
33import android.content.Loader;
34import android.database.Cursor;
35import android.os.Bundle;
36import android.os.Parcelable;
37import android.util.Log;
38import android.view.View;
39import android.widget.AdapterView;
40import android.widget.ListView;
41import android.widget.SimpleCursorAdapter;
42import android.widget.AdapterView.OnItemClickListener;
43
44// TODO restructure this into three classes -- a base class w/ two sub-classes. Intead of using
45// selectingMailbox(), we'd just define the proper methods in the sub-class.
46
47/**
48 * Fragment containing a list of accounts to show during shortcut creation.
49 */
50public abstract class ShortcutPickerFragment extends ListFragment
51        implements OnItemClickListener, LoaderCallbacks<Cursor> {
52    /**
53     * If true, creates pre-honeycomb style shortcuts. This allows developers to test launching
54     * the app from old style shortcuts (which point sat MessageList rather than Welcome) without
55     * actually carrying over shortcuts from previous versions.
56     */
57    private final static boolean TEST_CREATE_OLD_STYLE_SHORTCUT = false; // DO NOT SUBMIT WITH TRUE
58    private final static int LOADER_ID = 0;
59    private final static int[] TO_VIEWS = new int[] {
60        android.R.id.text1,
61    };
62
63    /** Cursor adapter that provides either the account or mailbox list */
64    private SimpleCursorAdapter mAdapter;
65
66    @Override
67    public void onAttach(Activity activity) {
68        super.onAttach(activity);
69
70        final String[] fromColumns = getFromColumns();
71        mAdapter = new SimpleCursorAdapter(activity,
72            android.R.layout.simple_expandable_list_item_1, null, fromColumns, TO_VIEWS, 0);
73        setListAdapter(mAdapter);
74
75        getLoaderManager().initLoader(LOADER_ID, null, this);
76    }
77
78    @Override
79    public void onActivityCreated(Bundle savedInstanceState) {
80        super.onActivityCreated(savedInstanceState);
81
82        ListView listView = getListView();
83        listView.setOnItemClickListener(this);
84        listView.setItemsCanFocus(false);
85    }
86
87    @Override
88    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
89        // No accounts; close the dialog
90        if (data.getCount() == 0) {
91            // TODO what's the proper handling if the mailbox list is '0'? display toast?
92            getActivity().finish();
93            return;
94        }
95        // TODO special handle case where there is one account or one mailbox; the user shouldn't
96        //      be forced to select anything in either of those cases.
97        mAdapter.swapCursor(data);
98    }
99
100    @Override
101    public void onLoaderReset(Loader<Cursor> loader) {
102        mAdapter.swapCursor(null);
103    }
104
105    /** Returns the cursor columns to map into list */
106    abstract String[] getFromColumns();
107
108    /**
109     * This function creates a shortcut and returns it to the caller.  There are actually two
110     * intents that you will send back.
111     *
112     * The first intent serves as a container for the shortcut and is returned to the launcher by
113     * setResult().  This intent must contain three fields:
114     *
115     * <ul>
116     * <li>{@link android.content.Intent#EXTRA_SHORTCUT_INTENT} The shortcut intent.</li>
117     * <li>{@link android.content.Intent#EXTRA_SHORTCUT_NAME} The text that will be displayed with
118     * the shortcut.</li>
119     * <li>{@link android.content.Intent#EXTRA_SHORTCUT_ICON} The shortcut's icon, if provided as a
120     * bitmap, <i>or</i> {@link android.content.Intent#EXTRA_SHORTCUT_ICON_RESOURCE} if provided as
121     * a drawable resource.</li>
122     * </ul>
123     *
124     * If you use a simple drawable resource, note that you must wrapper it using
125     * {@link android.content.Intent.ShortcutIconResource}, as shown below.  This is required so
126     * that the launcher can access resources that are stored in your application's .apk file.  If
127     * you return a bitmap, such as a thumbnail, you can simply put the bitmap into the extras
128     * bundle using {@link android.content.Intent#EXTRA_SHORTCUT_ICON}.
129     *
130     * The shortcut intent can be any intent that you wish the launcher to send, when the user
131     * clicks on the shortcut.  Typically this will be {@link android.content.Intent#ACTION_VIEW}
132     * with an appropriate Uri for your content, but any Intent will work here as long as it
133     * triggers the desired action within your Activity.
134     */
135    void setupShortcut(Account account, long mailboxId) {
136        Activity myActivity = getActivity();
137        // First, set up the shortcut intent.
138        final Intent shortcutIntent;
139        if (TEST_CREATE_OLD_STYLE_SHORTCUT) {
140            shortcutIntent = MessageList.createFroyoIntent(myActivity, account);
141            Log.d(Logging.LOG_TAG, "Created old style intent: " + shortcutIntent);
142        } else {
143            String uuid = account.mCompatibilityUuid;
144            shortcutIntent = Welcome.createAccountShortcutIntent(myActivity, uuid, mailboxId);
145        }
146
147        // Then, set up the container intent (the response to the caller)
148        Intent intent = new Intent();
149        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
150        intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, account.getDisplayName());
151        Parcelable iconResource
152                = Intent.ShortcutIconResource.fromContext(myActivity, R.mipmap.ic_launcher_email);
153        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
154
155        // Now, return the result to the launcher
156
157        myActivity.setResult(Activity.RESULT_OK, intent);
158    }
159
160    /** Account picker */
161    public static class AccountShortcutPickerFragment extends ShortcutPickerFragment {
162        private final static String[] ACCOUNT_FROM_COLUMNS = new String[] {
163            AccountColumns.DISPLAY_NAME,
164        };
165
166        @Override
167        public void onActivityCreated(Bundle savedInstanceState) {
168            super.onActivityCreated(savedInstanceState);
169            getActivity().setTitle(R.string.account_shortcut_picker_title);
170        }
171
172        @Override
173        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
174            Cursor cursor = (Cursor) parent.getItemAtPosition(position);
175            Account account = new Account();
176            account.restore(cursor);
177            ShortcutPickerFragment fragment = new MailboxShortcutPickerFragment();
178            final Bundle args = new Bundle();
179            args.putParcelable(MailboxShortcutPickerFragment.ARG_ACCOUNT, account);
180            fragment.setArguments(args);
181            getFragmentManager()
182                .beginTransaction()
183                    .replace(R.id.shortcut_list, fragment)
184                    .addToBackStack(null)
185                    .commit();
186        }
187
188        @Override
189        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
190            Context context = getActivity();
191            // TODO Add ability to insert special account "all accounts"
192            return new CursorLoader(
193                context, Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null, null);
194        }
195
196        @Override
197        String[] getFromColumns() {
198            return ACCOUNT_FROM_COLUMNS;
199        }
200    }
201
202    /** Mailbox picker */
203    public static class MailboxShortcutPickerFragment extends ShortcutPickerFragment {
204        static final String ARG_ACCOUNT = "MailboxShortcutPickerFragment.account";
205        private final static String[] MAILBOX_FROM_COLUMNS = new String[] {
206            MailboxColumns.DISPLAY_NAME,
207        };
208        /** Loader projection used for IMAP & POP3 accounts */
209        private final static String[] IMAP_PROJECTION = new String [] {
210            MailboxColumns.ID, MailboxColumns.SERVER_ID + " as " + MailboxColumns.DISPLAY_NAME
211        };
212        /** Loader projection used for EAS accounts */
213        private final static String[] EAS_PROJECTION = new String [] {
214            MailboxColumns.ID, MailboxColumns.DISPLAY_NAME
215        };
216        // TODO This is identical to MailboxesAdapter#ALL_MAILBOX_SELECTION; any way we can create a
217        // common selection? Move this to the Mailbox class?
218        private final static String ALL_MAILBOX_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?" +
219                " AND " + Mailbox.USER_VISIBLE_MAILBOX_SELECTION;
220
221        /** The currently selected account */
222        private Account mAccount;
223
224        @Override
225        public void onAttach(Activity activity) {
226            // Need to setup the account first thing
227            mAccount = getArguments().getParcelable(ARG_ACCOUNT);
228            super.onAttach(activity);
229        }
230
231        @Override
232        public void onActivityCreated(Bundle savedInstanceState) {
233            super.onActivityCreated(savedInstanceState);
234            getActivity().setTitle(R.string.mailbox_shortcut_picker_title);
235        }
236
237        @Override
238        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
239            Cursor cursor = (Cursor) parent.getItemAtPosition(position);
240            long mailboxId = cursor.getLong(Mailbox.CONTENT_ID_COLUMN);
241            setupShortcut(mAccount, mailboxId);
242            getActivity().finish();
243        }
244
245        @Override
246        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
247            Context context = getActivity();
248            // TODO Add ability to insert special mailboxes like "starred", etc...
249            // TODO Create a fully-qualified path name for Exchange accounts [code should also work
250            //      for MoveMessageToDialog.java]
251            HostAuth recvAuth = mAccount.getOrCreateHostAuthRecv(context);
252            final String[] projection;
253            final String orderBy;
254            if (recvAuth.isEasConnection()) {
255                projection = EAS_PROJECTION;
256                orderBy = MailboxColumns.DISPLAY_NAME;
257            } else {
258                projection = IMAP_PROJECTION;
259                orderBy = MailboxColumns.SERVER_ID;
260            }
261            return new CursorLoader(
262                context, Mailbox.CONTENT_URI, projection, ALL_MAILBOX_SELECTION,
263                new String[] { Long.toString(mAccount.mId) }, orderBy);
264        }
265
266        @Override
267        String[] getFromColumns() {
268            return MAILBOX_FROM_COLUMNS;
269        }
270    }
271}
272