Merge "Import translations. DO NOT MERGE" into klp-dev
diff --git a/res/layout/empty_print_state.xml b/res/layout/empty_print_state.xml
index e97bb85..6a82703 100644
--- a/res/layout/empty_print_state.xml
+++ b/res/layout/empty_print_state.xml
@@ -28,7 +28,6 @@
         android:orientation="vertical">
 
         <ImageView
-            android:id="@+id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginBottom="12dip"
@@ -41,7 +40,8 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textColor="?android:attr/textColorSecondary">
+            android:textColor="?android:attr/textColorSecondary"
+            android:importantForAccessibility="no">
         </TextView>
 
     </LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9c5d039..71eefe8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3483,6 +3483,18 @@
     <!-- Template for the label of the state for a blocked print job. [CHAR LIMIT=25] -->
     <string name="print_blocked_state_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
 
+    <!-- Utterance to announce that the search box is shown. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <string name="print_search_box_shown_utterance">Search box shown</string>
+
+    <!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <string name="print_search_box_hidden_utterance">Search box hidden</string>
+
+    <!-- Utterance to announce a change in the number of matches during a search. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <plurals name="print_search_result_count_utterance">
+        <item quantity="one"><xliff:g id="count" example="1">%1$s</xliff:g> printer found</item>
+        <item quantity="other"><xliff:g id="count" example="2">%1$s</xliff:g> printers found</item>
+    </plurals>
+
     <!-- App Fuel Gauge strings -->
     <skip />
 
@@ -3631,7 +3643,7 @@
     <!-- Suggestion for exploring application info to stop or uninstall -->
     <string name="battery_sugg_apps_info">Stop or uninstall the app</string>
     <!-- [CHAR LIMIT=100] Suggestion for getting apps to consume less power due to GPS-->
-    <string name="battery_sugg_apps_gps">"Manually control GPS to prevent app from using it"</string>
+    <string name="battery_sugg_apps_gps">Select battery-saving mode</string>
     <!-- Suggestion for getting apps to consume less power -->
     <string name="battery_sugg_apps_settings">The app may offer settings to reduce battery use</string>
 
@@ -4768,6 +4780,8 @@
 
     <!-- Home application selection - uninstall button [CHAR LIMIT=80] -->
     <string name="home_app_uninstall_button">Uninstall this application</string>
+    <!-- Message to user that Home Settings will be hidden because there is now only one available home application -->
+    <string name="only_one_home_message">Home settings will be hidden until you install another home application.</string>
 
     <!-- Warning message when changing a global setting for a tablet.[CHAR LIMIT=none] -->
     <string name="global_change_warning" product="tablet">This setting affects all users on this tablet.</string>
diff --git a/src/com/android/settings/HomeSettings.java b/src/com/android/settings/HomeSettings.java
index bac94d6..14e1d9f 100644
--- a/src/com/android/settings/HomeSettings.java
+++ b/src/com/android/settings/HomeSettings.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -50,6 +51,8 @@
 
     public static final String CURRENT_HOME = "current_home";
 
+    public static final String HOME_SHOW_NOTICE = "show";
+
     PreferenceGroup mPrefGroup;
 
     PackageManager mPm;
@@ -57,6 +60,7 @@
     ArrayList<HomeAppPreference> mPrefs;
     HomeAppPreference mCurrentHome = null;
     final IntentFilter mHomeFilter;
+    boolean mShowNotice;
 
     public HomeSettings() {
         mHomeFilter = new IntentFilter(Intent.ACTION_MAIN);
@@ -126,6 +130,17 @@
                 }
             }
         }
+
+        // If we're down to just one possible home app, back out of this settings
+        // fragment and show a dialog explaining to the user that they won't see
+        // 'Home' settings now until such time as there are multiple available.
+        if (mPrefs.size() < 2) {
+            if (mShowNotice) {
+                mShowNotice = false;
+                Settings.requestHomeNotice();
+            }
+            finishFragment();
+        }
     }
 
     void buildHomeActivitiesList() {
@@ -176,6 +191,9 @@
 
         mPm = getPackageManager();
         mPrefGroup = (PreferenceGroup) findPreference("home");
+
+        Bundle args = getArguments();
+        mShowNotice = (args != null) && args.getBoolean(HOME_SHOW_NOTICE, false);
     }
 
     @Override
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 87d34c6..eb98fec 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -19,6 +19,10 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -27,7 +31,6 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -117,6 +120,10 @@
     private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
     private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
 
