Merge "Bug fixes, UI flow improvements."
diff --git a/res/layout/custom_preference.xml b/res/layout/custom_preference.xml
new file mode 100644
index 0000000..cec8eba
--- /dev/null
+++ b/res/layout/custom_preference.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- Custom layout almost same as original preference.xml in framework.
+     One (current) only difference is this layout doesn't have a spacer
+     on the left side of the preference. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical"
+    android:paddingRight="?android:attr/scrollbarSize">
+
+    <!-- <View
+        android:layout_width="@dimen/preference_widget_width"
+        android:layout_height="match_parent" /> -->
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginRight="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <TextView android:id="@+android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignLeft="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="4" />
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@+android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml
index 16222d3..ec61310 100644
--- a/res/layout/master_clear.xml
+++ b/res/layout/master_clear.xml
@@ -35,6 +35,22 @@
                 android:layout_height="wrap_content"
                 android:textSize="18sp"
                 android:text="@string/master_clear_desc" />
+            <TextView android:id="@+id/accounts_label"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:textSize="18sp"
+                android:text="@string/master_clear_accounts" />
+            <LinearLayout android:id="@+id/accounts"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical"
+                    android:visibility="gone" />
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"
+                android:text="@string/master_clear_desc_erase_external_storage" />
             <LinearLayout android:id="@+id/erase_external_container"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
diff --git a/res/layout/master_clear_account.xml b/res/layout/master_clear_account.xml
new file mode 100644
index 0000000..4459c47
--- /dev/null
+++ b/res/layout/master_clear_account.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android" 
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textSize="18sp"
+    android:drawablePadding="5dip"
+    android:gravity="center_vertical" />
+
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 8967ff2..36ab5bc 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -318,6 +318,28 @@
         <item>2</item>
     </string-array>
 
+    <!-- Wi-Fi settings. Presented as a list dialog to the user to choose the Wi-Fi frequency band. -->
+    <string-array name="wifi_frequency_band_entries">
+        <!-- Operation on both 2.4 GHz and 5 GHz [CHAR LIMIT=25]-->
+        <item>Auto</item>
+        <!-- Operation on 5 GHz alone [CHAR LIMIT=25]-->
+        <item>5 GHz only</item>
+        <!-- Operation on 2.4 GHz alone [CHAR LIMIT=25]-->
+        <item>2.4 GHz only</item>
+    </string-array>
+
+    <!-- Match with wifi_frequency_band_entries and the values of the settings in WifiManager. -->
+    <!-- Do not translate. -->
+    <string-array name="wifi_frequency_band_values">
+        <!-- Do not translate. -->
+        <item>0</item>
+        <!-- Do not translate. -->
+        <item>1</item>
+        <!-- Do not translate. -->
+        <item>2</item>
+    </string-array>
+
+
     <!-- Display options for UsageStats class -->
     <string-array name="usage_stats_display_order_types">
         <item>Usage Time</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a50604a..cfaa980 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1025,6 +1025,12 @@
     <string name="wifi_setting_sleep_policy_summary">Specify when to switch from Wi-Fi to mobile data</string>
     <!-- Wi-Fi settings screen, generic error message when the sleep policy could not be set. -->
     <string name="wifi_setting_sleep_policy_error">There was a problem setting the sleep policy.</string>
+    <!-- Wi-Fi settings screen, setting title for setting the band [CHAR LIMIT=50]-->
+    <string name="wifi_setting_frequency_band_title">Wi-Fi frequency band</string>
+    <!-- Wi-Fi settings screen, setting summary for setting the wifi frequency band [CHAR LIMIT=50]-->
+    <string name="wifi_setting_frequency_band_summary">Specify the frequency range of operation</string>
+    <!-- Wi-Fi settings screen, error message when the frequency band could not be set [CHAR LIMIT=50]. -->
+    <string name="wifi_setting_frequency_band_error">There was a problem setting the frequency band.</string>
     <!-- Wi-Fi settings screen, advanced, title of the item to show the Wi-Fi device's MAC address. -->
     <string name="wifi_advanced_mac_address_title">MAC address</string>
     <!-- Title of the screen to adjust IP settings -->
