Home | History | Annotate | Download | only in quicklaunch
      1 /*
      2  * Copyright (C) 2008 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 
     17 package com.android.settings.quicklaunch;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Dialog;
     21 import android.content.DialogInterface;
     22 import android.content.Intent;
     23 import android.content.pm.PackageManager;
     24 import android.content.pm.ResolveInfo;
     25 import android.database.ContentObserver;
     26 import android.database.Cursor;
     27 import android.os.Bundle;
     28 import android.os.Handler;
     29 import android.preference.Preference;
     30 import android.preference.PreferenceActivity;
     31 import android.preference.PreferenceGroup;
     32 import android.preference.PreferenceScreen;
     33 import android.provider.Settings.Bookmarks;
     34 import android.util.Log;
     35 import android.util.SparseArray;
     36 import android.util.SparseBooleanArray;
     37 import android.view.KeyCharacterMap;
     38 import android.view.KeyEvent;
     39 import android.view.View;
     40 import android.widget.AdapterView;
     41 
     42 import com.android.settings.R;
     43 
     44 import java.net.URISyntaxException;
     45 
     46 /**
     47  * Settings activity for quick launch.
     48  * <p>
     49  * Shows a list of possible shortcuts, the current application each is bound to,
     50  * and allows choosing a new bookmark for a shortcut.
     51  */
     52 public class QuickLaunchSettings extends PreferenceActivity implements
     53         AdapterView.OnItemLongClickListener, DialogInterface.OnClickListener {
     54 
     55     private static final String TAG = "QuickLaunchSettings";
     56 
     57     private static final String KEY_SHORTCUT_CATEGORY = "shortcut_category";
     58 
     59     private static final int DIALOG_CLEAR_SHORTCUT = 0;
     60 
     61     private static final int REQUEST_PICK_BOOKMARK = 1;
     62 
     63     private static final int COLUMN_SHORTCUT = 0;
     64     private static final int COLUMN_TITLE = 1;
     65     private static final int COLUMN_INTENT = 2;
     66     private static final String[] sProjection = new String[] {
     67             Bookmarks.SHORTCUT, Bookmarks.TITLE, Bookmarks.INTENT
     68     };
     69     private static final String sShortcutSelection = Bookmarks.SHORTCUT + "=?";
     70 
     71     private Handler mUiHandler = new Handler();
     72 
     73     private static final String DEFAULT_BOOKMARK_FOLDER = "@quicklaunch";
     74     /** Cursor for Bookmarks provider. */
     75     private Cursor mBookmarksCursor;
     76     /** Listens for changes to Bookmarks provider. */
     77     private BookmarksObserver mBookmarksObserver;
     78     /** Used to keep track of which shortcuts have bookmarks. */
     79     private SparseBooleanArray mBookmarkedShortcuts;
     80 
     81     /** Preference category to hold the shortcut preferences. */
     82     private PreferenceGroup mShortcutGroup;
     83     /** Mapping of a shortcut to its preference. */
     84     private SparseArray<ShortcutPreference> mShortcutToPreference;
     85 
     86     /** The bookmark title of the shortcut that is being cleared. */
     87     private CharSequence mClearDialogBookmarkTitle;
     88     private static final String CLEAR_DIALOG_BOOKMARK_TITLE = "CLEAR_DIALOG_BOOKMARK_TITLE";
     89     /** The shortcut that is being cleared. */
     90     private char mClearDialogShortcut;
     91     private static final String CLEAR_DIALOG_SHORTCUT = "CLEAR_DIALOG_SHORTCUT";
     92 
     93     @Override
     94     protected void onCreate(Bundle savedInstanceState) {
     95         super.onCreate(savedInstanceState);
     96 
     97         addPreferencesFromResource(R.xml.quick_launch_settings);
     98 
     99         mShortcutGroup = (PreferenceGroup) findPreference(KEY_SHORTCUT_CATEGORY);
    100         mShortcutToPreference = new SparseArray<ShortcutPreference>();
    101         mBookmarksObserver = new BookmarksObserver(mUiHandler);
    102         initShortcutPreferences();
    103         mBookmarksCursor = managedQuery(Bookmarks.CONTENT_URI, sProjection, null, null);
    104         getListView().setOnItemLongClickListener(this);
    105     }
    106 
    107     @Override
    108     protected void onResume() {
    109         super.onResume();
    110         getContentResolver().registerContentObserver(Bookmarks.CONTENT_URI, true,
    111                 mBookmarksObserver);
    112         refreshShortcuts();
    113     }
    114 
    115     @Override
    116     protected void onPause() {
    117         super.onPause();
    118         getContentResolver().unregisterContentObserver(mBookmarksObserver);
    119     }
    120 
    121     @Override
    122     protected void onRestoreInstanceState(Bundle state) {
    123         super.onRestoreInstanceState(state);
    124 
    125         // Restore the clear dialog's info
    126         mClearDialogBookmarkTitle = state.getString(CLEAR_DIALOG_BOOKMARK_TITLE);
    127         mClearDialogShortcut = (char) state.getInt(CLEAR_DIALOG_SHORTCUT, 0);
    128     }
    129 
    130     @Override
    131     protected void onSaveInstanceState(Bundle outState) {
    132         super.onSaveInstanceState(outState);
    133 
    134         // Save the clear dialog's info
    135         outState.putCharSequence(CLEAR_DIALOG_BOOKMARK_TITLE, mClearDialogBookmarkTitle);
    136         outState.putInt(CLEAR_DIALOG_SHORTCUT, mClearDialogShortcut);
    137     }
    138 
    139     @Override
    140     protected Dialog onCreateDialog(int id) {
    141         switch (id) {
    142 
    143             case DIALOG_CLEAR_SHORTCUT: {
    144                 // Create the dialog for clearing a shortcut
    145                 return new AlertDialog.Builder(this)
    146                         .setTitle(getString(R.string.quick_launch_clear_dialog_title))
    147                         .setIconAttribute(android.R.attr.alertDialogIcon)
    148                         .setMessage(getString(R.string.quick_launch_clear_dialog_message,
    149                                 mClearDialogShortcut, mClearDialogBookmarkTitle))
    150                         .setPositiveButton(R.string.quick_launch_clear_ok_button, this)
    151                         .setNegativeButton(R.string.quick_launch_clear_cancel_button, this)
    152                         .create();
    153             }
    154         }
    155 
    156         return super.onCreateDialog(id);
    157     }
    158 
    159     @Override
    160     protected void onPrepareDialog(int id, Dialog dialog) {
    161         switch (id) {
    162 
    163             case DIALOG_CLEAR_SHORTCUT: {
    164                 AlertDialog alertDialog = (AlertDialog) dialog;
    165                 alertDialog.setMessage(getString(R.string.quick_launch_clear_dialog_message,
    166                         mClearDialogShortcut, mClearDialogBookmarkTitle));
    167             }
    168         }
    169     }
    170 
    171     private void showClearDialog(ShortcutPreference pref) {
    172 
    173         if (!pref.hasBookmark()) return;
    174 
    175         mClearDialogBookmarkTitle = pref.getTitle();
    176         mClearDialogShortcut = pref.getShortcut();
    177         showDialog(DIALOG_CLEAR_SHORTCUT);
    178     }
    179 
    180     public void onClick(DialogInterface dialog, int which) {
    181         if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON_POSITIVE) {
    182             // Clear the shortcut
    183             clearShortcut(mClearDialogShortcut);
    184         }
    185         mClearDialogBookmarkTitle = null;
    186         mClearDialogShortcut = 0;
    187     }
    188 
    189     private void clearShortcut(char shortcut) {
    190         getContentResolver().delete(Bookmarks.CONTENT_URI, sShortcutSelection,
    191                 new String[] { String.valueOf((int) shortcut) });
    192     }
    193 
    194     @Override
    195     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    196         if (!(preference instanceof ShortcutPreference)) return false;
    197 
    198         // Open the screen to pick a bookmark for this shortcut
    199         ShortcutPreference pref = (ShortcutPreference) preference;
    200         Intent intent = new Intent(this, BookmarkPicker.class);
    201         intent.putExtra(BookmarkPicker.EXTRA_SHORTCUT, pref.getShortcut());
    202         startActivityForResult(intent, REQUEST_PICK_BOOKMARK);
    203 
    204         return true;
    205     }
    206 
    207     public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
    208 
    209         // Open the clear shortcut dialog
    210         Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
    211         if (!(pref instanceof ShortcutPreference)) return false;
    212         showClearDialog((ShortcutPreference) pref);
    213         return true;
    214     }
    215 
    216     @Override
    217     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    218         if (resultCode != RESULT_OK) {
    219             return;
    220         }
    221 
    222         if (requestCode == REQUEST_PICK_BOOKMARK) {
    223 
    224             // Returned from the 'pick bookmark for this shortcut' screen
    225             if (data == null) {
    226                 Log.w(TAG, "Result from bookmark picker does not have an intent.");
    227                 return;
    228             }
    229 
    230             char shortcut = data.getCharExtra(BookmarkPicker.EXTRA_SHORTCUT, (char) 0);
    231             updateShortcut(shortcut, data);
    232 
    233         } else {
    234             super.onActivityResult(requestCode, resultCode, data);
    235         }
    236     }
    237 
    238     private void updateShortcut(char shortcut, Intent intent) {
    239         // Update the bookmark for a shortcut
    240         // Pass an empty title so it gets resolved each time this bookmark is
    241         // displayed (since the locale could change after we insert into the provider).
    242         Bookmarks.add(getContentResolver(), intent, "", DEFAULT_BOOKMARK_FOLDER, shortcut, 0);
    243     }
    244 
    245     private ShortcutPreference getOrCreatePreference(char shortcut) {
    246         ShortcutPreference pref = mShortcutToPreference.get(shortcut);
    247         if (pref != null) {
    248             return pref;
    249         } else {
    250             Log.w(TAG, "Unknown shortcut '" + shortcut + "', creating preference anyway");
    251             return createPreference(shortcut);
    252         }
    253     }
    254 
    255     private ShortcutPreference createPreference(char shortcut) {
    256         ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut);
    257         mShortcutGroup.addPreference(pref);
    258         mShortcutToPreference.put(shortcut, pref);
    259         return pref;
    260     }
    261 
    262     private void initShortcutPreferences() {
    263 
    264         /** Whether the shortcut has been seen already.  The array index is the shortcut. */
    265         SparseBooleanArray shortcutSeen = new SparseBooleanArray();
    266         KeyCharacterMap keyMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
    267 
    268         // Go through all the key codes and create a preference for the appropriate keys
    269         for (int keyCode = KeyEvent.getMaxKeyCode() - 1; keyCode >= 0; keyCode--) {
    270             // Get the label for the primary char on the key that produces this key code
    271             char shortcut = (char) Character.toLowerCase(keyMap.getDisplayLabel(keyCode));
    272             if (shortcut == 0 || shortcutSeen.get(shortcut, false)) continue;
    273             // TODO: need a to tell if the current keyboard can produce this key code, for now
    274             // only allow the letter or digits
    275             if (!Character.isLetterOrDigit(shortcut)) continue;
    276             shortcutSeen.put(shortcut, true);
    277 
    278             createPreference(shortcut);
    279         }
    280     }
    281 
    282     private synchronized void refreshShortcuts() {
    283         Cursor c = mBookmarksCursor;
    284         if (c == null) {
    285             // Haven't finished querying yet
    286             return;
    287         }
    288 
    289         if (!c.requery()) {
    290             Log.e(TAG, "Could not requery cursor when refreshing shortcuts.");
    291             return;
    292         }
    293 
    294         /**
    295          * We use the previous bookmarked shortcuts array to filter out those
    296          * shortcuts that had bookmarks before this method call, and don't after
    297          * (so we can set the preferences to be without bookmarks).
    298          */
    299         SparseBooleanArray noLongerBookmarkedShortcuts = mBookmarkedShortcuts;
    300         SparseBooleanArray newBookmarkedShortcuts = new SparseBooleanArray();
    301         while (c.moveToNext()) {
    302             char shortcut = Character.toLowerCase((char) c.getInt(COLUMN_SHORTCUT));
    303             if (shortcut == 0) continue;
    304 
    305             ShortcutPreference pref = getOrCreatePreference(shortcut);
    306             CharSequence title = Bookmarks.getTitle(this, c);
    307 
    308             /*
    309              * The title retrieved from Bookmarks.getTitle() will be in
    310              * the original boot locale, not the current locale.
    311              * Try to look up a localized title from the PackageManager.
    312              */
    313             int intentColumn = c.getColumnIndex(Bookmarks.INTENT);
    314             String intentUri = c.getString(intentColumn);
    315             PackageManager packageManager = getPackageManager();
    316             try {
    317                 Intent intent = Intent.parseUri(intentUri, 0);
    318                 ResolveInfo info = packageManager.resolveActivity(intent, 0);
    319                 if (info != null) {
    320                     title = info.loadLabel(packageManager);
    321                 }
    322             } catch (URISyntaxException e) {
    323                 // Just use the non-localized title, then.
    324             }
    325 
    326             pref.setTitle(title);
    327             pref.setSummary(getString(R.string.quick_launch_shortcut,
    328                     String.valueOf(shortcut)));
    329             pref.setHasBookmark(true);
    330 
    331             newBookmarkedShortcuts.put(shortcut, true);
    332             if (noLongerBookmarkedShortcuts != null) {
    333                 // After this loop, the shortcuts with value true in this array
    334                 // will no longer have bookmarks
    335                 noLongerBookmarkedShortcuts.put(shortcut, false);
    336             }
    337         }
    338 
    339         if (noLongerBookmarkedShortcuts != null) {
    340             for (int i = noLongerBookmarkedShortcuts.size() - 1; i >= 0; i--) {
    341                 if (noLongerBookmarkedShortcuts.valueAt(i)) {
    342                     // True, so there is no longer a bookmark for this shortcut
    343                     char shortcut = (char) noLongerBookmarkedShortcuts.keyAt(i);
    344                     ShortcutPreference pref = mShortcutToPreference.get(shortcut);
    345                     if (pref != null) {
    346                         pref.setHasBookmark(false);
    347                     }
    348                 }
    349             }
    350         }
    351 
    352         mBookmarkedShortcuts = newBookmarkedShortcuts;
    353 
    354         c.deactivate();
    355     }
    356 
    357     private class BookmarksObserver extends ContentObserver {
    358 
    359         public BookmarksObserver(Handler handler) {
    360             super(handler);
    361         }
    362 
    363         @Override
    364         public void onChange(boolean selfChange) {
    365             super.onChange(selfChange);
    366 
    367             refreshShortcuts();
    368         }
    369     }
    370 }
    371