+    static final int DIALOG_ONLY_ONE_HOME = 1;
+
+    private static boolean sShowNoHomeNotice = false;
+
     private String mFragmentClass;
     private int mTopLevelHeaderId;
     private Header mFirstHeader;
@@ -516,8 +523,10 @@
      */
     @Override
     public void onBuildHeaders(List<Header> headers) {
-        loadHeadersFromResource(R.xml.settings_headers, headers);
-        updateHeaderList(headers);
+        if (!onIsHidingHeaders()) {
+            loadHeadersFromResource(R.xml.settings_headers, headers);
+            updateHeaderList(headers);
+        }
     }
 
     private void updateHeaderList(List<Header> target) {
@@ -655,6 +664,7 @@
                 }
             }
             accountHeaders.add(accHeader);
+            mAuthenticatorHelper.preloadDrawableForType(this, accountType);
         }
 
         // Sort by label
@@ -681,8 +691,22 @@
             getPackageManager().getHomeActivities(homeApps);
             if (homeApps.size() < 2) {
                 // When there's only one available home app, omit this settings
-                // category entirely at the top level UI.
+                // category entirely at the top level UI.  If the user just
+                // uninstalled the penultimate home app candidiate, we also
+                // now tell them about why they aren't seeing 'Home' in the list.
+                if (sShowNoHomeNotice) {
+                    sShowNoHomeNotice = false;
+                    NoHomeDialogFragment.show(this);
+                }
                 return false;
+            } else {
+                // Okay, we're allowing the Home settings category.  Tell it, when
+                // invoked via this front door, that we'll need to be told about the
+                // case when the user uninstalls all but one home app.
+                if (header.fragmentArguments == null) {
+                    header.fragmentArguments = new Bundle();
+                }
+                header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
             }
         } catch (Exception e) {
             // Can't look up the home activity; bail on configuring the icon
@@ -724,6 +748,21 @@
         return super.getNextButton();
     }
 
+    public static class NoHomeDialogFragment extends DialogFragment {
+        public static void show(Activity parent) {
+            final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
+            dialog.show(parent.getFragmentManager(), null);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getActivity())
+                    .setMessage(R.string.only_one_home_message)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
+        }
+    }
+
     private static class HeaderAdapter extends ArrayAdapter<Header> {
         static final int HEADER_TYPE_CATEGORY = 0;
         static final int HEADER_TYPE_NORMAL = 1;
@@ -915,11 +954,6 @@
                             ManageAccountsSettings.KEY_ACCOUNT_TYPE);
                     Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
                     setHeaderIcon(holder, icon);
-                } else if (header.extras != null &&
-                        header.extras.containsKey(HomeSettings.CURRENT_HOME)) {
-                    ActivityInfo ai = header.extras.getParcelable(HomeSettings.CURRENT_HOME);
-                    Drawable icon = ai.loadIcon(getContext().getPackageManager());
-                    setHeaderIcon(holder, icon);
                 } else {
                     holder.icon.setImageResource(header.iconRes);
                 }
@@ -1012,6 +1046,10 @@
         invalidateHeaders();
     }
 
+    public static void requestHomeNotice() {
+        sShowNoHomeNotice = true;
+    }
+
     /*
      * Settings subclasses for launching independently.
      */
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index 756705d..6724cc1 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -247,12 +247,7 @@
 
     private boolean isSmsSupported() {
         // Some tablet has sim card but could not do telephony operations. Skip those.
-        if (mTm.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
-            return false;
-        }
-        int simState = mTm.getSimState();
-        return simState != TelephonyManager.SIM_STATE_ABSENT &&
-                simState != TelephonyManager.SIM_STATE_UNKNOWN;
+        return (mTm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE);
     }
 
     @Override
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 171b1ac..2fbbabd 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -48,10 +48,6 @@
     protected CharSequence mSettingsTitle;
     protected Intent mSettingsIntent;
 
-    // TODO: Showing sub-sub fragment does not handle the activity title
-    // so we do it but this is wrong. Do a real fix when there is time.
-    private CharSequence mOldActivityTitle;
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -100,9 +96,6 @@
     @Override
     public void onDestroyView() {
         getActivity().getActionBar().setCustomView(null);
-        if (mOldActivityTitle != null) {
-            getActivity().getActionBar().setTitle(mOldActivityTitle);
-        }
         mToggleSwitch.setOnBeforeCheckedChangeListener(null);
         super.onDestroyView();
     }
