Merge "Fix build breakage caused by proto conflict"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 117cd14..53dff05 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -59,6 +59,7 @@
             android:gravity="center"
             android:layout_gravity="end"
             android:translationZ="8dp"
+            android:clickable="true"
             android:orientation="vertical" >
 
             <TextView
@@ -76,7 +77,7 @@
                 android:id="@+id/ringer_icon"
                 style="@style/VolumeButtons"
                 android:background="?android:selectableItemBackgroundBorderless"
-                android:layout_width="@dimen/volume_button_size"
+                android:layout_width="@dimen/volume_dialog_panel_width"
                 android:layout_height="@dimen/volume_button_size"
                 android:tint="?android:attr/colorAccent"
                 android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 3e80085..a9e5adf 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -63,6 +63,7 @@
                 android:background="?android:selectableItemBackgroundBorderless"
                 android:contentDescription="@string/accessibility_output_chooser"
                 style="@style/VolumeButtons"
+                android:clickable="false"
                 android:layout_centerVertical="true"
                 android:src="@drawable/ic_swap"
                 android:soundEffectsEnabled="false" />
@@ -70,7 +71,7 @@
     </LinearLayout>
     <FrameLayout
         android:id="@+id/volume_row_slider_frame"
-        android:padding="10dp"
+        android:padding="0dp"
         android:layout_width="@dimen/volume_dialog_panel_width"
         android:layout_height="150dp">
         <SeekBar
@@ -80,8 +81,6 @@
             android:layout_width="150dp"
             android:layout_height="@dimen/volume_dialog_panel_width"
             android:layout_gravity="center"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
             android:rotation="270" />
     </FrameLayout>
 
@@ -91,6 +90,7 @@
         android:padding="10dp"
         android:layout_width="@dimen/volume_button_size"
         android:layout_height="@dimen/volume_button_size"
+        android:background="?android:selectableItemBackgroundBorderless"
         android:soundEffectsEnabled="false" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 001a582..0c6e0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -36,7 +36,6 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -58,7 +57,6 @@
 import android.view.View.AccessibilityDelegate;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
-import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
@@ -105,6 +103,7 @@
     private CustomDialog mDialog;
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
+    private ViewGroup mFooter;
     private ImageButton mRingerIcon;
     private TextView mRingerStatus;
     private final List<VolumeRow> mRows = new ArrayList<>();
@@ -202,8 +201,9 @@
         hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
 
         mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
-        mRingerIcon = mDialog.findViewById(R.id.ringer_icon);
-        mRingerStatus = mDialog.findViewById(R.id.ringer_status);
+        mFooter = mDialog.findViewById(R.id.footer);
+        mRingerIcon = mFooter.findViewById(R.id.ringer_icon);
+        mRingerStatus = mFooter.findViewById(R.id.ringer_status);
 
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
@@ -340,36 +340,8 @@
 
         row.outputChooser = row.view.findViewById(R.id.output_chooser);
         row.outputChooser.setOnClickListener(mClickOutputChooser);
-        row.outputChooser.findViewById(R.id.output_chooser_button)
-                .setOnClickListener(mClickOutputChooser);
         row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device);
 
-        // forward events above the slider into the slider
-        row.view.findViewById(R.id.volume_row_slider_frame)
-                .setOnTouchListener(new OnTouchListener() {
-            private final Rect mSliderHitRect = new Rect();
-            private boolean mDragging;
-
-            @SuppressLint("ClickableViewAccessibility")
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                row.slider.getHitRect(mSliderHitRect);
-                if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN
-                        && event.getY() < mSliderHitRect.top) {
-                    mDragging = true;
-                }
-                if (mDragging) {
-                    event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top);
-                    row.slider.dispatchTouchEvent(event);
-                    if (event.getActionMasked() == MotionEvent.ACTION_UP
-                            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
-                        mDragging = false;
-                    }
-                    return true;
-                }
-                return false;
-            }
-        });
         row.icon = row.view.findViewById(R.id.volume_row_icon);
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
@@ -412,6 +384,8 @@
             if (ss == null) {
                 return;
             }
+            // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
+            // a vibrator.
             final boolean hasVibrator = mController.hasVibrator();
             if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                 if (hasVibrator) {
@@ -419,7 +393,12 @@
                 } else {
                     final boolean wasZero = ss.level == 0;
                     mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+                    mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
                 }
+            } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
+                final boolean wasZero = ss.level == 0;
+                mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
+                mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
             } else {
                 mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
                 if (ss.level == 0) {
@@ -908,7 +887,6 @@
     private final OnClickListener mClickOutputChooser = new OnClickListener() {
         @Override
         public void onClick(View v) {
-            // TODO: log
             dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
             showOutputChooserH();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 368194e..f50a287 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -309,6 +309,12 @@
         return super.getOutlineProvider();
     }
 
+    @Override
+    public void setPressed(boolean pressed)
+    {
+        // Ignore presses because it activates the seekbar thumb unnecessarily.
+    }
+
     public void setOutsideTouchListener(OnClickListener onClickListener) {
         mHasOutsideTouch = true;
         requestLayout();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2d28c9f..4888fb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,12 +16,21 @@
 
 package com.android.systemui.volume;
 
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_RING;
+
 import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
 import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
 
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.app.KeyguardManager;
 import android.media.AudioManager;
 import android.support.test.filters.SmallTest;
@@ -32,8 +41,10 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 
 import org.junit.Before;
@@ -70,13 +81,19 @@
 
         mDialog = new VolumeDialogImpl(getContext());
         mDialog.init(0, null);
-        VolumeDialogController.State state = new VolumeDialogController.State();
+        State state = createShellState();
+        mDialog.onStateChangedH(state);
+    }
+
+    private State createShellState() {
+        State state = new VolumeDialogController.State();
         for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
             VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
             ss.name = STREAMS.get(i);
+            ss.level = 1;
             state.states.append(i, ss);
         }
-        mDialog.onStateChangedH(state);
+        return state;
     }
 
     private void navigateViews(View view, Predicate<View> condition) {
@@ -111,4 +128,94 @@
         mDialog.dismiss(DISMISS_REASON_UNKNOWN);
     }
 
+    @Test
+    public void testNoDuplicationOfParentState() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled());
+
+        mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+    }
+
+    @Test
+    public void testNoClickableViewGroups() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        navigateViews(dialog, view -> {
+            if (view instanceof ViewGroup) {
+                return !view.isClickable();
+            } else {
+                return true;
+            }
+        });
+
+        mDialog.dismiss(DISMISS_REASON_UNKNOWN);
+    }
+
+    @Test
+    public void testTristateToggle_withVibrator() {
+        when(mController.hasVibrator()).thenReturn(true);
+
+        State state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(state);
+
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        // click once, verify updates to vibrate
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_VIBRATE;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to silent
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.states.get(STREAM_RING).level = 0;
+        state.ringerModeInternal = RINGER_MODE_SILENT;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to normal
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+    }
+
+    @Test
+    public void testTristateToggle_withoutVibrator() {
+        when(mController.hasVibrator()).thenReturn(false);
+
+        State state = createShellState();
+        state.ringerModeInternal = RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(state);
+
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        ViewGroup dialog = mDialog.getDialogView();
+
+        // click once, verify updates to silent
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+
+        // fake the update back to the dialog with the new ringer mode
+        state = createShellState();
+        state.states.get(STREAM_RING).level = 0;
+        state.ringerModeInternal = RINGER_MODE_SILENT;
+        mDialog.onStateChangedH(state);
+
+        // click once, verify updates to normal
+        dialog.findViewById(R.id.ringer_icon).performClick();
+        verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
+        verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
+    }
 }