diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-18 17:39:48 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-18 17:39:48 -0700 |
commit | e6dd1fa1851302710ac7845d25d8ad8a5b6ee438 (patch) | |
tree | f5a8545397cd15f5bedf524a0673d88d5dd8d182 /src | |
parent | 72ed6feab24308a0acd1fa2135afa3476759cf39 (diff) | |
download | packages_apps_settings-e6dd1fa1851302710ac7845d25d8ad8a5b6ee438.zip packages_apps_settings-e6dd1fa1851302710ac7845d25d8ad8a5b6ee438.tar.gz packages_apps_settings-e6dd1fa1851302710ac7845d25d8ad8a5b6ee438.tar.bz2 |
auto import from //branches/cupcake_rel/...@140373
Diffstat (limited to 'src')
15 files changed, 697 insertions, 464 deletions
diff --git a/src/com/android/settings/AppWidgetPickActivity.java b/src/com/android/settings/AppWidgetPickActivity.java index 0a63712..acc9382 100644 --- a/src/com/android/settings/AppWidgetPickActivity.java +++ b/src/com/android/settings/AppWidgetPickActivity.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Parcelable; import android.view.View; import android.widget.ListView; import android.util.Log; @@ -40,6 +41,9 @@ public class AppWidgetPickActivity extends LauncherActivity AppWidgetManager mAppWidgetManager; int mAppWidgetId; + ArrayList mCustomInfo; + ArrayList mCustomExtras; + Drawable mDefaultIcon = null; public AppWidgetPickActivity() { mAppWidgetManager = AppWidgetManager.getInstance(this); @@ -47,12 +51,67 @@ public class AppWidgetPickActivity extends LauncherActivity @Override public void onCreate(Bundle icicle) { - super.onCreate(icicle); - Bundle extras = getIntent().getExtras(); + if (extras == null) { + setResultData(RESULT_CANCELED, null); + finish(); + } + mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); - setResultData(RESULT_CANCELED); + // get and validate the extras they gave us + ArrayList<Parcelable> customInfo = null; + ArrayList<AppWidgetProviderInfo> customExtras = null; + try_custom_items: { + customInfo = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_INFO); + if (customInfo == null || customInfo.size() == 0) { + Log.i(TAG, "EXTRA_CUSTOM_INFO not present."); + break try_custom_items; + } + + int customInfoSize = customInfo.size(); + for (int i=0; i<customInfoSize; i++) { + Parcelable p = customInfo.get(i); + if (p == null || !(p instanceof AppWidgetProviderInfo)) { + customInfo = null; + Log.e(TAG, "error using EXTRA_CUSTOM_INFO index=" + i); + break try_custom_items; + } + } + + customExtras = extras.getParcelableArrayList(AppWidgetManager.EXTRA_CUSTOM_EXTRAS); + if (customExtras == null) { + customInfo = null; + Log.e(TAG, "EXTRA_CUSTOM_INFO without EXTRA_CUSTOM_EXTRAS"); + break try_custom_items; + } + + int customExtrasSize = customExtras.size(); + if (customInfoSize != customExtrasSize) { + Log.e(TAG, "list size mismatch: EXTRA_CUSTOM_INFO: " + customInfoSize + + " EXTRA_CUSTOM_EXTRAS: " + customExtrasSize); + break try_custom_items; + } + + + for (int i=0; i<customExtrasSize; i++) { + Parcelable p = customExtras.get(i); + if (p == null || !(p instanceof Bundle)) { + customInfo = null; + customExtras = null; + Log.e(TAG, "error using EXTRA_CUSTOM_EXTRAS index=" + i); + break try_custom_items; + } + } + + mCustomInfo = customInfo; + mCustomExtras = customExtras; + } + + // After the stuff with mCustomInfo + super.onCreate(icicle); + + setResultData(RESULT_CANCELED, null); } @Override @@ -60,41 +119,42 @@ public class AppWidgetPickActivity extends LauncherActivity { Intent intent = intentForPosition(position); int result; - try { - mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent()); - result = RESULT_OK; - } catch (IllegalArgumentException e) { - // This is thrown if they're already bound, or otherwise somehow - // bogus. Set the result to canceled, and exit. The app *should* - // clean up at this point. We could pass the error along, but - // it's not clear that that's useful -- the widget will simply not - // appear. - result = RESULT_CANCELED; + if (intent.getExtras() != null) { + // If there are any extras, it's because this entry is custom. + // Don't try to bind it, just pass it back to the app. + setResultData(RESULT_OK, intent); + } else { + try { + mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent()); + result = RESULT_OK; + } catch (IllegalArgumentException e) { + // This is thrown if they're already bound, or otherwise somehow + // bogus. Set the result to canceled, and exit. The app *should* + // clean up at this point. We could pass the error along, but + // it's not clear that that's useful -- the widget will simply not + // appear. + result = RESULT_CANCELED; + } + setResultData(result, null); } - setResultData(result); finish(); } - - @Override - public List<ListItem> makeListItems() { - List<AppWidgetProviderInfo> installed = mAppWidgetManager.getInstalledProviders(); - PackageManager pm = getPackageManager(); - - Drawable defaultIcon = null; - IconResizer resizer = new IconResizer(); - ArrayList<ListItem> result = new ArrayList(); - final int N = installed.size(); + void makeItems(List<AppWidgetProviderInfo> items, ArrayList<Bundle> extras, + ArrayList<ListItem> result, IconResizer resizer, PackageManager pm) { + final int N = items.size(); for (int i=0; i<N; i++) { - AppWidgetProviderInfo info = installed.get(i); + AppWidgetProviderInfo info = items.get(i); LauncherActivity.ListItem item = new LauncherActivity.ListItem(); item.packageName = info.provider.getPackageName(); item.className = info.provider.getClassName(); - + if (extras != null) { + item.extras = extras.get(i); + } item.label = info.label; if (info.icon != 0) { - Drawable d = pm.getDrawable( item.packageName, info.icon, null); + Drawable d = pm.getDrawable(item.packageName, info.icon, null); if (d != null) { item.icon = resizer.createIconThumbnail(d); } else { @@ -104,26 +164,46 @@ public class AppWidgetPickActivity extends LauncherActivity } if (item.icon == null) { // (including error case above) - if (defaultIcon == null) { + if (mDefaultIcon == null) { // TODO: Load standard icon. } - item.icon = defaultIcon; + item.icon = mDefaultIcon; } result.add(item); } + } + + @Override + public List<ListItem> makeListItems() { + List<AppWidgetProviderInfo> installed = mAppWidgetManager.getInstalledProviders(); + PackageManager pm = getPackageManager(); + + IconResizer resizer = new IconResizer(); + ArrayList<ListItem> result = new ArrayList(); + + // the ones from the package manager + makeItems(installed, null, result, resizer, pm); + + // the ones provided in the intent we were launched with + if (mCustomInfo != null) { + Log.d(TAG, "Using " + mCustomInfo.size() + " custom items"); + makeItems(mCustomInfo, mCustomExtras, result, resizer, pm); + } + // sort the results by name Collections.sort(result, new Comparator<ListItem>() { Collator mCollator = Collator.getInstance(); public int compare(ListItem lhs, ListItem rhs) { return mCollator.compare(lhs.label, rhs.label); } }); + return result; } - void setResultData(int code) { - Intent result = new Intent(); + void setResultData(int code, Intent intent) { + Intent result = intent != null ? intent : new Intent(); result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId); setResult(code, result); } diff --git a/src/com/android/settings/BrightnessPreference.java b/src/com/android/settings/BrightnessPreference.java index a9851cc..9f55463 100644 --- a/src/com/android/settings/BrightnessPreference.java +++ b/src/com/android/settings/BrightnessPreference.java @@ -93,7 +93,7 @@ public class BrightnessPreference extends SeekBarPreference implements IHardwareService hardware = IHardwareService.Stub.asInterface( ServiceManager.getService("hardware")); if (hardware != null) { - hardware.setScreenBacklight(brightness); + hardware.setBacklights(brightness); } } catch (RemoteException doe) { diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java index 3ddd669..d0c3758 100644 --- a/src/com/android/settings/ChooseLockPattern.java +++ b/src/com/android/settings/ChooseLockPattern.java @@ -481,8 +481,15 @@ public class ChooseLockPattern extends Activity implements View.OnClickListener{ } private void saveChosenPatternAndFinish() { + final boolean lockVirgin = !mLockPatternUtils.isPatternEverChosen(); + mLockPatternUtils.saveLockPattern(mChosenPattern); mLockPatternUtils.setLockPatternEnabled(true); + + if (lockVirgin) { + mLockPatternUtils.setVisiblePatternEnabled(true); + mLockPatternUtils.setTactileFeedbackEnabled(false); + } setResult(RESULT_FINISHED); finish(); diff --git a/src/com/android/settings/InputMethodsSettings.java b/src/com/android/settings/InputMethodsSettings.java deleted file mode 100644 index 51b770d..0000000 --- a/src/com/android/settings/InputMethodsSettings.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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; - -import java.util.HashSet; -import java.util.List; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemProperties; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; -import android.preference.CheckBoxPreference; -import android.provider.Settings; -import android.text.TextUtils; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; - -/* - * Displays preferences for input methods. - */ -public class InputMethodsSettings extends PreferenceActivity { - private List<InputMethodInfo> mInputMethodProperties; - - final TextUtils.SimpleStringSplitter mStringColonSplitter - = new TextUtils.SimpleStringSplitter(':'); - - private String mLastInputMethodId; - private String mLastTickedInputMethodId; - - static public String getInputMethodIdFromKey(String key) { - return key; - } - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - addPreferencesFromResource(R.xml.input_methods_prefs); - - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - - mInputMethodProperties = imm.getInputMethodList(); - - mLastInputMethodId = Settings.Secure.getString(getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); - - int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties - .size()); - for (int i = 0; i < N; ++i) { - InputMethodInfo property = mInputMethodProperties.get(i); - String prefKey = property.getId(); - - CharSequence label = property.loadLabel(getPackageManager()); - - // Add a check box. - CheckBoxPreference chkbxPref = new CheckBoxPreference(this); - chkbxPref.setKey(prefKey); - chkbxPref.setTitle(label); - getPreferenceScreen().addPreference(chkbxPref); - - // If setting activity is available, add a setting screen entry. - if (null != property.getSettingsActivity()) { - PreferenceScreen prefScreen = new PreferenceScreen(this, null); - prefScreen.setKey(property.getSettingsActivity()); - prefScreen.setTitle(getResources().getString( - R.string.input_methods_settings_label_format, label)); - getPreferenceScreen().addPreference(prefScreen); - } - } - } - - @Override - protected void onResume() { - super.onResume(); - - final HashSet<String> enabled = new HashSet<String>(); - String enabledStr = Settings.Secure.getString(getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS); - if (enabledStr != null) { - final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; - splitter.setString(enabledStr); - while (splitter.hasNext()) { - enabled.add(splitter.next()); - } - } - - // Update the statuses of the Check Boxes. - int N = mInputMethodProperties.size(); - for (int i = 0; i < N; ++i) { - final String id = mInputMethodProperties.get(i).getId(); - CheckBoxPreference pref = (CheckBoxPreference) findPreference(mInputMethodProperties - .get(i).getId()); - pref.setChecked(enabled.contains(id)); - } - mLastTickedInputMethodId = null; - } - - @Override - protected void onPause() { - super.onPause(); - - StringBuilder builder = new StringBuilder(256); - - boolean haveLastInputMethod = false; - - int firstEnabled = -1; - int N = mInputMethodProperties.size(); - for (int i = 0; i < N; ++i) { - final String id = mInputMethodProperties.get(i).getId(); - CheckBoxPreference pref = (CheckBoxPreference) findPreference(id); - boolean hasIt = id.equals(mLastInputMethodId); - if (pref.isChecked()) { - if (builder.length() > 0) builder.append(':'); - builder.append(id); - if (firstEnabled < 0) { - firstEnabled = i; - } - if (hasIt) haveLastInputMethod = true; - } else if (hasIt) { - mLastInputMethodId = mLastTickedInputMethodId; - } - } - - // If the last input method is unset, set it as the first enabled one. - if (null == mLastInputMethodId || "".equals(mLastInputMethodId)) { - if (firstEnabled >= 0) { - mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId(); - } else { - mLastInputMethodId = null; - } - } - - Settings.Secure.putString(getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); - Settings.Secure.putString(getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId); - } - - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - - // Those monkeys kept committing suicide, so we add this property - // to disable this functionality - if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) { - return false; - } - - if (preference instanceof CheckBoxPreference) { - CheckBoxPreference chkPref = (CheckBoxPreference) preference; - String id = getInputMethodIdFromKey(chkPref.getKey()); - if (chkPref.isChecked()) { - mLastTickedInputMethodId = id; - } else if (id.equals(mLastTickedInputMethodId)) { - mLastTickedInputMethodId = null; - } - } else if (preference instanceof PreferenceScreen) { - if (preference.getIntent() == null) { - PreferenceScreen pref = (PreferenceScreen) preference; - String activityName = pref.getKey(); - String packageName = activityName.substring(0, activityName - .lastIndexOf(".")); - if (activityName.length() > 0) { - Intent i = new Intent(Intent.ACTION_MAIN); - i.setClassName(packageName, activityName); - startActivity(i); - } - } - } - - return false; - } -} diff --git a/src/com/android/settings/LanguageSettings.java b/src/com/android/settings/LanguageSettings.java index dd447be..cbab390 100644 --- a/src/com/android/settings/LanguageSettings.java +++ b/src/com/android/settings/LanguageSettings.java @@ -16,8 +16,12 @@ package com.android.settings; +import android.app.AlertDialog; +import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Bundle; import android.os.Environment; @@ -29,6 +33,7 @@ import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; import android.text.TextUtils; +import android.view.View.OnClickListener; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -74,12 +79,12 @@ public class LanguageSettings extends PreferenceActivity { mHaveHardKeyboard = true; } mCheckboxes = new ArrayList<CheckBoxPreference>(); - mRootDirectory = Environment.getRootDirectory().getAbsolutePath(); onCreateIMM(); } private boolean isSystemIme(InputMethodInfo property) { - return property.getServiceInfo().applicationInfo.sourceDir.startsWith(mRootDirectory); + return (property.getServiceInfo().applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0; } private void onCreateIMM() { @@ -161,8 +166,6 @@ public class LanguageSettings extends PreferenceActivity { StringBuilder builder = new StringBuilder(256); - boolean haveLastInputMethod = false; - int firstEnabled = -1; int N = mInputMethodProperties.size(); for (int i = 0; i < N; ++i) { @@ -178,7 +181,6 @@ public class LanguageSettings extends PreferenceActivity { if (firstEnabled < 0) { firstEnabled = i; } - if (hasIt) haveLastInputMethod = true; } else if (hasIt) { mLastInputMethodId = mLastTickedInputMethodId; } @@ -196,7 +198,8 @@ public class LanguageSettings extends PreferenceActivity { Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); Settings.Secure.putString(getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId); + Settings.Secure.DEFAULT_INPUT_METHOD, + mLastInputMethodId != null ? mLastInputMethodId : ""); } @Override @@ -210,10 +213,49 @@ public class LanguageSettings extends PreferenceActivity { } if (preference instanceof CheckBoxPreference) { - CheckBoxPreference chkPref = (CheckBoxPreference) preference; - String id = getInputMethodIdFromKey(chkPref.getKey()); + final CheckBoxPreference chkPref = (CheckBoxPreference) preference; + final String id = getInputMethodIdFromKey(chkPref.getKey()); if (chkPref.isChecked()) { - mLastTickedInputMethodId = id; + InputMethodInfo selImi = null; + final int N = mInputMethodProperties.size(); + for (int i=0; i<N; i++) { + InputMethodInfo imi = mInputMethodProperties.get(i); + if (id.equals(imi.getId())) { + selImi = imi; + if (isSystemIme(imi)) { + // This is a built-in IME, so no need to warn. + mLastTickedInputMethodId = id; + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + } + } + chkPref.setChecked(false); + if (selImi == null) { + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + AlertDialog d = (new AlertDialog.Builder(this)) + .setTitle(android.R.string.dialog_alert_title) + .setIcon(android.R.drawable.ic_dialog_alert) + .setMessage(getString(R.string.ime_security_warning, + selImi.getServiceInfo().applicationInfo.loadLabel( + getPackageManager()))) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + chkPref.setChecked(true); + mLastTickedInputMethodId = id; + } + + }) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + + }) + .create(); + d.show(); } else if (id.equals(mLastTickedInputMethodId)) { mLastTickedInputMethodId = null; } diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/ManageApplications.java index 7402727..7f2e2ce 100644 --- a/src/com/android/settings/ManageApplications.java +++ b/src/com/android/settings/ManageApplications.java @@ -59,6 +59,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -138,8 +139,9 @@ public class ManageApplications extends ListActivity implements private static final int REORDER_LIST = HANDLER_MESSAGE_BASE+4; private static final int ADD_PKG_START = HANDLER_MESSAGE_BASE+5; private static final int ADD_PKG_DONE = HANDLER_MESSAGE_BASE+6; - private static final int REFRESH_ICONS = HANDLER_MESSAGE_BASE+7; - private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+8; + private static final int REFRESH_APP_RESOURCE = HANDLER_MESSAGE_BASE+7; + private static final int REFRESH_DONE = HANDLER_MESSAGE_BASE+8; + private static final int NEXT_LOAD_STEP = HANDLER_MESSAGE_BASE+9; // observer object used for computing pkg sizes private PkgSizeObserver mObserver; @@ -248,37 +250,10 @@ public class ManageApplications extends ListActivity implements switch (msg.what) { case INIT_PKG_INFO: if(localLOGV) Log.i(TAG, "Message INIT_PKG_INFO"); + List<ApplicationInfo> newList = getInstalledApps(FILTER_APPS_ALL); if (!mJustCreated) { // Add or delete newly created packages by comparing lists - List<ApplicationInfo> newList = getInstalledApps(FILTER_APPS_ALL); - int oldCount = mAppPropCache.size(); - boolean idxArr[] = new boolean[oldCount]; - for ( int i = 0; i < oldCount; i++) { - idxArr[i] = false; - } - - if (newList != null) { - for (ApplicationInfo app : newList) { - AppInfo aInfo = mAppPropCache.get(app.packageName); - if ( aInfo == null) { - // New package. post an ADD_PKG message - if(localLOGV) Log.i(TAG, "Adding pkg: "+app.packageName); - updatePackageList(Intent.ACTION_PACKAGE_ADDED, app.packageName); - } else { - idxArr[aInfo.index] = true; - } - } - Set<String> keyList = mAppPropCache.keySet(); - for (String key : keyList) { - AppInfo aInfo = mAppPropCache.get(key); - int idx = aInfo.index; - if (!idxArr[idx]) { - String pkg = aInfo.pkgName; - if(localLOGV) Log.i(TAG, "Deleting pkg: " + pkg); - updatePackageList(Intent.ACTION_PACKAGE_REMOVED, pkg); - } - } - } + updateAppList(newList); } // Retrieve the package list and init some structures initAppList(mFilterApps); @@ -308,46 +283,11 @@ public class ManageApplications extends ListActivity implements mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo( mComputeIndex), COMPUTE_PKG_SIZE_DONE); - } else { - // check for added/removed packages - Set<String> keys = mAddRemoveMap.keySet(); - Iterator<String> iter = keys.iterator(); - List<String> removeList = new ArrayList<String>(); - boolean added = false; - boolean removed = false; - while (iter.hasNext()) { - String key = iter.next(); - if (mAddRemoveMap.get(key) == Boolean.TRUE) { - // add - try { - info = mPm.getApplicationInfo(key, 0); - mAppInfoAdapter.addApplicationInfo(info); - added = true; - } catch (NameNotFoundException e) { - Log.w(TAG, "Invalid added package:"+key+" Ignoring entry"); - } - } else { - // remove - removeList.add(key); - removed = true; - } - } - // remove uninstalled packages from list - if (removed) { - mAppInfoAdapter.removeFromList(removeList); - } - // handle newly installed packages - if (added) { - mObserver.invokeGetSizeInfo(mAppInfoAdapter.getApplicationInfo( - mComputeIndex), - COMPUTE_PKG_SIZE_DONE); - } else { - // end computation here - mComputeSizes = true; - mFirst = true; - mAppInfoAdapter.sortList(mSortOrder); - mHandler.sendEmptyMessage(NEXT_LOAD_STEP); - } + } else { + // End computation here + mComputeSizes = true; + mFirst = true; + mHandler.sendEmptyMessage(NEXT_LOAD_STEP); } break; case REMOVE_PKG: @@ -381,8 +321,7 @@ public class ManageApplications extends ListActivity implements } else if(menuOption != mFilterApps) { // Option to filter list mFilterApps = menuOption; - boolean ret = mAppInfoAdapter.resetAppList(mFilterApps, - getInstalledApps(mFilterApps)); + boolean ret = mAppInfoAdapter.resetAppList(mFilterApps); if(!ret) { // Reset cache mAppPropCache = null; @@ -398,7 +337,7 @@ public class ManageApplications extends ListActivity implements Log.w(TAG, "Ignoring message:ADD_PKG_START for null pkgName"); break; } - if (!mComputeSizes) { + if (!mComputeSizes || !mLoadLabels) { Boolean currB = mAddRemoveMap.get(pkgName); if (currB == null || (currB.equals(Boolean.FALSE))) { mAddRemoveMap.put(pkgName, Boolean.TRUE); @@ -406,11 +345,11 @@ public class ManageApplications extends ListActivity implements break; } try { - info = mPm.getApplicationInfo(pkgName, 0); - } catch (NameNotFoundException e) { - Log.w(TAG, "Couldnt find application info for:"+pkgName); - break; - } + info = mPm.getApplicationInfo(pkgName, 0); + } catch (NameNotFoundException e) { + Log.w(TAG, "Couldnt find application info for:"+pkgName); + break; + } mObserver.invokeGetSizeInfo(info, ADD_PKG_DONE); break; case ADD_PKG_DONE: @@ -422,19 +361,33 @@ public class ManageApplications extends ListActivity implements ps = data.getParcelable(ATTR_APP_PKG_STATS); mAppInfoAdapter.addToList(pkgName, ps); break; - case REFRESH_ICONS: - Map<String, AppInfo> iconMap = (Map<String, AppInfo>) msg.obj; - if(iconMap == null) { + case REFRESH_APP_RESOURCE: + AppInfo aInfo = (AppInfo) msg.obj; + if(aInfo == null) { Log.w(TAG, "Error loading icons for applications"); } else { - mAppInfoAdapter.updateAppsResourceInfo(iconMap); + mAppInfoAdapter.updateAppsResourceInfo(aInfo); } + break; + case REFRESH_DONE: mLoadLabels = true; mHandler.sendEmptyMessage(NEXT_LOAD_STEP); break; case NEXT_LOAD_STEP: if (mComputeSizes && mLoadLabels) { doneLoadingData(); + // Check for added/removed packages + Set<String> keys = mAddRemoveMap.keySet(); + for (String key : keys) { + if (mAddRemoveMap.get(key) == Boolean.TRUE) { + // Add the package + updatePackageList(Intent.ACTION_PACKAGE_ADDED, key); + } else { + // Remove the package + updatePackageList(Intent.ACTION_PACKAGE_REMOVED, key); + } + } + mAddRemoveMap.clear(); } else if (!mComputeSizes && !mLoadLabels) { // Either load the package labels or initiate get size info if (mSizesFirst) { @@ -446,12 +399,13 @@ public class ManageApplications extends ListActivity implements // Create list view from the adapter here. Wait till the sort order // of list is defined. its either by label or by size. so atleast one of the // first steps should be complete before filling the list + mAppInfoAdapter.sortList(mSortOrder); if (mJustCreated) { // Set the adapter here. mJustCreated = false; mListView.setAdapter(mAppInfoAdapter); dismissLoadingMsg(); - } + } if (!mComputeSizes) { initComputeSizes(); } else if (!mLoadLabels) { @@ -465,7 +419,49 @@ public class ManageApplications extends ListActivity implements } }; - + /* + * This method compares the current cache against a new list of + * installed applications and tries to update the list with add or remove + * messages. + */ + private boolean updateAppList(List<ApplicationInfo> newList) { + if ((newList == null) || (mAppPropCache == null)) { + return false; + } + Set<String> existingList = new HashSet<String>(); + boolean ret = false; + // Loop over new list and find out common elements between old and new lists + for (ApplicationInfo info : newList) { + String pkgName = info.packageName; + AppInfo aInfo = mAppPropCache.get(pkgName); + if (aInfo != null) { + existingList.add(pkgName); + } else { + // New package. update info by refreshing + if (localLOGV) Log.i(TAG, "New pkg :"+pkgName+" installed when paused"); + updatePackageList(Intent.ACTION_PACKAGE_ADDED, pkgName); + ret = true; + } + } + // Loop over old list and figure out state entries + List<String> deletedList = null; + Set<String> staleList = mAppPropCache.keySet(); + for (String pkgName : staleList) { + if (!existingList.contains(pkgName)) { + if (localLOGV) Log.i(TAG, "Pkg :"+pkgName+" deleted when paused"); + if (deletedList == null) { + deletedList = new ArrayList<String>(); + deletedList.add(pkgName); + } + ret = true; + } + } + // Delete right away + if (deletedList != null) { + mAppInfoAdapter.removeFromList(deletedList); + } + return ret; + } private void doneLoadingData() { setProgressBarIndeterminateVisibility(false); @@ -524,16 +520,68 @@ public class ManageApplications extends ListActivity implements } } + /* + * Utility method used to figure out list of apps based on filterOption + * If the framework supports an additional flag to indicate running apps + * we can get away with some code here. + */ + List<ApplicationInfo> getFilteredApps(List<ApplicationInfo> pAppList, int filterOption) { + List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>(); + if(pAppList == null) { + return retList; + } + if (filterOption == FILTER_APPS_THIRD_PARTY) { + List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> (); + for (ApplicationInfo appInfo : pAppList) { + boolean flag = false; + if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + // Updated system app + flag = true; + } else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + // Non-system app + flag = true; + } + if (flag) { + appList.add(appInfo); + } + } + return appList; + } else if (filterOption == FILTER_APPS_RUNNING) { + List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> (); + List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList(); + if ((procList == null) || (procList.size() == 0)) { + return appList; + } + // Retrieve running processes from ActivityManager + HashMap<String, ActivityManager.RunningAppProcessInfo> runningMap = + new HashMap<String, ActivityManager.RunningAppProcessInfo>(); + for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) { + if ((appProcInfo != null) && (appProcInfo.pkgList != null)){ + int size = appProcInfo.pkgList.length; + for (int i = 0; i < size; i++) { + runningMap.put(appProcInfo.pkgList[i], appProcInfo); + } + } + } + // Query list to find running processes in current list + for (ApplicationInfo appInfo : pAppList) { + if (runningMap.get(appInfo.packageName) != null) { + appList.add(appInfo); + } + } + return appList; + } else { + return pAppList; + } + } + private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() { ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); return am.getRunningAppProcesses(); } - // some initialization code used when kicking off the size computation private void initAppList(int filterOption) { - // Initialize lists - List<ApplicationInfo> appList = getInstalledApps(filterOption); - initAppList(appList, filterOption); + initAppList(null, filterOption); } // some initialization code used when kicking off the size computation @@ -544,7 +592,7 @@ public class ManageApplications extends ListActivity implements mLoadLabels = false; // Initialize lists mAddRemoveMap = new TreeMap<String, Boolean>(); - mAppInfoAdapter.resetAppList(filterOption, appList); + mAppInfoAdapter.initMapFromList(appList, filterOption); } // Utility method to start a thread to read application labels and icons @@ -552,6 +600,7 @@ public class ManageApplications extends ListActivity implements //load resources now if(mResourceThread.isAlive()) { mResourceThread.interrupt(); + mResourceThread = new ResourceLoaderThread(); } mResourceThread.loadAllResources(mAppInfoAdapter.getAppList()); } @@ -605,13 +654,13 @@ public class ManageApplications extends ListActivity implements for (ApplicationInfo appInfo : mAppList) { CharSequence appName = appInfo.loadLabel(mPm); Drawable appIcon = appInfo.loadIcon(mPm); - iconMap.put(appInfo.packageName, - new AppInfo(appInfo.packageName, appName, appIcon)); + Message msg = mHandler.obtainMessage(REFRESH_APP_RESOURCE); + msg.obj = new AppInfo(appInfo.packageName, appName, appIcon); + mHandler.sendMessage(msg); } } - Message msg = mHandler.obtainMessage(REFRESH_ICONS); - msg.obj = iconMap; - mHandler.sendMessage(msg); + Message doneMsg = mHandler.obtainMessage(REFRESH_DONE); + mHandler.sendMessage(doneMsg); } } @@ -700,6 +749,7 @@ public class ManageApplications extends ListActivity implements */ class AppInfoAdapter extends BaseAdapter { private Map<String, AppInfo> mAppPropMap; + private List<ApplicationInfo> mAppList; private List<ApplicationInfo> mAppLocalList; ApplicationInfo.DisplayNameComparator mAlphaComparator; AppInfoComparator mSizeComparator; @@ -710,34 +760,51 @@ public class ManageApplications extends ListActivity implements } return mAppPropCache.get(packageName); } - - public AppInfoAdapter(Context c, List<ApplicationInfo> appList) { - mAppLocalList = appList; - boolean useCache = false; + + // Make sure the cache or map contains entries for all elements + // in appList for a valid sort. + public void initMapFromList(List<ApplicationInfo> appList, int filterOption) { + if (appList == null) { + // Just refresh the list + appList = mAppList; + } else { + mAppList = appList; + } + mAppLocalList = getFilteredApps(appList, filterOption); int sortOrder = SORT_ORDER_ALPHA; - int imax = mAppLocalList.size(); - if(mAppPropCache != null) { - useCache = true; - // Activity has been resumed. can use the cache to populate values initially - mAppPropMap = mAppPropCache; + if (mAppPropCache != null) { + // Retain previous sort order sortOrder = mSortOrder; + mAppPropMap = mAppPropCache; + } else { + // Recreate property map + mAppPropMap = new TreeMap<String, AppInfo>(); } + // Re init the comparators + mAlphaComparator = null; + mSizeComparator = null; + sortAppList(sortOrder); - // Recreate property map - mAppPropMap = new TreeMap<String, AppInfo>(); + int imax = appList.size(); for (int i = 0; i < imax; i++) { - ApplicationInfo info = mAppLocalList.get(i); - AppInfo aInfo = getFromCache(info.packageName); + ApplicationInfo info = appList.get(i); + AppInfo aInfo = mAppPropMap.get(info.packageName); if(aInfo == null){ aInfo = new AppInfo(info.packageName, i, - info.packageName, mDefaultAppIcon, null); + info.packageName, mDefaultAppIcon, null); + if (localLOGV) Log.i(TAG, "Creating entry pkg:"+info.packageName+" to map"); } else { aInfo.index = i; + if (localLOGV) Log.i(TAG, "Adding pkg:"+info.packageName+" to map"); } mAppPropMap.put(info.packageName, aInfo); } } + public AppInfoAdapter(Context c, List<ApplicationInfo> appList) { + initMapFromList(appList, mFilterApps); + } + public int getCount() { return mAppLocalList.size(); } @@ -772,14 +839,6 @@ public class ManageApplications extends ListActivity implements } return mAppLocalList.get(position); } - - public void addApplicationInfo(ApplicationInfo info) { - if(info == null) { - Log.w(TAG, "Ignoring null add in List Adapter"); - return; - } - mAppLocalList.add(info); - } public long getItemId(int position) { int imax = mAppLocalList.size(); @@ -843,9 +902,8 @@ public class ManageApplications extends ListActivity implements private void adjustIndex() { int imax = mAppLocalList.size(); - ApplicationInfo info; for (int i = 0; i < imax; i++) { - info = mAppLocalList.get(i); + ApplicationInfo info = mAppLocalList.get(i); mAppPropMap.get(info.packageName).index = i; } } @@ -855,21 +913,28 @@ public class ManageApplications extends ListActivity implements } public void sortList(int sortOrder) { + if (localLOGV) Log.i(TAG, "sortOrder = "+sortOrder); sortAppList(sortOrder); adjustIndex(); notifyDataSetChanged(); } - public boolean resetAppList(int filterOption, List<ApplicationInfo> appList) { - // Create application list based on the filter value - mAppLocalList = appList; + /* + * Reset the application list associated with this adapter. + * @param filterOption Sort the list based on this value + * @param appList the actual application list that is used to reset + * @return Return a boolean value to indicate inconsistency + */ + public boolean resetAppList(int filterOption) { + // Change application list based on filter option + mAppLocalList = getFilteredApps(mAppList, filterOption); // Check for all properties in map before sorting. Populate values from cache for(ApplicationInfo applicationInfo : mAppLocalList) { AppInfo appInfo = mAppPropMap.get(applicationInfo.packageName); if(appInfo == null) { AppInfo rInfo = getFromCache(applicationInfo.packageName); if(rInfo == null) { - // Need to load resources again. Inconsistency somewhere + // Need to load resources again. Inconsistency somewhere return false; } mAppPropMap.put(applicationInfo.packageName, rInfo); @@ -912,7 +977,7 @@ public class ManageApplications extends ListActivity implements return true; } boolean changed = false; - for (ApplicationInfo info : mAppLocalList) { + for (ApplicationInfo info : mAppList) { AppInfo pInfo = iconMap.get(info.packageName); if(pInfo != null) { AppInfo aInfo = mAppPropMap.get(info.packageName); @@ -930,27 +995,39 @@ public class ManageApplications extends ListActivity implements return true; } + public boolean updateAppsResourceInfo(AppInfo pInfo) { + if(pInfo == null) { + Log.w(TAG, "Null info when refreshing icon in List Adapter"); + return false; + } + AppInfo aInfo = mAppPropMap.get(pInfo.pkgName); + if (aInfo != null) { + aInfo.refreshIcon(pInfo); + notifyDataSetChanged(); + return true; + } + return false; + } + private boolean shouldBeInList(int filterOption, ApplicationInfo info) { // Match filter here - boolean addToCurrList = false; if (filterOption == FILTER_APPS_RUNNING) { List<ApplicationInfo> runningList = getInstalledApps(FILTER_APPS_RUNNING); for (ApplicationInfo running : runningList) { if (running.packageName.equalsIgnoreCase(info.packageName)) { - addToCurrList = true; - break; + return true; } } } else if (filterOption == FILTER_APPS_THIRD_PARTY) { if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - addToCurrList = true; + return true; } else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { - addToCurrList = true; + return true; } } else { return true; } - return addToCurrList; + return false; } /* @@ -961,20 +1038,15 @@ public class ManageApplications extends ListActivity implements * @param ps PackageStats of new package */ public void addToList(String pkgName, PackageStats ps) { - if(pkgName == null) { - Log.w(TAG, "Adding null pkg to List Adapter"); + if((pkgName == null) || (ps == null)) { + if (pkgName == null) { + Log.w(TAG, "Adding null pkg to List Adapter"); + } else { + Log.w(TAG, "Adding pkg : "+pkgName+" with invalid PackageStats"); + } return; } boolean notInList = true; - int newIdx = getIndex(pkgName); - if (newIdx != -1) { - notInList = false; - if (mAppPropMap.get(pkgName) != null) { - // weird. just ignore entry - Log.i(TAG, "Package:"+pkgName+" already added"); - return; - } - } // Get ApplicationInfo ApplicationInfo info = null; try { @@ -988,14 +1060,17 @@ public class ManageApplications extends ListActivity implements Log.i(TAG, "Null ApplicationInfo for package:"+pkgName); return; } - // Add entry to map - mAppPropMap.put(pkgName, new AppInfo(pkgName, newIdx, + // Add entry to local list + mAppList.add(info); + // Add entry to map. Note that the index gets adjusted later on based on + // whether the newly added package is part of displayed list + mAppPropMap.put(pkgName, new AppInfo(pkgName, -1, info.loadLabel(mPm), info.loadIcon(mPm), ps)); // Add to list if (notInList && (shouldBeInList(mFilterApps, info))) { // Binary search returns a negative index (ie -index) of the position where // this might be inserted. - newIdx = Collections.binarySearch(mAppLocalList, info, + int newIdx = Collections.binarySearch(mAppLocalList, info, getAppComparator(mSortOrder)); if(newIdx >= 0) { Log.i(TAG, "Strange. Package:"+pkgName+" is not new"); @@ -1010,11 +1085,30 @@ public class ManageApplications extends ListActivity implements } } + private void removePkgListBase(List<String> pkgNames) { + for (String pkg : pkgNames) { + removePkgBase(pkg); + } + } + + private void removePkgBase(String pkgName) { + int imax = mAppList.size(); + for (int i = 0; i < imax; i++) { + ApplicationInfo app = mAppList.get(i); + if (app.packageName.equalsIgnoreCase(pkgName)) { + if (localLOGV) Log.i(TAG, "Removing pkg: "+pkgName+" from base list"); + mAppList.remove(i); + return; + } + } + } + public void removeFromList(List<String> pkgNames) { if(pkgNames == null) { Log.w(TAG, "Removing null pkg list from List Adapter"); return; } + removePkgListBase(pkgNames); int imax = mAppLocalList.size(); boolean found = false; ApplicationInfo info; @@ -1042,7 +1136,7 @@ public class ManageApplications extends ListActivity implements } // Sort idxArr Arrays.sort(idxArr); - // remove the packages based on decending indices + // remove the packages based on descending indices for (k = kmax-1; k >= 0; k--) { // Check if package has been found in the list of existing apps first if(idxArr[k] == -1) { @@ -1051,13 +1145,13 @@ public class ManageApplications extends ListActivity implements info = mAppLocalList.get(idxArr[k]); mAppLocalList.remove(idxArr[k]); mAppPropMap.remove(info.packageName); - if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " list"); + if (localLOGV) Log.i(TAG, "Removed pkg:"+info.packageName+ " from display list"); } if (found) { adjustIndex(); notifyDataSetChanged(); } - } + } public void updateAppSize(String pkgName, PackageStats ps) { if(pkgName == null) { @@ -1213,6 +1307,7 @@ public class ManageApplications extends ListActivity implements requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.compute_sizes); + showLoadingMsg(); mDefaultAppIcon =Resources.getSystem().getDrawable( com.android.internal.R.drawable.sym_def_app_icon); mInvalidSizeStr = getText(R.string.invalid_size_value); @@ -1231,7 +1326,6 @@ public class ManageApplications extends ListActivity implements lv.setItemsCanFocus(true); lv.setOnItemClickListener(this); mListView = lv; - showLoadingMsg(); } @Override @@ -1263,9 +1357,9 @@ public class ManageApplications extends ListActivity implements super.onStart(); // Create a thread to load resources mResourceThread = new ResourceLoaderThread(); - sendMessageToHandler(INIT_PKG_INFO); // register receiver mReceiver.registerReceiver(); + sendMessageToHandler(INIT_PKG_INFO); } @Override diff --git a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java index f1a2a1e..d458c5f 100644 --- a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java @@ -92,7 +92,6 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan } IntentFilter filter = new IntentFilter(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); - filter.addAction(BluetoothIntent.DISABLED_ACTION); mContext.registerReceiver(mReceiver, filter); mCheckBoxPreference.setOnPreferenceChangeListener(this); diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index 661700f..82961b8 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -17,8 +17,9 @@ package com.android.settings.bluetooth; import com.android.settings.R; -import com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -70,15 +71,15 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener { return; } - ExtendedBluetoothState state = mLocalManager.getBluetoothState(); + int state = mLocalManager.getBluetoothState(); // This is the widget enabled state, not the preference toggled state - mCheckBoxPreference.setEnabled(state == ExtendedBluetoothState.ENABLED || - state == ExtendedBluetoothState.DISABLED); + mCheckBoxPreference.setEnabled(state == BluetoothDevice.BLUETOOTH_STATE_ON || + state == BluetoothDevice.BLUETOOTH_STATE_OFF); // BT state is not a sticky broadcast, so set it manually handleStateChanged(state); mContext.registerReceiver(mReceiver, - new IntentFilter(LocalBluetoothManager.EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION)); + new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)); mCheckBoxPreference.setOnPreferenceChangeListener(this); } @@ -106,22 +107,24 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener { mLocalManager.setBluetoothEnabled(enable); } - private void handleStateChanged(ExtendedBluetoothState state) { + private void handleStateChanged(int state) { - if (state == ExtendedBluetoothState.DISABLED || state == ExtendedBluetoothState.ENABLED) { - mCheckBoxPreference.setChecked(state == ExtendedBluetoothState.ENABLED); - mCheckBoxPreference - .setSummary(state == ExtendedBluetoothState.DISABLED ? mOriginalSummary : null); + if (state == BluetoothDevice.BLUETOOTH_STATE_OFF || + state == BluetoothDevice.BLUETOOTH_STATE_ON) { + mCheckBoxPreference.setChecked(state == BluetoothDevice.BLUETOOTH_STATE_ON); + mCheckBoxPreference.setSummary(state == BluetoothDevice.BLUETOOTH_STATE_OFF ? + mOriginalSummary : + null); mCheckBoxPreference.setEnabled(isEnabledByDependency()); - } else if (state == ExtendedBluetoothState.ENABLING || - state == ExtendedBluetoothState.DISABLING) { - mCheckBoxPreference.setSummary(state == ExtendedBluetoothState.ENABLING + } else if (state == BluetoothDevice.BLUETOOTH_STATE_TURNING_ON || + state == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) { + mCheckBoxPreference.setSummary(state == BluetoothDevice.BLUETOOTH_STATE_TURNING_ON ? R.string.wifi_starting : R.string.wifi_stopping); - } else if (state == ExtendedBluetoothState.UNKNOWN) { + } else { mCheckBoxPreference.setChecked(false); mCheckBoxPreference.setSummary(R.string.wifi_error); mCheckBoxPreference.setEnabled(true); diff --git a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java index 2ad5726..71b91d3 100644 --- a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java +++ b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java @@ -16,8 +16,6 @@ package com.android.settings.bluetooth; -import com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState; - import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; @@ -50,12 +48,10 @@ public class BluetoothEventRedirector { String action = intent.getAction(); String address = intent.getStringExtra(BluetoothIntent.ADDRESS); - if (action.equals(BluetoothIntent.ENABLED_ACTION)) { - mManager.setBluetoothStateInt(ExtendedBluetoothState.ENABLED); - - } else if (action.equals(BluetoothIntent.DISABLED_ACTION)) { - mManager.setBluetoothStateInt(ExtendedBluetoothState.DISABLED); - + if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) { + int state = intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE, + BluetoothError.ERROR); + mManager.setBluetoothStateInt(state); } else if (action.equals(BluetoothIntent.DISCOVERY_STARTED_ACTION)) { mManager.onScanningStateChanged(true); @@ -86,25 +82,29 @@ public class BluetoothEventRedirector { } } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) { - mManager.getLocalDeviceManager().onProfileStateChanged(address); - int newState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, 0); int oldState = intent.getIntExtra(BluetoothIntent.HEADSET_PREVIOUS_STATE, 0); if (newState == BluetoothHeadset.STATE_DISCONNECTED && oldState == BluetoothHeadset.STATE_CONNECTING) { Log.i(TAG, "Failed to connect BT headset"); } - - } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { - mManager.getLocalDeviceManager().onProfileStateChanged(address); + boolean transientState = !(newState == BluetoothHeadset.STATE_CONNECTED + || newState == BluetoothHeadset.STATE_DISCONNECTED); + mManager.getLocalDeviceManager().onProfileStateChanged(address,transientState); + + } else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) { int newState = intent.getIntExtra(BluetoothA2dp.SINK_STATE, 0); int oldState = intent.getIntExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, 0); if (newState == BluetoothA2dp.STATE_DISCONNECTED && oldState == BluetoothA2dp.STATE_CONNECTING) { Log.i(TAG, "Failed to connect BT A2DP"); } - + + boolean transientState = !(newState == BluetoothA2dp.STATE_CONNECTED + || newState == BluetoothA2dp.STATE_DISCONNECTED); + mManager.getLocalDeviceManager().onProfileStateChanged(address, transientState); + } else if (action.equals(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION)) { mManager.getLocalDeviceManager().onBtClassChanged(address); @@ -120,8 +120,7 @@ public class BluetoothEventRedirector { IntentFilter filter = new IntentFilter(); // Bluetooth on/off broadcasts - filter.addAction(BluetoothIntent.ENABLED_ACTION); - filter.addAction(BluetoothIntent.DISABLED_ACTION); + filter.addAction(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); // Discovery broadcasts filter.addAction(BluetoothIntent.DISCOVERY_STARTED_ACTION); diff --git a/src/com/android/settings/bluetooth/BluetoothNamePreference.java b/src/com/android/settings/bluetooth/BluetoothNamePreference.java index 3065b26..40bab2c 100644 --- a/src/com/android/settings/bluetooth/BluetoothNamePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothNamePreference.java @@ -17,6 +17,7 @@ package com.android.settings.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -39,7 +40,14 @@ public class BluetoothNamePreference extends EditTextPreference { private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - setSummaryToName(); + String action = intent.getAction(); + if (action.equals(BluetoothIntent.NAME_CHANGED_ACTION)) { + setSummaryToName(); + } else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) && + (intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE, + BluetoothError.ERROR) == BluetoothDevice.BLUETOOTH_STATE_ON)) { + setSummaryToName(); + } } }; @@ -53,7 +61,7 @@ public class BluetoothNamePreference extends EditTextPreference { public void resume() { IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothIntent.ENABLED_ACTION); + filter.addAction(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); filter.addAction(BluetoothIntent.NAME_CHANGED_ACTION); getContext().registerReceiver(mReceiver, filter); } diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 5adada3..e6ac5fd 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -18,11 +18,12 @@ package com.android.settings.bluetooth; import com.android.settings.ProgressCategory; import com.android.settings.R; -import com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState; import java.util.List; import java.util.WeakHashMap; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -117,8 +118,8 @@ public class BluetoothSettings extends PreferenceActivity mLocalManager.startScanning(false); - registerReceiver(mReceiver, - new IntentFilter(LocalBluetoothManager.EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION)); + registerReceiver(mReceiver, + new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)); mLocalManager.setForegroundActivity(this); } @@ -248,12 +249,12 @@ public class BluetoothSettings extends PreferenceActivity mDeviceList.setProgress(started); } - private void onBluetoothStateChanged(ExtendedBluetoothState bluetoothState) { + private void onBluetoothStateChanged(int bluetoothState) { // When bluetooth is enabled (and we are in the activity, which we are), // we should start a scan - if (bluetoothState == ExtendedBluetoothState.ENABLED) { + if (bluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON) { mLocalManager.startScanning(false); - } else if (bluetoothState == ExtendedBluetoothState.DISABLED) { + } else if (bluetoothState == BluetoothDevice.BLUETOOTH_STATE_OFF) { mDeviceList.setProgress(false); } } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothDevice.java b/src/com/android/settings/bluetooth/LocalBluetoothDevice.java index a488540..eedae93 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothDevice.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothDevice.java @@ -20,22 +20,23 @@ import com.android.settings.R; import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; import android.app.AlertDialog; -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothClass; -import android.bluetooth.IBluetoothDeviceCallback; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; -import android.os.IBinder; -import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; +import java.text.DateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; /** @@ -71,6 +72,173 @@ public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> { */ private boolean mIsConnectingErrorPossible; + // Max time to hold the work queue if we don't get or missed a response + // from the bt framework. + private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000; + + private enum BluetoothCommand { + CONNECT, DISCONNECT, + } + + class BluetoothJob { + final BluetoothCommand command; // CONNECT, DISCONNECT + final LocalBluetoothDevice device; + final Profile profile; // HEADSET, A2DP, etc + // 0 means this command was not been sent to the bt framework. + long timeSent; + + public BluetoothJob(BluetoothCommand command, + LocalBluetoothDevice device, Profile profile) { + this.command = command; + this.device = device; + this.profile = profile; + this.timeSent = 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(command.name()); + sb.append(" Address:").append(device.mAddress); + sb.append(" Profile:").append(profile.name()); + sb.append(" TimeSent:"); + if (timeSent == 0) { + sb.append("not yet"); + } else { + sb.append(DateFormat.getTimeInstance().format(new Date(timeSent))); + } + return sb.toString(); + } + } + + /** + * We want to serialize connect and disconnect calls. http://b/170538 + * This are some headsets that may have L2CAP resource limitation. We want + * to limit the bt bandwidth usage. + * + * A queue to keep track of asynchronous calls to the bt framework. The + * first item, if exist, should be in progress i.e. went to the bt framework + * already, waiting for a notification to come back. The second item and + * beyond have not been sent to the bt framework yet. + */ + private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>(); + + private void queueCommand(BluetoothJob job) { + Log.d(TAG, workQueue.toString()); + synchronized (workQueue) { + boolean processNow = false; + long now = System.currentTimeMillis(); + + Iterator<BluetoothJob> it = workQueue.iterator(); + while (it.hasNext()) { + BluetoothJob existingJob = it.next(); + + // Remove any pending CONNECTS when we receive a DISCONNECT + if (job.command == BluetoothCommand.DISCONNECT) { + if (existingJob.timeSent == 0 + && existingJob.command == BluetoothCommand.CONNECT + && existingJob.device.mAddress.equals(job.device.mAddress) + && existingJob.profile == job.profile) { + it.remove(); + continue; + } + } + + // Defensive Code: Remove any job that older than a preset time. + // We never got a call back. It is better to have overlapping + // calls than to get stuck. + Log.d(TAG, "Age:" + (now - existingJob.timeSent)); + if (existingJob.timeSent != 0 + && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) { + Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString()); + it.remove(); + processNow = true; + continue; + } + } + + // Add job to queue + Log.d(TAG, "Adding: " + job.toString()); + workQueue.add(job); + + // if there's nothing pending from before, send the command to bt + // framework immediately. + if (workQueue.size() == 1 || processNow) { + Log.d(TAG, "workQueue.size() == 1 || TimeOut -> process command now"); + // If the failed to process, just drop it from the queue. + // There will be no callback to remove this from the queue. + processCommands(); + } + } + } + + private boolean processCommand(BluetoothJob job) { + boolean successful = false; + if (job.timeSent == 0) { + job.timeSent = System.currentTimeMillis(); + switch (job.command) { + case CONNECT: + successful = connectInt(job.device, job.profile); + break; + case DISCONNECT: + successful = disconnectInt(job.device, job.profile); + break; + } + + if (successful) { + Log.d(TAG, "Command sent successfully:" + job.toString()); + } else { + Log.d(TAG, "Framework rejected command immediately:" + job.toString()); + } + + } else { + Log.d(TAG, "Job already has a sent time. Skip. " + job.toString()); + } + + return successful; + } + + public void onProfileStateChanged() { + // Remove the first item and process the next one + BluetoothJob job = workQueue.poll(); + if (job == null) { + Log.w(TAG, "Yikes, onProfileStateChanged called but job queue is empty"); + } else if (job.device.mAddress != mAddress) { + Log.w(TAG, "Yikes, onProfileStateChanged called but the address differ. this.mAddress=" + + mAddress + " workQueue.head=" + job.toString()); + } else { + Log.d(TAG, "LocalBluetoothDevice.onProfileStateChanged() called. MAC addr matched"); + } + + processCommands(); + } + + /* + * This method is called in 2 places: + * 1) queryCommand() - when someone or something want to connect or + * disconnect + * 2) onProfileStateChanged() - when the framework sends an intent + * notification when it finishes processing a command + */ + private void processCommands() { + Iterator<BluetoothJob> it = workQueue.iterator(); + while (it.hasNext()) { + BluetoothJob job = it.next(); + if (processCommand(job)) { + // Sent to bt framework. Done for now. Will remove this job + // from queue when we get an event + return; + } else { + /* + * If the command failed immediately, there will be no event + * callbacks. So delete the job immediately and move on to the + * next one + */ + it.remove(); + } + } + } + LocalBluetoothDevice(Context context, String address) { mLocalManager = LocalBluetoothManager.getInstance(context); if (mLocalManager == null) { @@ -102,12 +270,19 @@ public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> { } public void disconnect(Profile profile) { + queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile)); + } + + private boolean disconnectInt(LocalBluetoothDevice device, Profile profile) { LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); - int status = profileManager.getConnectionStatus(mAddress); + int status = profileManager.getConnectionStatus(device.mAddress); if (SettingsBtStatus.isConnectionStatusConnected(status)) { - profileManager.disconnect(mAddress); + if (profileManager.disconnect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) { + return true; + } } + return false; } public void askDisconnect() { @@ -153,7 +328,7 @@ public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> { LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); if (profileManager.isPreferred(mAddress)) { hasAtLeastOnePreferredProfile = true; - connectInt(profile); + queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); } } @@ -173,27 +348,30 @@ public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> { LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); profileManager.setPreferred(mAddress, true); - connectInt(profile); + queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); } } public void connect(Profile profile) { // Reset the only-show-one-error-dialog tracking variable mIsConnectingErrorPossible = true; - connectInt(profile); + queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); } - public void connectInt(Profile profile) { - if (!ensurePaired()) return; + private boolean connectInt(LocalBluetoothDevice device, Profile profile) { + if (!device.ensurePaired()) return false; LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile); - int status = profileManager.getConnectionStatus(mAddress); + int status = profileManager.getConnectionStatus(device.mAddress); if (!SettingsBtStatus.isConnectionStatusConnected(status)) { - if (profileManager.connect(mAddress) != BluetoothDevice.RESULT_SUCCESS) { - Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName); + if (profileManager.connect(device.mAddress) == BluetoothDevice.RESULT_SUCCESS) { + return true; } + Log.i(TAG, "Failed to connect " + profile.toString() + " to " + device.mName); } + Log.i(TAG, "Not connected"); + return false; } public void showConnectingError() { @@ -228,6 +406,24 @@ public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> { } public void unpair() { + synchronized (workQueue) { + // Remove any pending commands for this device + boolean processNow = false; + Iterator<BluetoothJob> it = workQueue.iterator(); + while (it.hasNext()) { + BluetoothJob job = it.next(); + if (job.device.mAddress.equals(this.mAddress)) { + it.remove(); + if (job.timeSent != 0) { + processNow = true; + } + } + } + if (processNow) { + processCommands(); + } + } + BluetoothDevice manager = mLocalManager.getBluetoothManager(); switch (getBondState()) { diff --git a/src/com/android/settings/bluetooth/LocalBluetoothDeviceManager.java b/src/com/android/settings/bluetooth/LocalBluetoothDeviceManager.java index 6bb2b4a..9527980 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothDeviceManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothDeviceManager.java @@ -16,11 +16,8 @@ package com.android.settings.bluetooth; -import android.app.AlertDialog; import android.bluetooth.BluetoothDevice; import android.util.Log; -import android.widget.Toast; -import android.content.Context; import com.android.settings.R; import com.android.settings.bluetooth.LocalBluetoothManager.Callback; @@ -190,10 +187,13 @@ public class LocalBluetoothDeviceManager { R.string.bluetooth_pairing_error_message); } - public synchronized void onProfileStateChanged(String address) { + public synchronized void onProfileStateChanged(String address, boolean transientState) { LocalBluetoothDevice device = findDevice(address); if (device == null) return; + if (!transientState) { + device.onProfileStateChanged(); + } device.refresh(); } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothManager.java b/src/com/android/settings/bluetooth/LocalBluetoothManager.java index 4671fac..1a848b2 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothManager.java @@ -25,6 +25,8 @@ import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothError; +import android.bluetooth.BluetoothIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -40,8 +42,6 @@ public class LocalBluetoothManager { private static final String TAG = "LocalBluetoothManager"; static final boolean V = true; - public static final String EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION = - "com.android.settings.bluetooth.intent.action.EXTENDED_BLUETOOTH_STATE_CHANGED"; private static final String SHARED_PREFERENCES_NAME = "bluetooth_settings"; private static LocalBluetoothManager INSTANCE; @@ -60,8 +60,7 @@ public class LocalBluetoothManager { private BluetoothEventRedirector mEventRedirector; private BluetoothA2dp mBluetoothA2dp; - public static enum ExtendedBluetoothState { ENABLED, ENABLING, DISABLED, DISABLING, UNKNOWN } - private ExtendedBluetoothState mState = ExtendedBluetoothState.UNKNOWN; + private int mState = BluetoothError.ERROR; private List<Callback> mCallbacks = new ArrayList<Callback>(); @@ -182,34 +181,27 @@ public class LocalBluetoothManager { } } - public ExtendedBluetoothState getBluetoothState() { + public int getBluetoothState() { - if (mState == ExtendedBluetoothState.UNKNOWN) { + if (mState == BluetoothError.ERROR) { syncBluetoothState(); } return mState; } - void setBluetoothStateInt(ExtendedBluetoothState state) { + void setBluetoothStateInt(int state) { mState = state; - - /* - * TODO: change to callback method. originally it was broadcast to - * parallel the framework's method, but it just complicates things here. - */ - // If this were a real API, I'd add as an extra - mContext.sendBroadcast(new Intent(EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION)); - - if (state == ExtendedBluetoothState.ENABLED || state == ExtendedBluetoothState.DISABLED) { - mLocalDeviceManager.onBluetoothStateChanged(state == ExtendedBluetoothState.ENABLED); + if (state == BluetoothDevice.BLUETOOTH_STATE_ON || + state == BluetoothDevice.BLUETOOTH_STATE_OFF) { + mLocalDeviceManager.onBluetoothStateChanged(state == BluetoothDevice.BLUETOOTH_STATE_ON); } } private void syncBluetoothState() { setBluetoothStateInt(mManager.isEnabled() - ? ExtendedBluetoothState.ENABLED - : ExtendedBluetoothState.DISABLED); + ? BluetoothDevice.BLUETOOTH_STATE_ON + : BluetoothDevice.BLUETOOTH_STATE_OFF); } public void setBluetoothEnabled(boolean enabled) { @@ -219,8 +211,8 @@ public class LocalBluetoothManager { if (wasSetStateSuccessful) { setBluetoothStateInt(enabled - ? ExtendedBluetoothState.ENABLING - : ExtendedBluetoothState.DISABLING); + ? BluetoothDevice.BLUETOOTH_STATE_TURNING_ON + : BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF); } else { if (V) { Log.v(TAG, diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java index 50edf86..24563a7 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java @@ -72,19 +72,22 @@ public abstract class LocalBluetoothProfileManager { /** * Temporary method to fill profiles based on a device's class. * + * NOTE: This list happens to define the connection order. We should put this logic in a more + * well known place when this method is no longer temporary. + * * @param btClass The class * @param profiles The list of profiles to fill */ public static void fill(int btClass, List<Profile> profiles) { profiles.clear(); - if (BluetoothA2dp.doesClassMatchSink(btClass)) { - profiles.add(Profile.A2DP); - } - if (BluetoothHeadset.doesClassMatch(btClass)) { profiles.add(Profile.HEADSET); } + + if (BluetoothA2dp.doesClassMatchSink(btClass)) { + profiles.add(Profile.A2DP); + } } protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) { @@ -214,7 +217,7 @@ public abstract class LocalBluetoothProfileManager { */ String address = mService.getHeadsetAddress(); if (TextUtils.isEmpty(address)) return; - mLocalManager.getLocalDeviceManager().onProfileStateChanged(address); + mLocalManager.getLocalDeviceManager().onProfileStateChanged(address, true); } }); } |