Settings: Add effects suppression text to Ring volume.

If the effects are being muted by a notification listener, display
a "Muted by <x>" msg instead of an active slider inside the
Ring/Notification volume setting pref.

Update volume pref design to optimize for this new custom appearance.

Depends on frameworks/base:
  Icdb5f85eb4bcaa1ead7d77c1460e06ad3f0604d5

Bug: 17461563
Change-Id: I6da871e16009370402451a2ff507de4762644a80
diff --git a/res/drawable/ic_audio_alarm_24dp.xml b/res/drawable/ic_audio_alarm_24dp.xml
deleted file mode 100644
index ddfaa01..0000000
--- a/res/drawable/ic_audio_alarm_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-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_alarm from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_alarm"
-        android:inset="4dp"
-        />
diff --git a/res/drawable/ic_audio_vol_24dp.xml b/res/drawable/ic_audio_vol_24dp.xml
deleted file mode 100644
index ecb3a40..0000000
--- a/res/drawable/ic_audio_vol_24dp.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-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_vol from 32dp to 24dp using insets -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-        android:drawable="@*android:drawable/ic_audio_vol"
-        android:inset="4dp"
-        />
diff --git a/res/drawable/ring_notif.xml b/res/drawable/ring_notif.xml
deleted file mode 100644
index cc9da31..0000000
--- a/res/drawable/ring_notif.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
-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:inset="4dp"
-        />
-
diff --git a/res/drawable/ring_notif_mute.xml b/res/drawable/ring_notif_mute.xml
deleted file mode 100644
index fe9417d..0000000
--- a/res/drawable/ring_notif_mute.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-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:inset="4dp"
-        />
diff --git a/res/drawable/ring_notif_vibrate.xml b/res/drawable/ring_notif_vibrate.xml
deleted file mode 100644
index 37113d7..0000000
--- a/res/drawable/ring_notif_vibrate.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
-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:inset="4dp"
-        />
diff --git a/res/layout/preference_volume_slider.xml b/res/layout/preference_volume_slider.xml
new file mode 100644
index 0000000..878a710
--- /dev/null
+++ b/res/layout/preference_volume_slider.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout 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:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+            android:textColor="?android:attr/textColorPrimary"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="6dp">
+
+            <ImageView
+                android:id="@android:id/icon"
+                android:layout_gravity="center_vertical|start"
+                android:layout_width="24dp"
+                android:layout_height="24dp" />
+
+            <SeekBar android:id="@*android:id/seekbar"
+                android:layout_marginStart="24dp"
+                android:layout_gravity="center_vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <TextView android:id="@+id/suppression_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|start"
+                android:textAlignment="viewStart"
+                android:layout_marginStart="24dp"
+                android:paddingStart="14dp"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAppearance="@android:style/TextAppearance.Material.Body1"
+                android:textColor="?android:attr/textColorSecondary" />
+
+        </FrameLayout>
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index 3f9b40c..e5dda19 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -27,25 +27,25 @@
         <!-- Media volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="media_volume"
-                android:icon="@drawable/ic_audio_vol_24dp"
+                android:icon="@*android:drawable/ic_audio_vol"
                 android:title="@string/media_volume_option_title" />
 
         <!-- Alarm volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="alarm_volume"
-                android:icon="@drawable/ic_audio_alarm_24dp"
+                android:icon="@*android:drawable/ic_audio_alarm"
                 android:title="@string/alarm_volume_option_title" />
 
         <!-- Ring volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="ring_volume"
-                android:icon="@drawable/ring_notif"
+                android:icon="@*android:drawable/ic_audio_ring_notif"
                 android:title="@string/ring_volume_option_title" />
 
         <!-- Notification volume -->
         <com.android.settings.notification.VolumeSeekBarPreference
                 android:key="notification_volume"
-                android:icon="@drawable/ring_notif"
+                android:icon="@*android:drawable/ic_audio_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 6899440..bd08d8f 100644
--- a/src/com/android/settings/notification/NotificationSettings.java
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -16,9 +16,15 @@
 
 package com.android.settings.notification;
 
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
@@ -42,7 +48,6 @@
 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;
@@ -53,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 public class NotificationSettings extends SettingsPreferenceFragment implements Indexable {
     private static final String TAG = "NotificationSettings";
@@ -75,6 +81,7 @@
     private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
     private final H mHandler = new H();
     private final SettingsObserver mSettingsObserver = new SettingsObserver();
+    private final Receiver mReceiver = new Receiver();
 
     private Context mContext;
     private PackageManager mPM;
@@ -90,6 +97,8 @@
     private Preference mNotificationAccess;
     private boolean mSecure;
     private int mLockscreenSelectedValue;
+    private ComponentName mSuppressor;
+    private int mRingOrNotificationProgress;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -128,6 +137,7 @@
 
         mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
         refreshNotificationListeners();
+        updateEffectsSuppressor();
     }
 
     @Override
@@ -136,6 +146,8 @@
         refreshNotificationListeners();
         lookupRingtoneNames();
         mSettingsObserver.register(true);
