QuickLaunchSettings.java revision ec598cb91c02c035d19ccd8a9f17e2bf8da9da5a
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 17package com.android.settings.quicklaunch; 18 19import android.app.AlertDialog; 20import android.app.Dialog; 21import android.content.DialogInterface; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.content.pm.ResolveInfo; 25import android.database.ContentObserver; 26import android.database.Cursor; 27import android.os.Bundle; 28import android.os.Handler; 29import android.preference.Preference; 30import android.preference.PreferenceActivity; 31import android.preference.PreferenceGroup; 32import android.preference.PreferenceScreen; 33import android.provider.Settings.Bookmarks; 34import android.util.Log; 35import android.util.SparseArray; 36import android.util.SparseBooleanArray; 37import android.view.KeyCharacterMap; 38import android.view.KeyEvent; 39import android.view.View; 40import android.widget.AdapterView; 41 42import com.android.settings.R; 43 44import 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 */ 52public 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 .setIcon(android.R.drawable.ic_dialog_alert) 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.BUTTON1) { 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 String title = data.getStringExtra(BookmarkPicker.EXTRA_TITLE); 231 char shortcut = data.getCharExtra(BookmarkPicker.EXTRA_SHORTCUT, (char) 0); 232 updateShortcut(shortcut, data); 233 234 } else { 235 super.onActivityResult(requestCode, resultCode, data); 236 } 237 } 238 239 private void updateShortcut(char shortcut, Intent intent) { 240 // Update the bookmark for a shortcut 241 // Pass an empty title so it gets resolved each time this bookmark is 242 // displayed (since the locale could change after we insert into the provider). 243 Bookmarks.add(getContentResolver(), intent, "", DEFAULT_BOOKMARK_FOLDER, shortcut, 0); 244 } 245 246 private ShortcutPreference getOrCreatePreference(char shortcut) { 247 ShortcutPreference pref = mShortcutToPreference.get(shortcut); 248 if (pref != null) { 249 return pref; 250 } else { 251 Log.w(TAG, "Unknown shortcut '" + shortcut + "', creating preference anyway"); 252 return createPreference(shortcut); 253 } 254 } 255 256 private ShortcutPreference createPreference(char shortcut) { 257 ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut); 258 mShortcutGroup.addPreference(pref); 259 mShortcutToPreference.put(shortcut, pref); 260 return pref; 261 } 262 263 private void initShortcutPreferences() { 264 265 /** Whether the shortcut has been seen already. The array index is the shortcut. */ 266 SparseBooleanArray shortcutSeen = new SparseBooleanArray(); 267 KeyCharacterMap keyMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); 268 269 // Go through all the key codes and create a preference for the appropriate keys 270 for (int keyCode = KeyEvent.getMaxKeyCode() - 1; keyCode >= 0; keyCode--) { 271 // Get the label for the primary char on the key that produces this key code 272 char shortcut = (char) Character.toLowerCase(keyMap.getDisplayLabel(keyCode)); 273 if (shortcut == 0 || shortcutSeen.get(shortcut, false)) continue; 274 // TODO: need a to tell if the current keyboard can produce this key code, for now 275 // only allow the letter or digits 276 if (!Character.isLetterOrDigit(shortcut)) continue; 277 shortcutSeen.put(shortcut, true); 278 279 createPreference(shortcut); 280 } 281 } 282 283 private synchronized void refreshShortcuts() { 284 Cursor c = mBookmarksCursor; 285 if (c == null) { 286 // Haven't finished querying yet 287 return; 288 } 289 290 if (!c.requery()) { 291 Log.e(TAG, "Could not requery cursor when refreshing shortcuts."); 292 return; 293 } 294 295 /** 296 * We use the previous bookmarked shortcuts array to filter out those 297 * shortcuts that had bookmarks before this method call, and don't after 298 * (so we can set the preferences to be without bookmarks). 299 */ 300 SparseBooleanArray noLongerBookmarkedShortcuts = mBookmarkedShortcuts; 301 SparseBooleanArray newBookmarkedShortcuts = new SparseBooleanArray(); 302 while (c.moveToNext()) { 303 char shortcut = Character.toLowerCase((char) c.getInt(COLUMN_SHORTCUT)); 304 if (shortcut == 0) continue; 305 306 ShortcutPreference pref = getOrCreatePreference(shortcut); 307 CharSequence title = Bookmarks.getTitle(this, c); 308 309 /* 310 * The title retrieved from Bookmarks.getTitle() will be in 311 * the original boot locale, not the current locale. 312 * Try to look up a localized title from the PackageManager. 313 */ 314 int intentColumn = c.getColumnIndex(Bookmarks.INTENT); 315 String intentUri = c.getString(intentColumn); 316 PackageManager packageManager = getPackageManager(); 317 try { 318 Intent intent = Intent.getIntent(intentUri); 319 ResolveInfo info = packageManager.resolveActivity(intent, 0); 320 if (info != null) { 321 title = info.loadLabel(packageManager); 322 } 323 } catch (URISyntaxException e) { 324 // Just use the non-localized title, then. 325 } 326 327 pref.setTitle(title); 328 pref.setSummary(getString(R.string.quick_launch_shortcut, 329 String.valueOf(shortcut))); 330 pref.setHasBookmark(true); 331 332 newBookmarkedShortcuts.put(shortcut, true); 333 if (noLongerBookmarkedShortcuts != null) { 334 // After this loop, the shortcuts with value true in this array 335 // will no longer have bookmarks 336 noLongerBookmarkedShortcuts.put(shortcut, false); 337 } 338 } 339 340 if (noLongerBookmarkedShortcuts != null) { 341 for (int i = noLongerBookmarkedShortcuts.size() - 1; i >= 0; i--) { 342 if (noLongerBookmarkedShortcuts.valueAt(i)) { 343 // True, so there is no longer a bookmark for this shortcut 344 char shortcut = (char) noLongerBookmarkedShortcuts.keyAt(i); 345 ShortcutPreference pref = mShortcutToPreference.get(shortcut); 346 if (pref != null) { 347 pref.setHasBookmark(false); 348 } 349 } 350 } 351 } 352 353 mBookmarkedShortcuts = newBookmarkedShortcuts; 354 355 c.deactivate(); 356 } 357 358 private class BookmarksObserver extends ContentObserver { 359 360 public BookmarksObserver(Handler handler) { 361 super(handler); 362 } 363 364 @Override 365 public void onChange(boolean selfChange) { 366 super.onChange(selfChange); 367 368 refreshShortcuts(); 369 } 370 } 371} 372