@@ -144,9 +137,8 @@
         // Title.
         PreferenceActivity activity = (PreferenceActivity) getActivity();
         if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) {
-            mOldActivityTitle = getActivity().getTitle();
             String title = arguments.getString(AccessibilitySettings.EXTRA_TITLE);
-            getActivity().getActionBar().setTitle(title);
+            getActivity().setTitle(title);
         }
         // Summary.
         CharSequence summary = arguments.getCharSequence(AccessibilitySettings.EXTRA_SUMMARY);
diff --git a/src/com/android/settings/accounts/AuthenticatorHelper.java b/src/com/android/settings/accounts/AuthenticatorHelper.java
index eba785b..a164b8b 100644
--- a/src/com/android/settings/accounts/AuthenticatorHelper.java
+++ b/src/com/android/settings/accounts/AuthenticatorHelper.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -45,6 +46,16 @@
         return mEnabledAccountTypes.toArray(new String[mEnabledAccountTypes.size()]);
     }
 
+    public void preloadDrawableForType(final Context context, final String accountType) {
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                getDrawableForType(context, accountType);
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+    }
+
     /**
      * Gets an icon associated with a particular account type. If none found, return null.
      * @param accountType the type of account
@@ -52,15 +63,19 @@
      */
     public Drawable getDrawableForType(Context context, final String accountType) {
         Drawable icon = null;
-        if (mAccTypeIconCache.containsKey(accountType)) {
-            return mAccTypeIconCache.get(accountType);
+        synchronized (mAccTypeIconCache) {
+            if (mAccTypeIconCache.containsKey(accountType)) {
+                return mAccTypeIconCache.get(accountType);
+            }
         }
         if (mTypeToAuthDescription.containsKey(accountType)) {
             try {
                 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
                 Context authContext = context.createPackageContext(desc.packageName, 0);
                 icon = authContext.getResources().getDrawable(desc.iconId);
-                mAccTypeIconCache.put(accountType, icon);
+                synchronized (mAccTypeIconCache) {
+                    mAccTypeIconCache.put(accountType, icon);
+                }
             } catch (PackageManager.NameNotFoundException e) {
             } catch (Resources.NotFoundException e) {
             }
diff --git a/src/com/android/settings/print/PrintJobSettingsFragment.java b/src/com/android/settings/print/PrintJobSettingsFragment.java
index b4d517d..c15bc06 100644
--- a/src/com/android/settings/print/PrintJobSettingsFragment.java
+++ b/src/com/android/settings/print/PrintJobSettingsFragment.java
@@ -78,7 +78,7 @@
                 Context.PRINT_SERVICE)).getGlobalPrintManagerForUser(
                         ActivityManager.getCurrentUser());
 
-        getActivity().getActionBar().setTitle(R.string.print_print_job);
+        getActivity().setTitle(R.string.print_print_job);
 
         processArguments();
 
diff --git a/src/com/android/settings/print/PrintServiceSettingsFragment.java b/src/com/android/settings/print/PrintServiceSettingsFragment.java
index 044d86e..c10f4b9 100644
--- a/src/com/android/settings/print/PrintServiceSettingsFragment.java
+++ b/src/com/android/settings/print/PrintServiceSettingsFragment.java
@@ -55,6 +55,7 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.ImageView;
@@ -71,8 +72,6 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
-import android.widget.CompoundButton.OnCheckedChangeListener;
 /**
  * Fragment with print service settings.
  */
@@ -130,14 +129,12 @@
 
     private PrintersAdapter mPrintersAdapter;
 
-    // TODO: Showing sub-sub fragment does not handle the activity title
-    // so we do it but this is wrong. Do a real fix when there is time.
-    private CharSequence mOldActivityTitle;
-
     private int mLastUnfilteredItemCount;
 
     private boolean mServiceEnabled;
 
+    private AnnounceFilterResult mAnnounceFilterResult;
+
     @Override
     public void onResume() {
         super.onResume();
@@ -149,6 +146,9 @@
     @Override
     public void onPause() {
         mSettingsContentObserver.unregister(getContentResolver());
+        if (mAnnounceFilterResult != null) {
+            mAnnounceFilterResult.remove();
+        }
         super.onPause();
     }
 
@@ -162,9 +162,6 @@
     @Override
     public void onDestroyView() {
         getActivity().getActionBar().setCustomView(null);
-        if (mOldActivityTitle != null) {
-            getActivity().getActionBar().setTitle(mOldActivityTitle);
-        }
         mToggleSwitch.setOnBeforeCheckedChangeListener(null);
         super.onDestroyView();
     }
@@ -235,8 +232,7 @@
             if (emptyView == null) {
                 emptyView = getActivity().getLayoutInflater().inflate(
                         R.layout.empty_print_state, contentRoot, false);
-                ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon);
-                iconView.setContentDescription(getString(R.string.print_service_disabled));
+                emptyView.setContentDescription(getString(R.string.print_service_disabled));
                 TextView textView = (TextView) emptyView.findViewById(R.id.message);
                 textView.setText(R.string.print_service_disabled);
                 contentRoot.addView(emptyView);
@@ -262,8 +258,7 @@
             if (emptyView == null) {
                 emptyView = getActivity().getLayoutInflater().inflate(
                         R.layout.empty_print_state, contentRoot, false);
-                ImageView iconView = (ImageView) emptyView.findViewById(R.id.icon);
-                iconView.setContentDescription(getString(R.string.print_no_printers_found));
+                emptyView.setContentDescription(getString(R.string.print_no_printers_found));
                 TextView textView = (TextView) emptyView.findViewById(R.id.message);
                 textView.setText(R.string.print_no_printers_found);
                 contentRoot.addView(emptyView);
@@ -331,9 +326,11 @@
         // Title.
         PreferenceActivity activity = (PreferenceActivity) getActivity();
         if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) {
-            mOldActivityTitle = getActivity().getTitle();
+            // PreferenceActivity allows passing as an extra only title by its
+            // resource id but we do not have the resource id for the print
+            // service label. Therefore, we do it ourselves.
             String title = arguments.getString(PrintSettingsFragment.EXTRA_TITLE);
-            getActivity().getActionBar().setTitle(title);
+            getActivity().setTitle(title);
         }
 
         // Settings title and intent.
@@ -425,6 +422,18 @@
                     return true;
                 }
             });
