Merge "Fixed a Null Pointer Exception when Wifi Settings is indexing." into lmp-dev
diff --git a/res/drawable-ldrtl-hdpi/wifi_assistant_card.png b/res/drawable-ldrtl-hdpi/wifi_assistant_card.png
new file mode 100644
index 0000000..67a93b3
--- /dev/null
+++ b/res/drawable-ldrtl-hdpi/wifi_assistant_card.png
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/wifi_assistant_card.png b/res/drawable-ldrtl-mdpi/wifi_assistant_card.png
new file mode 100644
index 0000000..578454c
--- /dev/null
+++ b/res/drawable-ldrtl-mdpi/wifi_assistant_card.png
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/wifi_assistant_card.png b/res/drawable-ldrtl-xhdpi/wifi_assistant_card.png
new file mode 100644
index 0000000..8840a6a
--- /dev/null
+++ b/res/drawable-ldrtl-xhdpi/wifi_assistant_card.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/wifi_assistant_card.png b/res/drawable-ldrtl-xxhdpi/wifi_assistant_card.png
new file mode 100644
index 0000000..1273b73
--- /dev/null
+++ b/res/drawable-ldrtl-xxhdpi/wifi_assistant_card.png
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/wifi_assistant_card.png b/res/drawable-ldrtl-xxxhdpi/wifi_assistant_card.png
new file mode 100644
index 0000000..7c7bf0a
--- /dev/null
+++ b/res/drawable-ldrtl-xxxhdpi/wifi_assistant_card.png
Binary files differ
diff --git a/res/drawable/ring_notif.xml b/res/drawable/ring_notif.xml
new file mode 100644
index 0000000..414a83d
--- /dev/null
+++ b/res/drawable/ring_notif.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<!-- shrink ic_audio_ring_notif from 32dp to 24dp using insets -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:drawable="@*android:drawable/ic_audio_ring_notif"
+        android:insetLeft="4dp"
+        android:insetRight="4dp"
+        android:insetTop="4dp"
+        android:insetBottom="4dp"
+        />
+
diff --git a/res/drawable/ring_notif_mute.xml b/res/drawable/ring_notif_mute.xml
new file mode 100644
index 0000000..afb835c
--- /dev/null
+++ b/res/drawable/ring_notif_mute.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<!-- shrink ic_audio_ring_notif_mute from 32dp to 24dp using insets -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:drawable="@*android:drawable/ic_audio_ring_notif_mute"
+        android:insetLeft="4dp"
+        android:insetRight="4dp"
+        android:insetTop="4dp"
+        android:insetBottom="4dp"
+        />
diff --git a/res/drawable/ring_notif_vibrate.xml b/res/drawable/ring_notif_vibrate.xml
new file mode 100644
index 0000000..4f19e01
--- /dev/null
+++ b/res/drawable/ring_notif_vibrate.xml
@@ -0,0 +1,23 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<!-- shrink ic_audio_ring_notif_vibrate from 32dp to 24dp using insets -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:drawable="@*android:drawable/ic_audio_ring_notif_vibrate"
+        android:insetLeft="4dp"
+        android:insetRight="4dp"
+        android:insetTop="4dp"
+        android:insetBottom="4dp"
+        />
diff --git a/res/layout/wifi_assistant_card.xml b/res/layout/wifi_assistant_card.xml
index 7c9af17..bbfc3f3 100644
--- a/res/layout/wifi_assistant_card.xml
+++ b/res/layout/wifi_assistant_card.xml
@@ -20,15 +20,11 @@
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:gravity="center_vertical"
-    android:orientation="vertical"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="@dimen/wifi_assistant_padding"
-    android:paddingBottom="@dimen/wifi_assistant_padding">
+    android:orientation="vertical">
 
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
+        android:layout_height="@dimen/wifi_assistant_height">
 
         <ImageView
             android:id="@+id/cardBackground"
@@ -44,6 +40,8 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="@dimen/wifi_assistant_text_padding"
             android:gravity="start"