@@ -1591,11 +1597,14 @@
     <!-- SD card & phone storage settings screen, setting option summary text under Internal phone storage heading -->
     <string name="master_clear_summary" product="default">Erases all data on phone</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
-    <string name="master_clear_desc" product="tablet">"This will erase all data from your tablet\'s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and application data and settings</li>\n<li>Downloaded applications</li>\n\nTo clear all data on this tablet the <b>USB storage</b> needs to be erased.\n\n"</string>
+    <string name="master_clear_desc" product="tablet">"This will erase all data from your tablet\'s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and application data and settings</li>\n<li>Downloaded applications</li>"</string>
     <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset [CHAR LIMIT=NONE] -->
-    <string name="master_clear_desc" product="nosdcard">"This will erase all data from your phone\'s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and application data and settings</li>\n<li>Downloaded applications</li>\n\nTo clear all data on this phone the <b>USB storage</b> needs to be erased.\n\n"</string>
-    <!-- SD card & phone storage settings screen, message on screen after user selects Factory data reset -->
-    <string name="master_clear_desc" product="default">"This will erase all data from your phone\'s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and application data and settings</li>\n<li>Downloaded applications</li>\n\nTo also clear music, pictures, and other user data, the <b>SD card</b> needs to be erased.\n\n"</string>
+    <string name="master_clear_desc" product="default">"This will erase all data from your phone\'s <b>internal storage</b>, including:\n\n<li>Your Google account</li>\n<li>System and application data and settings</li>\n<li>Downloaded applications"</li></string>
+    <!-- SD card & phone storage settings screen, instructions and list of current accounts.  The list of accounts follows this text[CHAR LIMIT=NONE] -->
+    <string name="master_clear_accounts" product="default">"\n\nYou are currently signed into the following accounts:\n"</string>
+    <!-- SD card & phone storage settings screen, instructions about whether to also erase the external storage (SD card) when erasing the internal storage [CHAR LIMIT=NONE] -->
+    <string name="master_clear_desc_erase_external_storage" product="nosdcard">"\n\nTo also clear music, pictures, and other user data, the <b>USB storage</b> needs to be erased."</string>
+    <string name="master_clear_desc_erase_external_storage" product="default">"\n\nTo also clear music, pictures, and other user data, the <b>SD card</b> needs to be erased."</string>
     <!-- SD card & phone storage settings screen, label for check box to erase USB storage [CHAR LIMIT=30] -->
     <string name="erase_external_storage" product="nosdcard">Erase USB storage</string>
     <!-- SD card & phone storage settings screen, label for check box to erase SD card [CHAR LIMIT=30] -->
diff --git a/res/xml/wifi_advanced_settings.xml b/res/xml/wifi_advanced_settings.xml
index 7ccd588..8c545de 100644
--- a/res/xml/wifi_advanced_settings.xml
+++ b/res/xml/wifi_advanced_settings.xml
@@ -25,6 +25,15 @@
             android:entries="@array/wifi_sleep_policy_entries"
             android:entryValues="@array/wifi_sleep_policy_values"
             />
+
+    <ListPreference
+            android:key="frequency_band"
+            android:title="@string/wifi_setting_frequency_band_title"
+            android:summary="@string/wifi_setting_frequency_band_summary"
+            android:persistent="false"
+            android:entries="@array/wifi_frequency_band_entries"
+            android:entryValues="@array/wifi_frequency_band_values"
+            />
     
     <Preference android:key="mac_address" 
         style="?android:attr/preferenceInformationStyle" 
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index a2ef7f4..3060d3e 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -18,18 +18,27 @@
 
 import com.android.settings.R;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
 import android.app.Activity;
 import android.app.Fragment;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 /**
  * Confirm and execute a reset of the device to a clean "just out of the box"
@@ -42,6 +51,7 @@
  * This is the initial screen.
  */
 public class MasterClear extends Fragment {
+    private static final String TAG = "MasterClear";
 
     private static final int KEYGUARD_REQUEST = 55;
 
@@ -130,12 +140,72 @@
                 mExternalStorage.toggle();
             }
         });
