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
This commit is contained in:
Julia Reynolds
2018-01-23 13:55:18 -05:00
parent 25ff1cd41f
commit f5e4182d2f
5 changed files with 193 additions and 54 deletions

View File

@@ -15,47 +15,56 @@
limitations under the License.
-->
<!-- extends LinearLayout -->
<com.android.systemui.volume.OutputChooserLayout
<com.android.systemui.HardwareUiLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/output_chooser"
android:minWidth="320dp"
android:minHeight="320dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textDirection="locale"
android:textAppearance="@style/TextAppearance.QS.DetailHeader"
android:layout_marginBottom="20dp" />
<com.android.systemui.qs.AutoSizingList
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="0dp"
android:clipToPadding="false"
android:theme="@style/qs_theme"
android:clipChildren="false">
<com.android.systemui.volume.OutputChooserLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/output_chooser"
android:layout_width="@dimen/output_chooser_panel_width"
android:layout_height="@dimen/output_chooser_panel_width"
android:layout_gravity="center_vertical|end"
android:orientation="vertical"
sysui:itemHeight="@dimen/qs_detail_item_height"
style="@style/AutoSizingList"/>
<LinearLayout
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
android:translationZ="8dp"
android:padding="20dp" >
<TextView
android:id="@+id/empty_text"
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textDirection="locale"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
</LinearLayout>
</com.android.systemui.volume.OutputChooserLayout>
android:textAppearance="@style/TextAppearance.QS.DetailHeader"
android:layout_marginBottom="20dp" />
<com.android.systemui.qs.AutoSizingList
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
sysui:itemHeight="@dimen/qs_detail_item_height"
style="@style/AutoSizingList"/>
<LinearLayout
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textDirection="locale"
android:layout_marginTop="20dp"
android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
</LinearLayout>
</com.android.systemui.volume.OutputChooserLayout>
</com.android.systemui.HardwareUiLayout>

View File

@@ -267,6 +267,8 @@
<dimen name="volume_dialog_panel_width">120dp</dimen>
<dimen name="output_chooser_panel_width">320dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->

View File

@@ -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);
}
}

View File

@@ -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) {}
};
}

View File

@@ -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());
}
}