+            android:paddingStart="@dimen/wifi_assistant_padding_start_end"
+            android:paddingTop="@dimen/wifi_assistant_padding_top_bottom"
             android:text="@string/wifi_assistant_intro_setup"
             style="@style/WifiAssistantText" />
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fcfd5bc..3ec64da 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -170,6 +170,9 @@
     <dimen name="wifi_assistant_padding_start_end">16dp</dimen>
     <dimen name="wifi_assistant_padding">25dp</dimen>
     <dimen name="wifi_assistant_text_padding">16dp</dimen>
+    <dimen name="wifi_assistant_height">182dp</dimen>
+    <dimen name="wifi_assistant_image_top">32dp</dimen>
+    <dimen name="wifi_assistant_image_start">24dp</dimen>
 
     <!-- CryptKeeper top margin for password/pin screen -->
     <dimen name="crypt_keeper_password_top_margin">88dip</dimen>
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index e5dda19..e9a4992 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -39,13 +39,13 @@
         <!-- Ring volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="ring_volume"
-                android:icon="@*android:drawable/ic_audio_ring_notif"
+                android:icon="@drawable/ring_notif"
                 android:title="@string/ring_volume_option_title" />
 
         <!-- Notification volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="notification_volume"
-                android:icon="@*android:drawable/ic_audio_ring_notif"
+                android:icon="@drawable/ring_notif"
                 android:title="@string/notification_volume_option_title" />
 
         <!-- Also vibrate for calls -->
diff --git a/src/com/android/settings/notification/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java
index a523a75..3094032 100644
--- a/src/com/android/settings/notification/NotificationSettings.java
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Vibrator;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.PreferenceCategory;
@@ -40,6 +41,7 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import android.widget.SeekBar;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
@@ -76,6 +78,8 @@
     private Context mContext;
     private PackageManager mPM;
     private boolean mVoiceCapable;
+    private Vibrator mVibrator;
+    private VolumeSeekBarPreference mRingOrNotificationPreference;
 
     private Preference mPhoneRingtonePreference;
     private Preference mNotificationRingtonePreference;
@@ -93,16 +97,24 @@
         mPM = mContext.getPackageManager();
         mVoiceCapable = Utils.isVoiceCapable(mContext);
         mSecure = new LockPatternUtils(getActivity()).isSecure();
+
+        mVibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
+        if (mVibrator != null && !mVibrator.hasVibrator()) {
+            mVibrator = null;
+        }
+
         addPreferencesFromResource(R.xml.notification_settings);
 
         final PreferenceCategory sound = (PreferenceCategory) findPreference(KEY_SOUND);
         initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC);
         initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM);
         if (mVoiceCapable) {
-            initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING);
+            mRingOrNotificationPreference =
+                    initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING);
             sound.removePreference(sound.findPreference(KEY_NOTIFICATION_VOLUME));
         } else {
-            initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION);
+            mRingOrNotificationPreference =
+                    initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION);
             sound.removePreference(sound.findPreference(KEY_RING_VOLUME));
         }
         initRingtones(sound);
@@ -134,10 +146,19 @@
 
     // === Volumes ===
 
-    private void initVolumePreference(String key, int stream) {
+    private VolumeSeekBarPreference initVolumePreference(String key, int stream) {
         final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key);
-        volumePref.setStream(stream);
         volumePref.setCallback(mVolumeCallback);
+        volumePref.setStream(stream);
+        return volumePref;
+    }
+
+    private void updateRingOrNotificationIcon(int progress) {
+        mRingOrNotificationPreference.showIcon(progress > 0
+                    ? R.drawable.ring_notif
+                    : (mVibrator == null
+                            ? R.drawable.ring_notif_mute
+                            : R.drawable.ring_notif_vibrate));
     }
 
     private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
@@ -155,6 +176,14 @@
             }
         }
 