+
+        loadAccountList();
+    }
+
+    private void loadAccountList() {
+        View accountsLabel = mContentView.findViewById(R.id.accounts_label);
+        LinearLayout contents = (LinearLayout)mContentView.findViewById(R.id.accounts);
+
+        Context context = getActivity();
+
+        AccountManager mgr = AccountManager.get(context);
+        Account[] accounts = mgr.getAccounts();
+        final int N = accounts.length;
+        if (N == 0) {
+            accountsLabel.setVisibility(View.GONE);
+            contents.setVisibility(View.GONE);
+            return;
+        }
+
+        LayoutInflater inflater = (LayoutInflater)context.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        AuthenticatorDescription[] descs = AccountManager.get(context).getAuthenticatorTypes();
+        final int M = descs.length;
+
+        for (int i=0; i<N; i++) {
+            Account account = accounts[i];
+            AuthenticatorDescription desc = null;
+            for (int j=0; j<M; j++) {
+                if (account.type.equals(descs[j].type)) {
+                    desc = descs[j];
+                    break;
+                }
+            }
+            if (desc == null) {
+                Log.w(TAG, "No descriptor for account name=" + account.name
+                        + " type=" + account.type);
+                continue;
+            }
+            Drawable icon;
+            try {
+                Context authContext = context.createPackageContext(desc.packageName, 0);
+                icon = authContext.getResources().getDrawable(desc.iconId);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "No icon for account type " + desc.type);
+                icon = null;
+            }
+
+            TextView child = (TextView)inflater.inflate(R.layout.master_clear_account,
+                    contents, false);
+            child.setText(account.name);
+            if (icon != null) {
+                child.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
+            }
+            contents.addView(child);
+        }
+
+        accountsLabel.setVisibility(View.VISIBLE);
+        contents.setVisibility(View.VISIBLE);
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         mContentView = inflater.inflate(R.layout.master_clear, null);
+
         establishInitialState();
         return mContentView;
     }
