Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45d4ab07 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Improve volume touches

- Change the ringer toggle into a tristate
- Make the tap targets for the output chooser and ringer toggle
larger
- Prevent the slider from capturing extra touches
- Add ripples to the row icon images

Fixes: 72727455
Fixes: 72711039
Fixes: 72627046

Test: runtest systemui
Change-Id: Ie658e9ee813be253dfface827fea86544ef80ed5
parent 45d9f674
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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" />
+3 −3
Original line number Diff line number Diff line
@@ -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
+11 −33
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.content.pm.PackageManager;
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;
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 @@ public class VolumeDialogImpl implements VolumeDialog {
    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 @@ public class VolumeDialogImpl implements VolumeDialog {
        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 @@ public class VolumeDialogImpl implements VolumeDialog {

        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 @@ public class VolumeDialogImpl implements VolumeDialog {
            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 @@ public class VolumeDialogImpl implements VolumeDialog {
                } 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 @@ public class VolumeDialogImpl implements VolumeDialog {
    private final OnClickListener mClickOutputChooser = new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO: log
            dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
            showOutputChooserH();
        }
+6 −0
Original line number Diff line number Diff line
@@ -309,6 +309,12 @@ public class VolumeUiLayout extends FrameLayout {
        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();
+109 −2
Original line number Diff line number Diff line
@@ -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.View;
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 @@ public class VolumeDialogImplTest extends SysuiTestCase {

        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 @@ public class VolumeDialogImplTest extends SysuiTestCase {
        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);
    }
}