/* * Copyright (C) 2008 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 android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.ContentObserver; import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings.Bookmarks; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.widget.AdapterView; import com.android.settings.R; import java.net.URISyntaxException; /** * Settings activity for quick launch. * <p> * Shows a list of possible shortcuts, the current application each is bound to, * and allows choosing a new bookmark for a shortcut. */ public class QuickLaunchSettings extends PreferenceActivity implements AdapterView.OnItemLongClickListener, DialogInterface.OnClickListener { private static final String TAG = "QuickLaunchSettings"; private static final String KEY_SHORTCUT_CATEGORY = "shortcut_category"; private static final int DIALOG_CLEAR_SHORTCUT = 0; private static final int REQUEST_PICK_BOOKMARK = 1; private static final int COLUMN_SHORTCUT = 0; private static final int COLUMN_TITLE = 1; private static final int COLUMN_INTENT = 2; private static final String[] sProjection = new String[] { Bookmarks.SHORTCUT, Bookmarks.TITLE, Bookmarks.INTENT }; private static final String sShortcutSelection = Bookmarks.SHORTCUT + "=?"; private Handler mUiHandler = new Handler(); private static final String DEFAULT_BOOKMARK_FOLDER = "@quicklaunch"; /** Cursor for Bookmarks provider. */ private Cursor mBookmarksCursor; /** Listens for changes to Bookmarks provider. */ private BookmarksObserver mBookmarksObserver; /** Used to keep track of which shortcuts have bookmarks. */ private SparseBooleanArray mBookmarkedShortcuts; /** Preference category to hold the shortcut preferences. */ private PreferenceGroup mShortcutGroup; /** Mapping of a shortcut to its preference. */ private SparseArray<ShortcutPreference> mShortcutToPreference; /** The bookmark title of the shortcut that is being cleared. */ private CharSequence mClearDialogBookmarkTitle; private static final String CLEAR_DIALOG_BOOKMARK_TITLE = "CLEAR_DIALOG_BOOKMARK_TITLE"; /** The shortcut that is being cleared. */ private char mClearDialogShortcut; private static final String CLEAR_DIALOG_SHORTCUT = "CLEAR_DIALOG_SHORTCUT"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.quick_launch_settings); mShortcutGroup = (PreferenceGroup) findPreference(KEY_SHORTCUT_CATEGORY); mShortcutToPreference = new SparseArray<ShortcutPreference>(); mBookmarksObserver = new BookmarksObserver(mUiHandler); initShortcutPreferences(); mBookmarksCursor = managedQuery(Bookmarks.CONTENT_URI, sProjection, null, null); getListView().setOnItemLongClickListener(this); } @Override protected void onResume() { super.onResume(); getContentResolver().registerContentObserver(Bookmarks.CONTENT_URI, true, mBookmarksObserver); refreshShortcuts(); } @Override protected void onPause() { super.onPause(); getContentResolver().unregisterContentObserver(mBookmarksObserver); } @Override protected void onRestoreInstanceState(Bundle state) { super.onRestoreInstanceState(state); // Restore the clear dialog's info mClearDialogBookmarkTitle = state.getString(CLEAR_DIALOG_BOOKMARK_TITLE); mClearDialogShortcut = (char) state.getInt(CLEAR_DIALOG_SHORTCUT, 0); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Save the clear dialog's info outState.putCharSequence(CLEAR_DIALOG_BOOKMARK_TITLE, mClearDialogBookmarkTitle); outState.putInt(CLEAR_DIALOG_SHORTCUT, mClearDialogShortcut); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CLEAR_SHORTCUT: { // Create the dialog for clearing a shortcut return new AlertDialog.Builder(this) .setTitle(getString(R.string.quick_launch_clear_dialog_title)) .setIcon(android.R.drawable.ic_dialog_alert) .setMessage(getString(R.string.quick_launch_clear_dialog_message, mClearDialogShortcut, mClearDialogBookmarkTitle)) .setPositiveButton(R.string.quick_launch_clear_ok_button, this) .setNegativeButton(R.string.quick_launch_clear_cancel_button, this) .create(); } } return super.onCreateDialog(id); } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_CLEAR_SHORTCUT: { AlertDialog alertDialog = (AlertDialog) dialog; alertDialog.setMessage(getString(R.string.quick_launch_clear_dialog_message, mClearDialogShortcut, mClearDialogBookmarkTitle)); } } } private void showClearDialog(ShortcutPreference pref) { if (!pref.hasBookmark()) return; mClearDialogBookmarkTitle = pref.getTitle(); mClearDialogShortcut = pref.getShortcut(); showDialog(DIALOG_CLEAR_SHORTCUT); } public void onClick(DialogInterface dialog, int which) { if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON_POSITIVE) { // Clear the shortcut clearShortcut(mClearDialogShortcut); } mClearDialogBookmarkTitle = null; mClearDialogShortcut = 0; } private void clearShortcut(char shortcut) { getContentResolver().delete(Bookmarks.CONTENT_URI, sShortcutSelection, new String[] { String.valueOf((int) shortcut) }); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if (!(preference instanceof ShortcutPreference)) return false; // Open the screen to pick a bookmark for this shortcut ShortcutPreference pref = (ShortcutPreference) preference; Intent intent = new Intent(this, BookmarkPicker.class); intent.putExtra(BookmarkPicker.EXTRA_SHORTCUT, pref.getShortcut()); startActivityForResult(intent, REQUEST_PICK_BOOKMARK); return true; } public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // Open the clear shortcut dialog Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position); if (!(pref instanceof ShortcutPreference)) return false; showClearDialog((ShortcutPreference) pref); return true; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK) { return; } if (requestCode == REQUEST_PICK_BOOKMARK) { // Returned from the 'pick bookmark for this shortcut' screen if (data == null) { Log.w(TAG, "Result from bookmark picker does not have an intent."); return; } char shortcut = data.getCharExtra(BookmarkPicker.EXTRA_SHORTCUT, (char) 0); updateShortcut(shortcut, data); } else { super.onActivityResult(requestCode, resultCode, data); } } private void updateShortcut(char shortcut, Intent intent) { // Update the bookmark for a shortcut // Pass an empty title so it gets resolved each time this bookmark is // displayed (since the locale could change after we insert into the provider). Bookmarks.add(getContentResolver(), intent, "", DEFAULT_BOOKMARK_FOLDER, shortcut, 0); } private ShortcutPreference getOrCreatePreference(char shortcut) { ShortcutPreference pref = mShortcutToPreference.get(shortcut); if (pref != null) { return pref; } else { Log.w(TAG, "Unknown shortcut '" + shortcut + "', creating preference anyway"); return createPreference(shortcut); } } private ShortcutPreference createPreference(char shortcut) { ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut); mShortcutGroup.addPreference(pref); mShortcutToPreference.put(shortcut, pref); return pref; } private void initShortcutPreferences() { /** Whether the shortcut has been seen already. The array index is the shortcut. */ SparseBooleanArray shortcutSeen = new SparseBooleanArray(); KeyCharacterMap keyMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); // Go through all the key codes and create a preference for the appropriate keys for (int keyCode = KeyEvent.getMaxKeyCode() - 1; keyCode >= 0; keyCode--) { // Get the label for the primary char on the key that produces this key code char shortcut = (char) Character.toLowerCase(keyMap.getDisplayLabel(keyCode)); if (shortcut == 0 || shortcutSeen.get(shortcut, false)) continue; // TODO: need a to tell if the current keyboard can produce this key code, for now // only allow the letter or digits if (!Character.isLetterOrDigit(shortcut)) continue; shortcutSeen.put(shortcut, true); createPreference(shortcut); } } private synchronized void refreshShortcuts() { Cursor c = mBookmarksCursor; if (c == null) { // Haven't finished querying yet return; } if (!c.requery()) { Log.e(TAG, "Could not requery cursor when refreshing shortcuts."); return; } /** * We use the previous bookmarked shortcuts array to filter out those * shortcuts that had bookmarks before this method call, and don't after * (so we can set the preferences to be without bookmarks). */ SparseBooleanArray noLongerBookmarkedShortcuts = mBookmarkedShortcuts; SparseBooleanArray newBookmarkedShortcuts = new SparseBooleanArray(); while (c.moveToNext()) { char shortcut = Character.toLowerCase((char) c.getInt(COLUMN_SHORTCUT)); if (shortcut == 0) continue; ShortcutPreference pref = getOrCreatePreference(shortcut); CharSequence title = Bookmarks.getTitle(this, c); /* * The title retrieved from Bookmarks.getTitle() will be in * the original boot locale, not the current locale. * Try to look up a localized title from the PackageManager. */ int intentColumn = c.getColumnIndex(Bookmarks.INTENT); String intentUri = c.getString(intentColumn); PackageManager packageManager = getPackageManager(); try { Intent intent = Intent.parseUri(intentUri, 0); ResolveInfo info = packageManager.resolveActivity(intent, 0); if (info != null) { title = info.loadLabel(packageManager); } } catch (URISyntaxException e) { // Just use the non-localized title, then. } pref.setTitle(title); pref.setSummary(getString(R.string.quick_launch_shortcut, String.valueOf(shortcut))); pref.setHasBookmark(true); newBookmarkedShortcuts.put(shortcut, true); if (noLongerBookmarkedShortcuts != null) { // After this loop, the shortcuts with value true in this array // will no longer have bookmarks noLongerBookmarkedShortcuts.put(shortcut, false); } } if (noLongerBookmarkedShortcuts != null) { for (int i = noLongerBookmarkedShortcuts.size() - 1; i >= 0; i--) { if (noLongerBookmarkedShortcuts.valueAt(i)) { // True, so there is no longer a bookmark for this shortcut char shortcut = (char) noLongerBookmarkedShortcuts.keyAt(i); ShortcutPreference pref = mShortcutToPreference.get(shortcut); if (pref != null) { pref.setHasBookmark(false); } } } } mBookmarkedShortcuts = newBookmarkedShortcuts; c.deactivate(); } private class BookmarksObserver extends ContentObserver { public BookmarksObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); refreshShortcuts(); } } }