From: Julia Reynolds Date: Wed, 20 Dec 2017 14:53:59 +0000 (-0500) Subject: Update output chooser for calls X-Git-Tag: android-x86-9.0-r1~256^2~3^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=5daa47270bb591d186e723637af1abebbe1350cc;p=android-x86%2Fframeworks-base.git Update output chooser for calls - Hide media routes while in call - Change the title while in call - Add a title while not in call, too Bug: 63096355 Test: manual Change-Id: Ib7bcd314efb010fe903327dd4d9bdbfe521c1f73 --- diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml index 3d0ab3599bf1..b96f44750a0c 100644 --- a/packages/SystemUI/res/layout/output_chooser.xml +++ b/packages/SystemUI/res/layout/output_chooser.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + + diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml index ed7df4b7f58a..c3ddbbe83dd7 100644 --- a/packages/SystemUI/res/layout/output_chooser_item.xml +++ b/packages/SystemUI/res/layout/output_chooser_item.xml @@ -30,6 +30,7 @@ android:layout_height="@dimen/qs_detail_item_icon_size" android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart" android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd" + android:background="?android:selectableItemBackgroundBorderless" android:tint="?android:attr/textColorPrimary"/> getRoutes() { + return mRouter.getRoutes(); + } +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java index f8843a997d59..e0af9baf0bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 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. @@ -40,6 +40,7 @@ import android.os.SystemClock; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouter; +import android.telecom.TelecomManager; import android.util.Log; import android.util.Pair; @@ -54,7 +55,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -65,15 +65,17 @@ public class OutputChooserDialog extends SystemUIDialog private static final int MAX_DEVICES = 10; private static final long UPDATE_DELAY_MS = 300L; - static final int MSG_UPDATE_ITEMS = 1; + private static final int MSG_UPDATE_ITEMS = 1; private final Context mContext; - private final BluetoothController mController; - private final WifiManager mWifiManager; + private final BluetoothController mBluetoothController; + private WifiManager mWifiManager; private OutputChooserLayout mView; - private final MediaRouter mRouter; + private final MediaRouterWrapper mRouter; private final MediaRouterCallback mRouterCallback; private long mLastUpdateTime; + private boolean mIsInCall; + protected boolean isAttached; private final MediaRouteSelector mRouteSelector; private Drawable mDefaultIcon; @@ -81,12 +83,14 @@ public class OutputChooserDialog extends SystemUIDialog private Drawable mSpeakerIcon; private Drawable mSpeakerGroupIcon; - public OutputChooserDialog(Context context) { + public OutputChooserDialog(Context context, MediaRouterWrapper router) { super(context); mContext = context; - mController = Dependency.get(BluetoothController.class); + mBluetoothController = Dependency.get(BluetoothController.class); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mRouter = MediaRouter.getInstance(context); + TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + mIsInCall = tm.isInCall(); + mRouter = router; mRouterCallback = new MediaRouterCallback(); mRouteSelector = new MediaRouteSelector.Builder() .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) @@ -96,27 +100,38 @@ public class OutputChooserDialog extends SystemUIDialog context.registerReceiver(mReceiver, filter); } + protected void setIsInCall(boolean inCall) { + mIsInCall = inCall; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.output_chooser); setCanceledOnTouchOutside(true); setOnDismissListener(this::onDismiss); - setTitle(R.string.output_title); mView = findViewById(R.id.output_chooser); mView.setCallback(this); + if (mIsInCall) { + mView.setTitle(R.string.output_calls_title); + } else { + mView.setTitle(R.string.output_title); + } + mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast); mTvIcon = mContext.getDrawable(R.drawable.ic_tv); mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker); mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group); final boolean wifiOff = !mWifiManager.isWifiEnabled(); - final boolean btOff = !mController.isBluetoothEnabled(); - if (wifiOff || btOff) { + final boolean btOff = !mBluetoothController.isBluetoothEnabled(); + if (wifiOff && btOff) { mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff)); } + // time out after 5 seconds + mView.postDelayed(() -> updateItems(true), 5000); } protected void cleanUp() {} @@ -131,15 +146,19 @@ public class OutputChooserDialog extends SystemUIDialog public void onAttachedToWindow() { super.onAttachedToWindow(); - mRouter.addCallback(mRouteSelector, mRouterCallback, - MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - mController.addCallback(mCallback); + if (!mIsInCall) { + mRouter.addCallback(mRouteSelector, mRouterCallback, + MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + } + mBluetoothController.addCallback(mCallback); + isAttached = true; } @Override public void onDetachedFromWindow() { + isAttached = false; mRouter.removeCallback(mRouterCallback); - mController.removeCallback(mCallback); + mBluetoothController.removeCallback(mCallback); super.onDetachedFromWindow(); } @@ -154,9 +173,8 @@ public class OutputChooserDialog extends SystemUIDialog if (item == null || item.tag == null) return; if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) { final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag; - if (device != null && device.getMaxConnectionState() - == BluetoothProfile.STATE_DISCONNECTED) { - mController.connect(device); + if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) { + mBluetoothController.connect(device); } } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) { final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag; @@ -171,18 +189,16 @@ public class OutputChooserDialog extends SystemUIDialog if (item == null || item.tag == null) return; if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) { final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag; - if (device != null) { - mController.disconnect(device); - } + mBluetoothController.disconnect(device); } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) { mRouter.unselect(UNSELECT_REASON_DISCONNECTED); } } - private void updateItems() { + private void updateItems(boolean timeout) { if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) { mHandler.removeMessages(MSG_UPDATE_ITEMS); - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS), + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout), mLastUpdateTime + UPDATE_DELAY_MS); return; } @@ -194,14 +210,16 @@ public class OutputChooserDialog extends SystemUIDialog addBluetoothDevices(items); // Add remote displays - addRemoteDisplayRoutes(items); + if (!mIsInCall) { + addRemoteDisplayRoutes(items); + } - Collections.sort(items, ItemComparator.sInstance); + items.sort(ItemComparator.sInstance); - if (items.size() == 0) { + if (items.size() == 0 && timeout) { String emptyMessage = mContext.getString(R.string.output_none_found); final boolean wifiOff = !mWifiManager.isWifiEnabled(); - final boolean btOff = !mController.isBluetoothEnabled(); + final boolean btOff = !mBluetoothController.isBluetoothEnabled(); if (wifiOff || btOff) { emptyMessage = getDisabledServicesMessage(wifiOff, btOff); } @@ -219,12 +237,12 @@ public class OutputChooserDialog extends SystemUIDialog } private void addBluetoothDevices(List items) { - final Collection devices = mController.getDevices(); + final Collection devices = mBluetoothController.getDevices(); if (devices != null) { int connectedDevices = 0; int count = 0; for (CachedBluetoothDevice device : devices) { - if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; + if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; final int majorClass = device.getBtClass().getMajorDeviceClass(); if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) { @@ -328,22 +346,22 @@ public class OutputChooserDialog extends SystemUIDialog private final class MediaRouterCallback extends MediaRouter.Callback { @Override public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { - updateItems(); + updateItems(false); } @Override public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { - dismiss(); + updateItems(false); } } @@ -361,12 +379,12 @@ public class OutputChooserDialog extends SystemUIDialog private final BluetoothController.Callback mCallback = new BluetoothController.Callback() { @Override public void onBluetoothStateChange(boolean enabled) { - updateItems(); + updateItems(false); } @Override public void onBluetoothDevicesChanged() { - updateItems(); + updateItems(false); } }; @@ -393,7 +411,7 @@ public class OutputChooserDialog extends SystemUIDialog public void handleMessage(Message message) { switch (message.what) { case MSG_UPDATE_ITEMS: - updateItems(); + updateItems((Boolean) message.obj); break; } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java index 22ced60006b0..d4c6f897846e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java +++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java @@ -29,8 +29,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.systemui.FontSizeUtils; @@ -40,11 +40,10 @@ import com.android.systemui.qs.AutoSizingList; /** * Limited height list of devices. */ -public class OutputChooserLayout extends FrameLayout { +public class OutputChooserLayout extends LinearLayout { private static final String TAG = "OutputChooserLayout"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final int mQsDetailIconOverlaySize; private final Context mContext; private final H mHandler = new H(); private final Adapter mAdapter = new Adapter(); @@ -55,6 +54,7 @@ public class OutputChooserLayout extends FrameLayout { private AutoSizingList mItemList; private View mEmpty; private TextView mEmptyText; + private TextView mTitle; private Item[] mItems; @@ -62,8 +62,6 @@ public class OutputChooserLayout extends FrameLayout { super(context, attrs); mContext = context; mTag = TAG; - mQsDetailIconOverlaySize = (int) getResources().getDimension( - R.dimen.qs_detail_icon_overlay_size); } @Override @@ -74,7 +72,8 @@ public class OutputChooserLayout extends FrameLayout { mItemList.setAdapter(mAdapter); mEmpty = findViewById(android.R.id.empty); mEmpty.setVisibility(GONE); - mEmptyText = mEmpty.findViewById(android.R.id.title); + mEmptyText = mEmpty.findViewById(R.id.empty_text); + mTitle = findViewById(R.id.title); } @Override @@ -84,17 +83,21 @@ public class OutputChooserLayout extends FrameLayout { int count = mItemList.getChildCount(); for (int i = 0; i < count; i++) { View item = mItemList.getChildAt(i); - FontSizeUtils.updateFontSize(item, android.R.id.title, + FontSizeUtils.updateFontSize(item, R.id.empty_text, R.dimen.qs_detail_item_primary_text_size); FontSizeUtils.updateFontSize(item, android.R.id.summary, R.dimen.qs_detail_item_secondary_text_size); + FontSizeUtils.updateFontSize(item, android.R.id.title, + R.dimen.qs_detail_header_text_size); } } + public void setTitle(int title) { + mTitle.setText(title); + } + public void setEmptyState(String text) { - mEmpty.post(() -> { - mEmptyText.setText(text); - }); + mEmptyText.setText(text); } @Override @@ -176,11 +179,6 @@ public class OutputChooserLayout extends FrameLayout { } else { iv.setImageResource(item.iconResId); } - iv.getOverlay().clear(); - if (item.overlay != null) { - item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize); - iv.getOverlay().add(item.overlay); - } final TextView title = view.findViewById(android.R.id.title); title.setText(item.line1); final TextView summary = view.findViewById(android.R.id.summary); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 9f7c5a7fde22..e76bf572b1a1 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -47,6 +47,7 @@ import android.os.Message; import android.os.SystemClock; import android.provider.Settings; import android.provider.Settings.Global; +import android.support.v7.media.MediaRouter; import android.util.Log; import android.util.Slog; import android.util.SparseBooleanArray; @@ -858,7 +859,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (mOutputChooserDialog != null) { return; } - mOutputChooserDialog = new OutputChooserDialog(mContext) { + mOutputChooserDialog = new OutputChooserDialog(mContext, + new MediaRouterWrapper(MediaRouter.getInstance(mContext))) { @Override protected void cleanUp() { synchronized (mOutputChooserLock) { diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index e74736ab2fcc..859dc2f18dc6 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -46,6 +46,7 @@ + diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index fbcbd2096db7..4c7c1d1608fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -83,7 +83,7 @@ public abstract class SysuiTestCase { return null; } - public Context getContext() { + public SysuiTestableContext getContext() { return mContext; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java new file mode 100644 index 000000000000..537d606365c4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 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. + */ + +package com.android.systemui.volume; + +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothProfile; +import android.net.wifi.WifiManager; +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.support.v7.media.MediaRouter; +import android.telecom.TelecomManager; +import android.widget.TextView; + +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.BluetoothController; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class OutputChooserDialogTest extends SysuiTestCase { + + OutputChooserDialog mDialog; + + @Mock + private BluetoothController mController; + @Mock + private WifiManager mWifiManager; + @Mock + private TelecomManager mTelecomManager; + + @Mock + private MediaRouterWrapper mRouter; + + + @Before + @UiThreadTest + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + mController = mDependency.injectMockDependency(BluetoothController.class); + when(mWifiManager.isWifiEnabled()).thenReturn(true); + + getContext().addMockSystemService(WifiManager.class, mWifiManager); + getContext().addMockSystemService(TelecomManager.class, mTelecomManager); + + 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(); + + OutputChooserLayout.Item item = new OutputChooserLayout.Item(); + item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER; + MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class); + when(info.isEnabled()).thenReturn(true); + item.tag = info; + + mDialog.onDetailItemClick(item); + verify(info, times(1)).select(); + verify(mController, never()).connect(any()); + } + + @Test + @UiThreadTest + public void testClickBtItemConnectsBt() { + showDialog(); + + OutputChooserLayout.Item item = new OutputChooserLayout.Item(); + item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT; + CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class); + when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED); + item.tag = btDevice; + + mDialog.onDetailItemClick(item); + verify(mController, times(1)).connect(any()); + } + + @Test + @UiThreadTest + public void testTitleNotInCall() { + showDialog(); + + assertTrue(((TextView) mDialog.findViewById(R.id.title)) + .getText().toString().contains("Media")); + } + + @Test + @UiThreadTest + public void testTitleInCall() { + mDialog.setIsInCall(true); + showDialog(); + + assertTrue(((TextView) mDialog.findViewById(R.id.title)) + .getText().toString().contains("Phone")); + } + + @Test + @UiThreadTest + public void testNoMediaScanIfInCall() { + mDialog.setIsInCall(true); + mDialog.onAttachedToWindow(); + + verify(mRouter, never()).addCallback(any(), any(), anyInt()); + } + + @Test + @UiThreadTest + public void testMediaScanIfNotInCall() { + mDialog.setIsInCall(false); + mDialog.onAttachedToWindow(); + + verify(mRouter, times(1)).addCallback(any(), any(), anyInt()); + } +}