summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/quicklaunch/BookmarkPicker.java
blob: 32594b6d3d0ee441827f46d02885c9add87d3db5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.quicklaunch;

import com.android.settings.R;

import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * Activity to pick a bookmark that will be returned to the caller.
 * <p>
 * Currently, bookmarks are either:
 * <li> Activities that are in the launcher
 * <li> Activities that are within an app that is capable of being launched with
 * the {@link Intent#ACTION_CREATE_SHORTCUT}.
 */
public class BookmarkPicker extends ListActivity implements SimpleAdapter.ViewBinder {

    private static final String TAG = "BookmarkPicker";

    /** Extra in the returned intent from this activity. */
    public static final String EXTRA_TITLE = "com.android.settings.quicklaunch.TITLE";
    
    /** Extra that should be provided, and will be returned. */
    public static final String EXTRA_SHORTCUT = "com.android.settings.quicklaunch.SHORTCUT";

    /**
     * The request code for the screen to create a bookmark that is WITHIN an
     * application. For example, Gmail can return a bookmark for the inbox
     * folder.
     */
    private static final int REQUEST_CREATE_SHORTCUT = 1;

    /** Intent used to get all the activities that are launch-able */
    private static Intent sLaunchIntent;
    /** Intent used to get all the activities that are {@link #REQUEST_CREATE_SHORTCUT}-able */
    private static Intent sShortcutIntent;
    
    /**
     * List of ResolveInfo for activities that we can bookmark (either directly
     * to the activity, or by launching the activity and it returning a bookmark
     * WITHIN that application).
     */
    private List<ResolveInfo> mResolveList;
    
    // List adapter stuff
    private static final String KEY_TITLE = "TITLE";
    private static final String KEY_RESOLVE_INFO = "RESOLVE_INFO";
    private static final String sKeys[] = new String[] { KEY_TITLE, KEY_RESOLVE_INFO };
    private static final int sResourceIds[] = new int[] { R.id.title, R.id.icon };
    private SimpleAdapter mMyAdapter;

    /** Display those activities that are launch-able */
    private static final int DISPLAY_MODE_LAUNCH = 0;
    /** Display those activities that are able to have bookmarks WITHIN the application */
    private static final int DISPLAY_MODE_SHORTCUT = 1;
    private int mDisplayMode = DISPLAY_MODE_LAUNCH;
    
    private Handler mUiHandler = new Handler();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        updateListAndAdapter();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, DISPLAY_MODE_LAUNCH, 0, R.string.quick_launch_display_mode_applications)
                .setIcon(com.android.internal.R.drawable.ic_menu_archive);
        menu.add(0, DISPLAY_MODE_SHORTCUT, 0, R.string.quick_launch_display_mode_shortcuts)
                .setIcon(com.android.internal.R.drawable.ic_menu_goto);
        return true;
    }
    
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.findItem(DISPLAY_MODE_LAUNCH).setVisible(mDisplayMode != DISPLAY_MODE_LAUNCH);
        menu.findItem(DISPLAY_MODE_SHORTCUT).setVisible(mDisplayMode != DISPLAY_MODE_SHORTCUT);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        
        switch (item.getItemId()) {

            case DISPLAY_MODE_LAUNCH: 
                mDisplayMode = DISPLAY_MODE_LAUNCH;
                break;
            
            case DISPLAY_MODE_SHORTCUT:
                mDisplayMode = DISPLAY_MODE_SHORTCUT;
                break;
            
            default:
                return false;
        }
        
        updateListAndAdapter();
        return true;
    }

    private void ensureIntents() {
        if (sLaunchIntent == null) {
            sLaunchIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
            sShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
        }
    }

    /**
     * This should be called from the UI thread.
     */
    private void updateListAndAdapter() {
        // Get the activities in a separate thread
        new Thread("data updater") {
            @Override
            public void run() {
                synchronized (BookmarkPicker.this) {
                    /*
                     * Don't touch any of the lists that are being used by the
                     * adapter in this thread!
                     */
                    ArrayList<ResolveInfo> newResolveList = new ArrayList<ResolveInfo>();
                    ArrayList<Map<String, ?>> newAdapterList = new ArrayList<Map<String, ?>>();

                    fillResolveList(newResolveList);
                    Collections.sort(newResolveList,
                            new ResolveInfo.DisplayNameComparator(getPackageManager()));
                    
                    fillAdapterList(newAdapterList, newResolveList);
                    
                    updateAdapterToUseNewLists(newAdapterList, newResolveList);
                }
            }
        }.start();  
    }
    
    private void updateAdapterToUseNewLists(final ArrayList<Map<String, ?>> newAdapterList,
            final ArrayList<ResolveInfo> newResolveList) {
        // Post this back on the UI thread
        mUiHandler.post(new Runnable() {
            public void run() {
                /*
                 * SimpleAdapter does not support changing the lists after it
                 * has been created. We just create a new instance.
                 */
                mMyAdapter = createResolveAdapter(newAdapterList);
                mResolveList = newResolveList;
                setListAdapter(mMyAdapter);
            }
        });
    }
    
    /**
     * Gets all activities matching our current display mode.
     * 
     * @param list The list to fill.
     */
    private void fillResolveList(List<ResolveInfo> list) {
        ensureIntents();
        PackageManager pm = getPackageManager();
        list.clear();
        
        if (mDisplayMode == DISPLAY_MODE_LAUNCH) {
            list.addAll(pm.queryIntentActivities(sLaunchIntent, 0));
        } else if (mDisplayMode == DISPLAY_MODE_SHORTCUT) {
            list.addAll(pm.queryIntentActivities(sShortcutIntent, 0)); 
        }
    }
    
    private SimpleAdapter createResolveAdapter(List<Map<String, ?>> list) {
        SimpleAdapter adapter = new SimpleAdapter(this, list,
                R.layout.bookmark_picker_item, sKeys, sResourceIds);
        adapter.setViewBinder(this);
        return adapter;
    }

    private void fillAdapterList(List<Map<String, ?>> list,
            List<ResolveInfo> resolveList) {
        list.clear();
        int resolveListSize = resolveList.size();
        for (int i = 0; i < resolveListSize; i++) {
            ResolveInfo info = resolveList.get(i);
            /*
             * Simple adapter craziness. For each item, we need to create a map
             * from a key to its value (the value can be any object--the view
             * binder will take care of filling the View with a representation
             * of that object).
             */
            Map<String, Object> map = new TreeMap<String, Object>();
            map.put(KEY_TITLE, getResolveInfoTitle(info));
            map.put(KEY_RESOLVE_INFO, info);
            list.add(map);
        }
    }

    /** Get the title for a resolve info. */
    private String getResolveInfoTitle(ResolveInfo info) {
        CharSequence label = info.loadLabel(getPackageManager());
        if (label == null) label = info.activityInfo.name;
        return label != null ? label.toString() : null;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if (position >= mResolveList.size()) return;

        ResolveInfo info = mResolveList.get(position);
        
        switch (mDisplayMode) {

            case DISPLAY_MODE_LAUNCH: 
                // We can go ahead and return the clicked info's intent
                Intent intent = getIntentForResolveInfo(info, Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_LAUNCHER);
                finish(intent, getResolveInfoTitle(info));
                break;

            case DISPLAY_MODE_SHORTCUT:
                // Start the shortcut activity so the user can pick the actual intent
                // (example: Gmail's shortcut activity shows a list of mailboxes)
                startShortcutActivity(info);
                break;
        }
        
    }
    
    private static Intent getIntentForResolveInfo(ResolveInfo info, String action) {
        Intent intent = new Intent(action);
        ActivityInfo ai = info.activityInfo;
        intent.setClassName(ai.packageName, ai.name);
        return intent;
    }

    /**
     * Starts an activity to get a shortcut.
     * <p>
     * For example, Gmail has an activity that lists the available labels. It
     * returns a shortcut intent for going directly to this label.
     */
    private void startShortcutActivity(ResolveInfo info) {
        Intent intent = getIntentForResolveInfo(info, Intent.ACTION_CREATE_SHORTCUT);
        startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
        
        // Will get a callback to onActivityResult
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }
        
        switch (requestCode) {
            
            case REQUEST_CREATE_SHORTCUT:
                if (data != null) {
                    finish((Intent) data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT),
                            data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
                }
                break;
                
            default:
                super.onActivityResult(requestCode, resultCode, data);
                break;
        }
    }
    
    /**
     * Finishes the activity and returns the given data.
     */
    private void finish(Intent intent, String title) {
        // Give back what was given to us (it will have the shortcut, for example)
        intent.putExtras(getIntent());
        // Put our information
        intent.putExtra(EXTRA_TITLE, title);
        setResult(RESULT_OK, intent);
        finish();
    }

    /**
     * {@inheritDoc}
     */
    public boolean setViewValue(View view, Object data, String textRepresentation) {
        if (view.getId() == R.id.icon) {
            Drawable icon = ((ResolveInfo) data).loadIcon(getPackageManager());
            if (icon != null) {
                ((ImageView) view).setImageDrawable(icon);
            }
            return true;
        } else {
            return false;
        }
    }
    
}