From f5e4182d2fdf7cee3a95ed4734b7b2533bdeddc9 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 23 Jan 2018 13:55:18 -0500 Subject: [PATCH] Output chooser layout updates - Anchor output chooser to volume buttons - Dismiss output chooser if volume dialog becomes visible Test: manual, runtest systemui Fixes: 72319160 Bug: 63096355 Change-Id: Ibca7576f287655fb5c98554e67781ec53152ddef --- .../SystemUI/res/layout/output_chooser.xml | 79 +++++++------ packages/SystemUI/res/values/dimens.xml | 2 + .../android/systemui/HardwareUiLayout.java | 13 ++- .../systemui/volume/OutputChooserDialog.java | 105 +++++++++++++++++- .../volume/OutputChooserDialogTest.java | 48 +++++--- 5 files changed, 193 insertions(+), 54 deletions(-) diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml index b96f44750a0cf..b9f7b152ecac7 100644 --- a/packages/SystemUI/res/layout/output_chooser.xml +++ b/packages/SystemUI/res/layout/output_chooser.xml @@ -15,47 +15,56 @@ limitations under the License. --> - - - - - + - - + android:translationZ="8dp" + android:padding="20dp" > - - + android:textAppearance="@style/TextAppearance.QS.DetailHeader" + android:layout_marginBottom="20dp" /> + + + + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b749a18a8c680..a90106d7131ae 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -267,6 +267,8 @@ 120dp + 320dp + 0x31 diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index ca34345d923b8..9481788020033 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -58,6 +58,7 @@ public class HardwareUiLayout extends FrameLayout implements Tunable { private boolean mRoundedDivider; private int mRotation = ROTATION_NONE; private boolean mRotatedBackground; + private boolean mSwapOrientation = true; public HardwareUiLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -145,6 +146,10 @@ public class HardwareUiLayout extends FrameLayout implements Tunable { updateRotation(); } + public void setSwapOrientation(boolean swapOrientation) { + mSwapOrientation = swapOrientation; + } + private void updateRotation() { int rotation = RotationUtils.getRotation(getContext()); if (rotation != mRotation) { @@ -173,7 +178,9 @@ public class HardwareUiLayout extends FrameLayout implements Tunable { if (to == ROTATION_SEASCAPE) { swapOrder(linearLayout); } - linearLayout.setOrientation(LinearLayout.HORIZONTAL); + if (mSwapOrientation) { + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + } swapDimens(this.mChild); } } else { @@ -184,7 +191,9 @@ public class HardwareUiLayout extends FrameLayout implements Tunable { if (from == ROTATION_SEASCAPE) { swapOrder(linearLayout); } - linearLayout.setOrientation(LinearLayout.VERTICAL); + if (mSwapOrientation) { + linearLayout.setOrientation(LinearLayout.VERTICAL); + } swapDimens(mChild); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java index e0af9baf0bb0e..e3c8503077d82 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java @@ -22,6 +22,7 @@ import static android.support.v7.media.MediaRouter.UNSELECT_REASON_DISCONNECTED; import static com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription; +import android.app.Dialog; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; @@ -30,6 +31,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.wifi.WifiManager; @@ -43,12 +46,16 @@ import android.support.v7.media.MediaRouter; import android.telecom.TelecomManager; import android.util.Log; import android.util.Pair; +import android.view.Window; +import android.view.WindowManager; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.systemui.Dependency; +import com.android.systemui.HardwareUiLayout; +import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.statusbar.policy.BluetoothController; import java.io.IOException; @@ -58,7 +65,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; -public class OutputChooserDialog extends SystemUIDialog +public class OutputChooserDialog extends Dialog implements DialogInterface.OnDismissListener, OutputChooserLayout.Callback { private static final String TAG = Util.logTag(OutputChooserDialog.class); @@ -82,9 +89,11 @@ public class OutputChooserDialog extends SystemUIDialog private Drawable mTvIcon; private Drawable mSpeakerIcon; private Drawable mSpeakerGroupIcon; + private HardwareUiLayout mHardwareLayout; + private final VolumeDialogController mController; public OutputChooserDialog(Context context, MediaRouterWrapper router) { - super(context); + super(context, com.android.systemui.R.style.qs_theme); mContext = context; mBluetoothController = Dependency.get(BluetoothController.class); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); @@ -98,6 +107,22 @@ public class OutputChooserDialog extends SystemUIDialog final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); context.registerReceiver(mReceiver, filter); + + mController = Dependency.get(VolumeDialogController.class); + + // Window initialization + Window window = getWindow(); + window.requestFeature(Window.FEATURE_NO_TITLE); + window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); + window.addFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); } protected void setIsInCall(boolean inCall) { @@ -112,6 +137,9 @@ public class OutputChooserDialog extends SystemUIDialog setOnDismissListener(this::onDismiss); mView = findViewById(R.id.output_chooser); + mHardwareLayout = HardwareUiLayout.get(mView); + mHardwareLayout.setOutsideTouchListener(view -> dismiss()); + mHardwareLayout.setSwapOrientation(false); mView.setCallback(this); if (mIsInCall) { @@ -151,6 +179,7 @@ public class OutputChooserDialog extends SystemUIDialog MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); } mBluetoothController.addCallback(mCallback); + mController.addCallback(mControllerCallbackH, mHandler); isAttached = true; } @@ -158,6 +187,7 @@ public class OutputChooserDialog extends SystemUIDialog public void onDetachedFromWindow() { isAttached = false; mRouter.removeCallback(mRouterCallback); + mController.removeCallback(mControllerCallbackH); mBluetoothController.removeCallback(mCallback); super.onDetachedFromWindow(); } @@ -168,6 +198,38 @@ public class OutputChooserDialog extends SystemUIDialog cleanUp(); } + @Override + public void show() { + super.show(); + mHardwareLayout.setTranslationX(getAnimTranslation()); + mHardwareLayout.setAlpha(0); + mHardwareLayout.animate() + .alpha(1) + .translationX(0) + .setDuration(300) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus()) + .start(); + } + + @Override + public void dismiss() { + mHardwareLayout.setTranslationX(0); + mHardwareLayout.setAlpha(1); + mHardwareLayout.animate() + .alpha(0) + .translationX(getAnimTranslation()) + .setDuration(300) + .withEndAction(() -> super.dismiss()) + .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) + .start(); + } + + private float getAnimTranslation() { + return getContext().getResources().getDimension( + com.android.systemui.R.dimen.output_chooser_panel_width) / 2; + } + @Override public void onDetailItemClick(OutputChooserLayout.Item item) { if (item == null || item.tag == null) return; @@ -416,4 +478,41 @@ public class OutputChooserDialog extends SystemUIDialog } } }; + + private final VolumeDialogController.Callbacks mControllerCallbackH + = new VolumeDialogController.Callbacks() { + @Override + public void onShowRequested(int reason) { + dismiss(); + } + + @Override + public void onDismissRequested(int reason) {} + + @Override + public void onScreenOff() { + dismiss(); + } + + @Override + public void onStateChanged(VolumeDialogController.State state) {} + + @Override + public void onLayoutDirectionChanged(int layoutDirection) {} + + @Override + public void onConfigurationChanged() {} + + @Override + public void onShowVibrateHint() {} + + @Override + public void onShowSilentHint() {} + + @Override + public void onShowSafetyWarning(int flags) {} + + @Override + public void onAccessibilityModeChanged(Boolean showA11yStream) {} + }; } \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java index 537d606365c42..de99d71351c72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -38,6 +38,7 @@ import android.widget.TextView; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.statusbar.policy.BluetoothController; import org.junit.After; @@ -53,6 +54,8 @@ public class OutputChooserDialogTest extends SysuiTestCase { OutputChooserDialog mDialog; + @Mock + private VolumeDialogController mVolumeController; @Mock private BluetoothController mController; @Mock @@ -69,6 +72,7 @@ public class OutputChooserDialogTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); + mVolumeController = mDependency.injectMockDependency(VolumeDialogController.class); mController = mDependency.injectMockDependency(BluetoothController.class); when(mWifiManager.isWifiEnabled()).thenReturn(true); @@ -78,20 +82,10 @@ public class OutputChooserDialogTest extends SysuiTestCase { mDialog = new OutputChooserDialog(getContext(), mRouter); } - @After - @UiThreadTest - public void tearDown() throws Exception { - mDialog.dismiss(); - } - - private void showDialog() { - mDialog.show(); - } - @Test @UiThreadTest public void testClickMediaRouterItemConnectsMedia() { - showDialog(); + mDialog.show(); OutputChooserLayout.Item item = new OutputChooserLayout.Item(); item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER; @@ -102,12 +96,13 @@ public class OutputChooserDialogTest extends SysuiTestCase { mDialog.onDetailItemClick(item); verify(info, times(1)).select(); verify(mController, never()).connect(any()); + mDialog.dismiss(); } @Test @UiThreadTest public void testClickBtItemConnectsBt() { - showDialog(); + mDialog.show(); OutputChooserLayout.Item item = new OutputChooserLayout.Item(); item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT; @@ -117,25 +112,28 @@ public class OutputChooserDialogTest extends SysuiTestCase { mDialog.onDetailItemClick(item); verify(mController, times(1)).connect(any()); + mDialog.dismiss(); } @Test @UiThreadTest public void testTitleNotInCall() { - showDialog(); + mDialog.show(); assertTrue(((TextView) mDialog.findViewById(R.id.title)) .getText().toString().contains("Media")); + mDialog.dismiss(); } @Test @UiThreadTest public void testTitleInCall() { mDialog.setIsInCall(true); - showDialog(); + mDialog.show(); assertTrue(((TextView) mDialog.findViewById(R.id.title)) .getText().toString().contains("Phone")); + mDialog.dismiss(); } @Test @@ -155,4 +153,26 @@ public class OutputChooserDialogTest extends SysuiTestCase { verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); } + + @Test + @UiThreadTest + public void testRegisterCallbacks() { + mDialog.setIsInCall(false); + mDialog.onAttachedToWindow(); + + verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); + verify(mController, times(1)).addCallback(any()); + verify(mVolumeController, times(1)).addCallback(any(), any()); + } + + @Test + @UiThreadTest + public void testUnregisterCallbacks() { + mDialog.setIsInCall(false); + mDialog.onDetachedFromWindow(); + + verify(mRouter, times(1)).removeCallback(any()); + verify(mController, times(1)).removeCallback(any()); + verify(mVolumeController, times(1)).removeCallback(any()); + } }