+            searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    view.announceForAccessibility(getString(
+                            R.string.print_search_box_shown_utterance));
+                }
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                    view.announceForAccessibility(getString(
+                            R.string.print_search_box_hidden_utterance));
+                }
+            });
         } else {
             menu.removeItem(R.id.print_menu_item_search);
         }
@@ -463,6 +472,39 @@
         public abstract void onChange(boolean selfChange, Uri uri);
     }
 
+    private final class AnnounceFilterResult implements Runnable {
+        private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+        public void post() {
+            remove();
+            getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+        }
+
+        public void remove() {
+            getListView().removeCallbacks(this);
+        }
+
+        @Override
+        public void run() {
+            final int count = getListView().getAdapter().getCount();
+            final String text;
+            if (count <= 0) {
+                text = getString(R.string.print_no_printers_found);
+            } else {
+                text = getActivity().getResources().getQuantityString(
+                    R.plurals.print_search_result_count_utterance, count, count);
+            }
+            getListView().announceForAccessibility(text);
+        }
+    }
+
+    private void announceSearchResult() {
+        if (mAnnounceFilterResult == null) {
+            mAnnounceFilterResult = new AnnounceFilterResult();
+        }
+        mAnnounceFilterResult.post();
+    }
+
     private final class PrintersAdapter extends BaseAdapter
             implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>, Filterable {
         private final Object mLock = new Object();
@@ -514,7 +556,9 @@
                 @Override
                 @SuppressWarnings("unchecked")
                 protected void publishResults(CharSequence constraint, FilterResults results) {
+                    final boolean resultCountChanged;
                     synchronized (mLock) {
+                        final int oldPrinterCount = mFilteredPrinters.size();
                         mLastSearchString = constraint;
                         mFilteredPrinters.clear();
                         if (results == null) {
@@ -523,6 +567,10 @@
                             List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
                             mFilteredPrinters.addAll(printers);
                         }
+                        resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
+                    }
+                    if (resultCountChanged) {
+                        announceSearchResult();
                     }
                     notifyDataSetChanged();
                 }