+        mReceiver.register(true);
+        updateEffectsSuppressor();
     }
 
     @Override
@@ -143,6 +155,7 @@
         super.onPause();
         mVolumeCallback.stopSample();
         mSettingsObserver.register(false);
+        mReceiver.register(false);
     }
 
     // === Volumes ===
@@ -154,12 +167,46 @@
         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 void updateRingOrNotificationIcon() {
+        mRingOrNotificationPreference.showIcon(mSuppressor != null
+                ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+                : mRingOrNotificationProgress > 0
+                        ? com.android.internal.R.drawable.ic_audio_ring_notif
+                        : (mVibrator == null
+                                ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
+                                : com.android.internal.R.drawable.ic_audio_ring_notif_vibrate));
+    }
+
+    private void updateEffectsSuppressor() {
+        final ComponentName suppressor = NotificationManager.from(mContext).getEffectsSuppressor();
+        if (Objects.equals(suppressor, mSuppressor)) return;
+        mSuppressor = suppressor;
+        if (mRingOrNotificationPreference != null) {
+            final String text = suppressor != null ?
+                    mContext.getString(com.android.internal.R.string.muted_by,
+                            getSuppressorCaption(suppressor)) : null;
+            mRingOrNotificationPreference.setSuppressionText(text);
+        }
+        updateRingOrNotificationIcon();
+    }
+
+    private String getSuppressorCaption(ComponentName suppressor) {
+        final PackageManager pm = mContext.getPackageManager();
+        try {
+            final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
+            if (info != null) {
+                final CharSequence seq = info.loadLabel(pm);
+                if (seq != null) {
+                    final String str = seq.toString().trim();
+                    if (str.length() > 0) {
+                        return str;
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            Log.w(TAG, "Error loading suppressor caption", e);
+        }
+        return suppressor.getPackageName();
     }
 
     private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
@@ -464,6 +511,7 @@
         private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
         private static final int STOP_SAMPLE = 3;
         private static final int UPDATE_RINGER_ICON = 4;
+        private static final int UPDATE_EFFECTS_SUPPRESSOR = 5;
 
         private H() {
             super(Looper.getMainLooper());
@@ -482,12 +530,36 @@
                     mVolumeCallback.stopSample();
                     break;
                 case UPDATE_RINGER_ICON:
-                    updateRingOrNotificationIcon(msg.arg1);
+                    mRingOrNotificationProgress = msg.arg1;
+                    updateRingOrNotificationIcon();
+                    break;
+                case UPDATE_EFFECTS_SUPPRESSOR:
+                    updateEffectsSuppressor();
                     break;
             }
         }
     }
 
+    private class Receiver extends BroadcastReceiver {
+        private boolean mRegistered;
+
+        public void register(boolean register) {
+            if (mRegistered == register) return;
+            if (register) {
+                mContext.registerReceiver(this,
+                        new IntentFilter(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+            } else {
+                mContext.unregisterReceiver(this);
+            }
+            mRegistered = register;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
+        }
+    }
+
     // === Indexing ===
 
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java
index f391f86..0fdcc19 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreference.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java
@@ -23,14 +23,18 @@
 import android.preference.PreferenceManager;
 import android.preference.SeekBarPreference;
 import android.preference.SeekBarVolumizer;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.SeekBar;
+import android.widget.TextView;
 
 import com.android.settings.R;
 
+import java.util.Objects;
+
 /** A slider preference that directly controls an audio stream volume (no dialog) **/
 public class VolumeSeekBarPreference extends SeekBarPreference
         implements PreferenceManager.OnActivityStopListener {
@@ -41,10 +45,13 @@
     private SeekBarVolumizer mVolumizer;
     private Callback mCallback;
     private ImageView mIconView;
+    private TextView mSuppressionTextView;
+    private String mSuppressionText;
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        setLayoutResource(R.layout.preference_volume_slider);
     }
 
     public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -108,13 +115,14 @@
         mVolumizer.start();
         mVolumizer.setSeekBar(mSeekBar);
         mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+        mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
         mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
+        updateSuppressionText();
     }
 
     // during initialization, this preference is the SeekBar listener
     @Override
-    public void onProgressChanged(SeekBar seekBar, int progress,
-            boolean fromTouch) {
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
         super.onProgressChanged(seekBar, progress, fromTouch);
         mCallback.onStreamValueChanged(mStream, progress);
     }
@@ -133,6 +141,21 @@
                 + "/" + R.raw.media_volume);
     }
 
+    public void setSuppressionText(String text) {
+        if (Objects.equals(text, mSuppressionText)) return;
+        mSuppressionText = text;
+        updateSuppressionText();
+    }
+
+    private void updateSuppressionText() {
+        if (mSuppressionTextView != null && mSeekBar != null) {
+            mSuppressionTextView.setText(mSuppressionText);
+            final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText);
+            mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.INVISIBLE);
+            mSeekBar.setVisibility(showSuppression ? View.INVISIBLE : View.VISIBLE);
+        }
+    }
+
     public interface Callback {
         void onSampleStarting(SeekBarVolumizer sbv);
         void onStreamValueChanged(int stream, int progress);