+        @Override
+        public void onStreamValueChanged(int stream, int progress) {
+            if (stream == AudioManager.STREAM_RING) {
+                mHandler.removeMessages(H.UPDATE_RINGER_ICON);
+                mHandler.obtainMessage(H.UPDATE_RINGER_ICON, progress, 0).sendToTarget();
+            }
+        }
+
         public void stopSample() {
             if (mCurrent != null) {
                 mCurrent.stopSample();
@@ -426,6 +455,7 @@
         private static final int UPDATE_PHONE_RINGTONE = 1;
         private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
         private static final int STOP_SAMPLE = 3;
+        private static final int UPDATE_RINGER_ICON = 4;
 
         private H() {
             super(Looper.getMainLooper());
@@ -443,6 +473,9 @@
                 case STOP_SAMPLE:
                     mVolumeCallback.stopSample();
                     break;
+                case UPDATE_RINGER_ICON:
+                    updateRingOrNotificationIcon(msg.arg1);
+                    break;
             }
         }
     }
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index 11a83a7..f94e6a1 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -26,6 +26,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.SeekBar;
 
 import com.android.settings.R;
@@ -39,6 +40,7 @@
     private SeekBar mSeekBar;
     private SeekBarVolumizer mVolumizer;
     private Callback mCallback;
+    private ImageView mIconView;
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
@@ -93,9 +95,35 @@
         };
         final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
         if (mVolumizer == null) {
-            mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
+            mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc) {
+                // we need to piggyback on SBV's SeekBar listener to update our icon
+                @Override
+                public void onProgressChanged(SeekBar seekBar, int progress,
+                        boolean fromTouch) {
+                    super.onProgressChanged(seekBar, progress, fromTouch);
+                    mCallback.onStreamValueChanged(mStream, progress);
+                }
+            };
         }
         mVolumizer.setSeekBar(mSeekBar);
+        mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+        mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
+    }
+
+    // during initialization, this preference is the SeekBar listener
+    @Override
+    public void onProgressChanged(SeekBar seekBar, int progress,
+            boolean fromTouch) {
+        super.onProgressChanged(seekBar, progress, fromTouch);
+        mCallback.onStreamValueChanged(mStream, progress);
+    }
+
+    public void showIcon(int resId) {
+        // Instead of using setIcon, which will trigger listeners, this just decorates the
+        // preference temporarily with a new icon.
+        if (mIconView != null) {
+            mIconView.setImageResource(resId);
+        }
     }
 
     private Uri getMediaVolumeUri() {
@@ -106,5 +134,6 @@
 
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
+        void onStreamValueChanged(int stream, int progress);
     }
 }
diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java
index 65d825b..f5a2fa9 100644
--- a/src/com/android/settings/search/Index.java
+++ b/src/com/android/settings/search/Index.java
@@ -181,6 +181,16 @@
             nonIndexableKeys = new HashMap<String, List<String>>();
         }
 
+        public UpdateData(UpdateData other) {
+            dataToUpdate = new ArrayList<SearchIndexableData>(other.dataToUpdate);
+            dataToDelete = new ArrayList<SearchIndexableData>(other.dataToDelete);
+            nonIndexableKeys = new HashMap<String, List<String>>(other.nonIndexableKeys);
+        }
+
+        public UpdateData copy() {
+            return new UpdateData(this);
+        }
+
         public void clear() {
             dataToUpdate.clear();
             dataToDelete.clear();
@@ -286,7 +296,7 @@
         }
     }
 
-    public boolean update() {
+    public void update() {
         final Intent intent = new Intent(SearchIndexablesContract.PROVIDER_INTERFACE);
         List<ResolveInfo> list =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
@@ -304,7 +314,7 @@
             addNonIndexablesKeysFromRemoteProvider(packageName, authority);
         }
 
-        return updateInternal();
+        updateInternal();
     }
 
     private boolean addIndexablesFromRemoteProvider(String packageName, String authority) {
@@ -443,11 +453,10 @@
         }
     }
 
