blob: e3c8503077d82416bb8705ef3c56a06561b399e9 [file] [log] [blame]
Julia Reynoldsfba474a82017-12-13 15:35:54 -05001/*
Julia Reynolds5daa4722017-12-20 09:53:59 -05002 * Copyright (C) 2017 The Android Open Source Project
Julia Reynoldsfba474a82017-12-13 15:35:54 -05003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.volume;
18
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050019import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
20import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
21import static android.support.v7.media.MediaRouter.UNSELECT_REASON_DISCONNECTED;
22
Julia Reynoldsfba474a82017-12-13 15:35:54 -050023import static com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription;
24
Julia Reynoldsf5e41822018-01-23 13:55:18 -050025import android.app.Dialog;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050026import android.bluetooth.BluetoothClass;
27import android.bluetooth.BluetoothDevice;
28import android.bluetooth.BluetoothProfile;
29import android.content.BroadcastReceiver;
30import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
33import android.content.IntentFilter;
Julia Reynoldsf5e41822018-01-23 13:55:18 -050034import android.graphics.Color;
35import android.graphics.drawable.ColorDrawable;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050036import android.graphics.drawable.Drawable;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050037import android.net.Uri;
38import android.net.wifi.WifiManager;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050039import android.os.Bundle;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050040import android.os.Handler;
41import android.os.Message;
42import android.os.SystemClock;
43import android.support.v7.media.MediaControlIntent;
44import android.support.v7.media.MediaRouteSelector;
45import android.support.v7.media.MediaRouter;
Julia Reynolds5daa4722017-12-20 09:53:59 -050046import android.telecom.TelecomManager;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050047import android.util.Log;
48import android.util.Pair;
Julia Reynoldsf5e41822018-01-23 13:55:18 -050049import android.view.Window;
50import android.view.WindowManager;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050051
52import com.android.settingslib.Utils;
53import com.android.settingslib.bluetooth.CachedBluetoothDevice;
54import com.android.systemui.Dependency;
Julia Reynoldsf5e41822018-01-23 13:55:18 -050055import com.android.systemui.HardwareUiLayout;
56import com.android.systemui.Interpolators;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050057import com.android.systemui.R;
Julia Reynoldsf5e41822018-01-23 13:55:18 -050058import com.android.systemui.plugins.VolumeDialogController;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050059import com.android.systemui.statusbar.policy.BluetoothController;
60
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050061import java.io.IOException;
62import java.io.InputStream;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050063import java.util.ArrayList;
64import java.util.Collection;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050065import java.util.Comparator;
66import java.util.List;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050067
Julia Reynoldsf5e41822018-01-23 13:55:18 -050068public class OutputChooserDialog extends Dialog
Julia Reynoldsfba474a82017-12-13 15:35:54 -050069 implements DialogInterface.OnDismissListener, OutputChooserLayout.Callback {
70
71 private static final String TAG = Util.logTag(OutputChooserDialog.class);
72 private static final int MAX_DEVICES = 10;
73
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050074 private static final long UPDATE_DELAY_MS = 300L;
Julia Reynolds5daa4722017-12-20 09:53:59 -050075 private static final int MSG_UPDATE_ITEMS = 1;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050076
Julia Reynoldsfba474a82017-12-13 15:35:54 -050077 private final Context mContext;
Julia Reynolds5daa4722017-12-20 09:53:59 -050078 private final BluetoothController mBluetoothController;
79 private WifiManager mWifiManager;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050080 private OutputChooserLayout mView;
Julia Reynolds5daa4722017-12-20 09:53:59 -050081 private final MediaRouterWrapper mRouter;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050082 private final MediaRouterCallback mRouterCallback;
83 private long mLastUpdateTime;
Julia Reynolds5daa4722017-12-20 09:53:59 -050084 private boolean mIsInCall;
85 protected boolean isAttached;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050086
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050087 private final MediaRouteSelector mRouteSelector;
88 private Drawable mDefaultIcon;
89 private Drawable mTvIcon;
90 private Drawable mSpeakerIcon;
91 private Drawable mSpeakerGroupIcon;
Julia Reynoldsf5e41822018-01-23 13:55:18 -050092 private HardwareUiLayout mHardwareLayout;
93 private final VolumeDialogController mController;
Julia Reynoldsfba474a82017-12-13 15:35:54 -050094
Julia Reynolds5daa4722017-12-20 09:53:59 -050095 public OutputChooserDialog(Context context, MediaRouterWrapper router) {
Julia Reynoldsf5e41822018-01-23 13:55:18 -050096 super(context, com.android.systemui.R.style.qs_theme);
Julia Reynoldsfba474a82017-12-13 15:35:54 -050097 mContext = context;
Julia Reynolds5daa4722017-12-20 09:53:59 -050098 mBluetoothController = Dependency.get(BluetoothController.class);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -050099 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Julia Reynolds5daa4722017-12-20 09:53:59 -0500100 TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
101 mIsInCall = tm.isInCall();
102 mRouter = router;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500103 mRouterCallback = new MediaRouterCallback();
104 mRouteSelector = new MediaRouteSelector.Builder()
105 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
106 .build();
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500107
108 final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
109 context.registerReceiver(mReceiver, filter);
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500110
111 mController = Dependency.get(VolumeDialogController.class);
112
113 // Window initialization
114 Window window = getWindow();
115 window.requestFeature(Window.FEATURE_NO_TITLE);
116 window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
117 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
118 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
119 window.addFlags(
120 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
121 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
122 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
123 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
124 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
125 window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500126 }
127
Julia Reynolds5daa4722017-12-20 09:53:59 -0500128 protected void setIsInCall(boolean inCall) {
129 mIsInCall = inCall;
130 }
131
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500132 @Override
133 protected void onCreate(Bundle savedInstanceState) {
134 super.onCreate(savedInstanceState);
135 setContentView(R.layout.output_chooser);
136 setCanceledOnTouchOutside(true);
137 setOnDismissListener(this::onDismiss);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500138
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500139 mView = findViewById(R.id.output_chooser);
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500140 mHardwareLayout = HardwareUiLayout.get(mView);
141 mHardwareLayout.setOutsideTouchListener(view -> dismiss());
142 mHardwareLayout.setSwapOrientation(false);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500143 mView.setCallback(this);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500144
Julia Reynolds5daa4722017-12-20 09:53:59 -0500145 if (mIsInCall) {
146 mView.setTitle(R.string.output_calls_title);
147 } else {
148 mView.setTitle(R.string.output_title);
149 }
150
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500151 mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
152 mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
153 mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
154 mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
155
156 final boolean wifiOff = !mWifiManager.isWifiEnabled();
Julia Reynolds5daa4722017-12-20 09:53:59 -0500157 final boolean btOff = !mBluetoothController.isBluetoothEnabled();
158 if (wifiOff && btOff) {
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500159 mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
160 }
Julia Reynolds5daa4722017-12-20 09:53:59 -0500161 // time out after 5 seconds
162 mView.postDelayed(() -> updateItems(true), 5000);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500163 }
164
165 protected void cleanUp() {}
166
167
168 @Override
169 protected void onStart() {
170 super.onStart();
171 }
172
173 @Override
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500174 public void onAttachedToWindow() {
175 super.onAttachedToWindow();
176
Julia Reynolds5daa4722017-12-20 09:53:59 -0500177 if (!mIsInCall) {
178 mRouter.addCallback(mRouteSelector, mRouterCallback,
179 MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
180 }
181 mBluetoothController.addCallback(mCallback);
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500182 mController.addCallback(mControllerCallbackH, mHandler);
Julia Reynolds5daa4722017-12-20 09:53:59 -0500183 isAttached = true;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500184 }
185
186 @Override
187 public void onDetachedFromWindow() {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500188 isAttached = false;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500189 mRouter.removeCallback(mRouterCallback);
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500190 mController.removeCallback(mControllerCallbackH);
Julia Reynolds5daa4722017-12-20 09:53:59 -0500191 mBluetoothController.removeCallback(mCallback);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500192 super.onDetachedFromWindow();
193 }
194
195 @Override
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500196 public void onDismiss(DialogInterface unused) {
197 mContext.unregisterReceiver(mReceiver);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500198 cleanUp();
199 }
200
201 @Override
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500202 public void show() {
203 super.show();
204 mHardwareLayout.setTranslationX(getAnimTranslation());
205 mHardwareLayout.setAlpha(0);
206 mHardwareLayout.animate()
207 .alpha(1)
208 .translationX(0)
209 .setDuration(300)
210 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
211 .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
212 .start();
213 }
214
215 @Override
216 public void dismiss() {
217 mHardwareLayout.setTranslationX(0);
218 mHardwareLayout.setAlpha(1);
219 mHardwareLayout.animate()
220 .alpha(0)
221 .translationX(getAnimTranslation())
222 .setDuration(300)
223 .withEndAction(() -> super.dismiss())
224 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
225 .start();
226 }
227
228 private float getAnimTranslation() {
229 return getContext().getResources().getDimension(
230 com.android.systemui.R.dimen.output_chooser_panel_width) / 2;
231 }
232
233 @Override
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500234 public void onDetailItemClick(OutputChooserLayout.Item item) {
235 if (item == null || item.tag == null) return;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500236 if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
237 final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
Julia Reynolds5daa4722017-12-20 09:53:59 -0500238 if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
239 mBluetoothController.connect(device);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500240 }
241 } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
242 final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
243 if (route.isEnabled()) {
244 route.select();
245 }
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500246 }
247 }
248
249 @Override
250 public void onDetailItemDisconnect(OutputChooserLayout.Item item) {
251 if (item == null || item.tag == null) return;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500252 if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
253 final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
Julia Reynolds5daa4722017-12-20 09:53:59 -0500254 mBluetoothController.disconnect(device);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500255 } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
256 mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500257 }
258 }
259
Julia Reynolds5daa4722017-12-20 09:53:59 -0500260 private void updateItems(boolean timeout) {
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500261 if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
262 mHandler.removeMessages(MSG_UPDATE_ITEMS);
Julia Reynolds5daa4722017-12-20 09:53:59 -0500263 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout),
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500264 mLastUpdateTime + UPDATE_DELAY_MS);
265 return;
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500266 }
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500267 mLastUpdateTime = SystemClock.uptimeMillis();
268 if (mView == null) return;
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500269 ArrayList<OutputChooserLayout.Item> items = new ArrayList<>();
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500270
271 // Add bluetooth devices
272 addBluetoothDevices(items);
273
274 // Add remote displays
Julia Reynolds5daa4722017-12-20 09:53:59 -0500275 if (!mIsInCall) {
276 addRemoteDisplayRoutes(items);
277 }
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500278
Julia Reynolds5daa4722017-12-20 09:53:59 -0500279 items.sort(ItemComparator.sInstance);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500280
Julia Reynolds5daa4722017-12-20 09:53:59 -0500281 if (items.size() == 0 && timeout) {
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500282 String emptyMessage = mContext.getString(R.string.output_none_found);
283 final boolean wifiOff = !mWifiManager.isWifiEnabled();
Julia Reynolds5daa4722017-12-20 09:53:59 -0500284 final boolean btOff = !mBluetoothController.isBluetoothEnabled();
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500285 if (wifiOff || btOff) {
286 emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
287 }
288 mView.setEmptyState(emptyMessage);
289 }
290
291 mView.setItems(items.toArray(new OutputChooserLayout.Item[items.size()]));
292 }
293
294 private String getDisabledServicesMessage(boolean wifiOff, boolean btOff) {
295 return mContext.getString(R.string.output_none_found_service_off,
296 wifiOff && btOff ? mContext.getString(R.string.output_service_bt_wifi)
297 : wifiOff ? mContext.getString(R.string.output_service_wifi)
298 : mContext.getString(R.string.output_service_bt));
299 }
300
301 private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500302 final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices();
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500303 if (devices != null) {
304 int connectedDevices = 0;
305 int count = 0;
306 for (CachedBluetoothDevice device : devices) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500307 if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500308 final int majorClass = device.getBtClass().getMajorDeviceClass();
309 if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
310 && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
311 continue;
312 }
313 final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
314 item.iconResId = R.drawable.ic_qs_bluetooth_on;
315 item.line1 = device.getName();
316 item.tag = device;
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500317 item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500318 int state = device.getMaxConnectionState();
319 if (state == BluetoothProfile.STATE_CONNECTED) {
320 item.iconResId = R.drawable.ic_qs_bluetooth_connected;
321 int batteryLevel = device.getBatteryLevel();
322 if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
323 Pair<Drawable, String> pair =
324 getBtClassDrawableWithDescription(getContext(), device);
325 item.icon = pair.first;
326 item.line2 = mContext.getString(
327 R.string.quick_settings_connected_battery_level,
328 Utils.formatPercentage(batteryLevel));
329 } else {
330 item.line2 = mContext.getString(R.string.quick_settings_connected);
331 }
332 item.canDisconnect = true;
333 items.add(connectedDevices, item);
334 connectedDevices++;
335 } else if (state == BluetoothProfile.STATE_CONNECTING) {
336 item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
337 item.line2 = mContext.getString(R.string.quick_settings_connecting);
338 items.add(connectedDevices, item);
339 } else {
340 items.add(item);
341 }
342 if (++count == MAX_DEVICES) {
343 break;
344 }
345 }
346 }
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500347 }
348
349 private void addRemoteDisplayRoutes(List<OutputChooserLayout.Item> items) {
350 List<MediaRouter.RouteInfo> routes = mRouter.getRoutes();
351 for(MediaRouter.RouteInfo route : routes) {
352 if (route.isDefaultOrBluetooth() || !route.isEnabled()
353 || !route.matchesSelector(mRouteSelector)) {
354 continue;
355 }
356 final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
357 item.icon = getIconDrawable(route);
358 item.line1 = route.getName();
359 item.tag = route;
360 item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
361 if (route.getConnectionState() == CONNECTION_STATE_CONNECTING) {
362 mContext.getString(R.string.quick_settings_connecting);
363 } else {
364 item.line2 = route.getDescription();
365 }
366
367 if (route.getConnectionState() == CONNECTION_STATE_CONNECTED) {
368 item.canDisconnect = true;
369 }
370 items.add(item);
371 }
372 }
373
374 private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
375 Uri iconUri = route.getIconUri();
376 if (iconUri != null) {
377 try {
378 InputStream is = getContext().getContentResolver().openInputStream(iconUri);
379 Drawable drawable = Drawable.createFromStream(is, null);
380 if (drawable != null) {
381 return drawable;
382 }
383 } catch (IOException e) {
384 Log.w(TAG, "Failed to load " + iconUri, e);
385 // Falls back.
386 }
387 }
388 return getDefaultIconDrawable(route);
389 }
390
391 private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
392 // If the type of the receiver device is specified, use it.
393 switch (route.getDeviceType()) {
394 case MediaRouter.RouteInfo.DEVICE_TYPE_TV:
395 return mTvIcon;
396 case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
397 return mSpeakerIcon;
398 }
399
400 // Otherwise, make the best guess based on other route information.
401 if (route instanceof MediaRouter.RouteGroup) {
402 // Only speakers can be grouped for now.
403 return mSpeakerGroupIcon;
404 }
405 return mDefaultIcon;
406 }
407
408 private final class MediaRouterCallback extends MediaRouter.Callback {
409 @Override
410 public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500411 updateItems(false);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500412 }
413
414 @Override
415 public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500416 updateItems(false);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500417 }
418
419 @Override
420 public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500421 updateItems(false);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500422 }
423
424 @Override
425 public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500426 updateItems(false);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500427 }
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500428 }
429
430 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
431 @Override
432 public void onReceive(Context context, Intent intent) {
433 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
434 if (D.BUG) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
435 cancel();
436 cleanUp();
437 }
438 }
439 };
440
441 private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
442 @Override
443 public void onBluetoothStateChange(boolean enabled) {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500444 updateItems(false);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500445 }
446
447 @Override
448 public void onBluetoothDevicesChanged() {
Julia Reynolds5daa4722017-12-20 09:53:59 -0500449 updateItems(false);
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500450 }
451 };
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500452
453 static final class ItemComparator implements Comparator<OutputChooserLayout.Item> {
454 public static final ItemComparator sInstance = new ItemComparator();
455
456 @Override
457 public int compare(OutputChooserLayout.Item lhs, OutputChooserLayout.Item rhs) {
458 // Connected item(s) first
459 if (lhs.canDisconnect != rhs.canDisconnect) {
460 return Boolean.compare(rhs.canDisconnect, lhs.canDisconnect);
461 }
462 // Bluetooth items before media routes
463 if (lhs.deviceType != rhs.deviceType) {
464 return Integer.compare(lhs.deviceType, rhs.deviceType);
465 }
466 // then by name
467 return lhs.line1.toString().compareToIgnoreCase(rhs.line1.toString());
468 }
469 }
470
471 private final Handler mHandler = new Handler() {
472 @Override
473 public void handleMessage(Message message) {
474 switch (message.what) {
475 case MSG_UPDATE_ITEMS:
Julia Reynolds5daa4722017-12-20 09:53:59 -0500476 updateItems((Boolean) message.obj);
Julia Reynoldsdd2b96b2017-12-15 10:35:57 -0500477 break;
478 }
479 }
480 };
Julia Reynoldsf5e41822018-01-23 13:55:18 -0500481
482 private final VolumeDialogController.Callbacks mControllerCallbackH
483 = new VolumeDialogController.Callbacks() {
484 @Override
485 public void onShowRequested(int reason) {
486 dismiss();
487 }
488
489 @Override
490 public void onDismissRequested(int reason) {}
491
492 @Override
493 public void onScreenOff() {
494 dismiss();
495 }
496
497 @Override
498 public void onStateChanged(VolumeDialogController.State state) {}
499
500 @Override
501 public void onLayoutDirectionChanged(int layoutDirection) {}
502
503 @Override
504 public void onConfigurationChanged() {}
505
506 @Override
507 public void onShowVibrateHint() {}
508
509 @Override
510 public void onShowSilentHint() {}
511
512 @Override
513 public void onShowSafetyWarning(int flags) {}
514
515 @Override
516 public void onAccessibilityModeChanged(Boolean showA11yStream) {}
517 };
Julia Reynoldsfba474a82017-12-13 15:35:54 -0500518}