diff --git a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
index 947eaf6..c8787a2 100644
--- a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
+++ b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
@@ -32,8 +32,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.os.Handler;
 import android.util.Log;
 
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
 /**
  * BluetoothEventRedirector receives broadcasts and callbacks from the Bluetooth
  * API and dispatches the event on the UI thread to the right class in the
@@ -42,9 +47,14 @@
 public class BluetoothEventRedirector {
     private static final String TAG = "BluetoothEventRedirector";
 
-    private LocalBluetoothManager mManager;
+    private final LocalBluetoothManager mManager;
 
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    private final ThreadPoolExecutor mSerialExecutor = new ThreadPoolExecutor(
+        0, 1, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
+
+    private final Handler mHandler = new Handler();
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.v(TAG, "Received " + intent.getAction());
@@ -57,13 +67,11 @@
                                         BluetoothAdapter.ERROR);
                 mManager.setBluetoothStateInt(state);
             } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
-                persistDiscoveringTimestamp();
-                mManager.onScanningStateChanged(true);
-
+                PendingResult pr = goAsync();  // so loading shared prefs doesn't kill animation
+                persistDiscoveringTimestamp(pr, true);
             } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
-                persistDiscoveringTimestamp();
-                mManager.onScanningStateChanged(false);
-
+                PendingResult pr = goAsync();  // so loading shared prefs doesn't kill animation
+                persistDiscoveringTimestamp(pr, false);
             } else if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                 short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
                 BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
@@ -222,10 +230,26 @@
         return null;
     }
 
-    private void persistDiscoveringTimestamp() {
-        SharedPreferences.Editor editor = mManager.getSharedPreferences().edit();
-        editor.putLong(LocalBluetoothManager.SHARED_PREFERENCES_KEY_DISCOVERING_TIMESTAMP,
-                System.currentTimeMillis());
-        editor.apply();
+    private void persistDiscoveringTimestamp(
+        final BroadcastReceiver.PendingResult pr, final boolean newState) {
+        // Load the shared preferences and edit it on a background
+        // thread (but serialized!), but then post back to the main
+        // thread to run the onScanningStateChanged callbacks which
+        // update the UI...
+        mSerialExecutor.submit(new Runnable() {
+                public void run() {
+                    SharedPreferences.Editor editor = mManager.getSharedPreferences().edit();
+                    editor.putLong(
+                        LocalBluetoothManager.SHARED_PREFERENCES_KEY_DISCOVERING_TIMESTAMP,
+                        System.currentTimeMillis());
+                    editor.apply();
+                    mHandler.post(new Runnable() {
+                            public void run() {
+                                mManager.onScanningStateChanged(newState);
+                                pr.finish();
+                            }
+                        });
+                }
+            });
     }
 }
diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java
index 0d33a10..1968eb0 100644
--- a/src/com/android/settings/wifi/AdvancedSettings.java
+++ b/src/com/android/settings/wifi/AdvancedSettings.java
@@ -20,6 +20,7 @@
 import com.android.settings.SettingsPreferenceFragment;
 
 import android.app.Activity;
+import android.content.Context;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
@@ -29,13 +30,18 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.widget.Toast;
+import android.util.Log;
 
 public class AdvancedSettings extends SettingsPreferenceFragment
         implements Preference.OnPreferenceChangeListener {
 
+    private static final String TAG = "AdvancedSettings";
     private static final String KEY_MAC_ADDRESS = "mac_address";
     private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address";
     private static final String KEY_SLEEP_POLICY = "sleep_policy";
+    private static final String KEY_FREQUENCY_BAND = "frequency_band";
+
+    private WifiManager mWifiManager;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -46,22 +52,36 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
+        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
     }
 
     @Override
     public void onResume() {
         super.onResume();
-
-        initSleepPolicyPreference();
+        initPreferences();
         refreshWifiInfo();
     }
 
-    private void initSleepPolicyPreference() {
+    private void initPreferences() {
         ListPreference pref = (ListPreference) findPreference(KEY_SLEEP_POLICY);
         pref.setOnPreferenceChangeListener(this);
         int value = Settings.System.getInt(getContentResolver(),
-                Settings.System.WIFI_SLEEP_POLICY,Settings. System.WIFI_SLEEP_POLICY_DEFAULT);
+                Settings.System.WIFI_SLEEP_POLICY, Settings.System.WIFI_SLEEP_POLICY_DEFAULT);
         pref.setValue(String.valueOf(value));
+
+        pref = (ListPreference) findPreference(KEY_FREQUENCY_BAND);
+
+        if (mWifiManager.isDualBandSupported()) {
+            pref.setOnPreferenceChangeListener(this);
+            value = mWifiManager.getFrequencyBand();
+            if (value != -1) {
+                pref.setValue(String.valueOf(value));
+            } else {
+                Log.e(TAG, "Failed to fetch frequency band");
+            }
+        } else {
+            getPreferenceScreen().removePreference(pref);
+        }
     }
 
     public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -77,14 +97,21 @@
                         Toast.LENGTH_SHORT).show();
                 return false;
             }
+        } else if (key.equals(KEY_FREQUENCY_BAND)) {
+            try {
+                mWifiManager.setFrequencyBand(Integer.parseInt(((String) newValue)), true);
+            } catch (NumberFormatException e) {
+                Toast.makeText(getActivity(), R.string.wifi_setting_frequency_band_error,
+                        Toast.LENGTH_SHORT).show();
+                return false;
+            }
         }
 
         return true;
     }
 
     private void refreshWifiInfo() {
-        WifiManager wifiManager = (WifiManager) getSystemService(Activity.WIFI_SERVICE);
-        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
 
         Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS);
         String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index bc1f09d..eea03c7 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -463,6 +463,7 @@
                 //limit access points on set up wizard
                 int count = MAX_MENU_COUNT_IN_XL;
                 for (AccessPoint accessPoint : accessPoints) {
+                    accessPoint.setLayoutResource(R.layout.custom_preference);
                     mAccessPoints.addPreference(accessPoint);
                     count--;
                     if (count <= 0) {