-    private boolean updateFromRemoteProvider(String packageName, String authority) {
-        if (!addIndexablesFromRemoteProvider(packageName, authority)) {
-            return false;
+    private void updateFromRemoteProvider(String packageName, String authority) {
+        if (addIndexablesFromRemoteProvider(packageName, authority)) {
+            updateInternal();
         }
-        return updateInternal();
     }
 
     /**
@@ -457,9 +466,8 @@
      * @param rebuild true means that you want to delete the data from the Index first.
      * @param includeInSearchResults true means that you want the bit "enabled" set so that the
      *                               data will be seen included into the search results
-     * @return true of the Index update has been successful.
      */
-    public boolean updateFromClassNameResource(String className, boolean rebuild,
+    public void updateFromClassNameResource(String className, boolean rebuild,
             boolean includeInSearchResults) {
         if (className == null) {
             throw new IllegalArgumentException("class name cannot be null!");
@@ -467,7 +475,7 @@
         final SearchIndexableResource res = SearchIndexableResources.getResourceByName(className);
         if (res == null ) {
             Log.e(LOG_TAG, "Cannot find SearchIndexableResources for class name: " + className);
-            return false;
+            return;
         }
         res.context = mContext;
         res.enabled = includeInSearchResults;
@@ -476,15 +484,14 @@
         }
         addIndexableData(res);
         mDataToProcess.forceUpdate = true;
-        boolean result = updateInternal();
+        updateInternal();
         res.enabled = false;
-        return result;
     }
 
-    public boolean updateFromSearchIndexableData(SearchIndexableData data) {
+    public void updateFromSearchIndexableData(SearchIndexableData data) {
         addIndexableData(data);
         mDataToProcess.forceUpdate = true;
-        return updateInternal();
+        updateInternal();
     }
 
     private SQLiteDatabase getReadableDatabase() {
@@ -510,21 +517,12 @@
                 SearchIndexablesContract.NON_INDEXABLES_KEYS_PATH);
     }
 
-    private boolean updateInternal() {
+    private void updateInternal() {
         synchronized (mDataToProcess) {
             final UpdateIndexTask task = new UpdateIndexTask();
-            task.execute(mDataToProcess);
-            try {
-                final boolean result = task.get();
-                mDataToProcess.clear();
-                return result;
-            } catch (InterruptedException e) {
-                Log.e(LOG_TAG, "Cannot update index", e);
-                return false;
-            } catch (ExecutionException e) {
-                Log.e(LOG_TAG, "Cannot update index", e);
-                return false;
-            }
+            UpdateData copy = mDataToProcess.copy();
+            task.execute(copy);
+            mDataToProcess.clear();
         }
     }
 
@@ -1143,7 +1141,7 @@
     /**
      * A private class for updating the Index database
      */
-    private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Boolean> {
+    private class UpdateIndexTask extends AsyncTask<UpdateData, Integer, Void> {
 
         @Override
         protected void onPreExecute() {
@@ -1152,15 +1150,13 @@
         }
 
         @Override
-        protected void onPostExecute(Boolean aBoolean) {
-            super.onPostExecute(aBoolean);
+        protected void onPostExecute(Void aVoid) {
+            super.onPostExecute(aVoid);
             mIsAvailable.set(true);
         }
 
         @Override
-        protected Boolean doInBackground(UpdateData... params) {
-            boolean result = false;
-
+        protected Void doInBackground(UpdateData... params) {
             final List<SearchIndexableData> dataToUpdate = params[0].dataToUpdate;
             final List<SearchIndexableData> dataToDelete = params[0].dataToDelete;
             final Map<String, List<String>> nonIndexableKeys = params[0].nonIndexableKeys;
@@ -1180,11 +1176,11 @@
                             forceUpdate);
                 }
                 database.setTransactionSuccessful();
-                result = true;
             } finally {
                 database.endTransaction();
             }
-            return result;
+
+            return null;
         }
 
         private boolean processDataToUpdate(SQLiteDatabase database, String localeStr,