1692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim/* 2692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Copyright 2018 The Android Open Source Project 3692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 4692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License"); 5692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * you may not use this file except in compliance with the License. 6692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * You may obtain a copy of the License at 7692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 8692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * http://www.apache.org/licenses/LICENSE-2.0 9692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 10692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Unless required by applicable law or agreed to in writing, software 11692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS, 12692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * See the License for the specific language governing permissions and 14692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * limitations under the License. 15692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 16692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 17692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limpackage com.android.support.mediarouter.app; 18692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 19692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED; 20692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport static com.android.support.mediarouter.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING; 21692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 22692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.annotation.NonNull; 230bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.app.Dialog; 24692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.content.Context; 250bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.content.res.Resources; 26692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.content.res.TypedArray; 27692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.graphics.drawable.Drawable; 28692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.net.Uri; 29692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Bundle; 30692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Handler; 31692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.Message; 32692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.os.SystemClock; 33692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.support.v7.app.AppCompatDialog; 34692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.text.TextUtils; 35692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.util.Log; 360bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport android.view.ContextThemeWrapper; 37692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.Gravity; 38692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.LayoutInflater; 39692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.View; 40692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.view.ViewGroup; 41692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.AdapterView; 42692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ArrayAdapter; 43692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ImageView; 44692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.ListView; 45692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport android.widget.TextView; 46692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 470bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limimport com.android.media.update.ApiHelper; 48692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.media.update.R; 49692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.support.mediarouter.media.MediaRouteSelector; 50692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport com.android.support.mediarouter.media.MediaRouter; 51692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 52692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.io.IOException; 53692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.io.InputStream; 54692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.ArrayList; 55692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.Collections; 56692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.Comparator; 57692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Limimport java.util.List; 58692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 59692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim/** 60692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * This class implements the route chooser dialog for {@link MediaRouter}. 61692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * <p> 62692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * This dialog allows the user to choose a route that matches a given selector. 63692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * </p> 64692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 65692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @see MediaRouteButton 66692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @see MediaRouteActionProvider 67692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 680bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Limpublic class MediaRouteChooserDialog extends Dialog { 69692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim static final String TAG = "MediaRouteChooserDialog"; 70692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 71692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim // Do not update the route list immediately to avoid unnatural dialog change. 72692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private static final long UPDATE_ROUTES_DELAY_MS = 300L; 73692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim static final int MSG_UPDATE_ROUTES = 1; 74692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 75692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final MediaRouter mRouter; 76692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final MediaRouterCallback mCallback; 77692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 78692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private TextView mTitleView; 79692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; 80692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private ArrayList<MediaRouter.RouteInfo> mRoutes; 81692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private RouteAdapter mAdapter; 82692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private ListView mListView; 83692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private boolean mAttachedToWindow; 84692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private long mLastUpdateTime; 85692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final Handler mHandler = new Handler() { 86692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 87692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void handleMessage(Message message) { 88692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim switch (message.what) { 89692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim case MSG_UPDATE_ROUTES: 90692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim updateRoutes((List<MediaRouter.RouteInfo>) message.obj); 91692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim break; 92692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 93692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 94692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim }; 95692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 96692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public MediaRouteChooserDialog(Context context) { 97692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim this(context, 0); 98692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 99692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 100692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public MediaRouteChooserDialog(Context context, int theme) { 101d5b1f00dbd91f5bc64f30891ec7a35f28274dbb0Sungsoo Lim // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme. 10292fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten super(new ContextThemeWrapper(context, ApiHelper.getLibTheme(context, 10392fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten MediaRouterThemeHelper.getRouterThemeId(context))), theme); 104692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim context = getContext(); 105692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 106692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRouter = MediaRouter.getInstance(context); 107692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mCallback = new MediaRouterCallback(); 108692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 109692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 110692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 111692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Gets the media route selector for filtering the routes that the user can select. 112692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 113692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @return The selector, never null. 114692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 115692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @NonNull 116692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public MediaRouteSelector getRouteSelector() { 117692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return mSelector; 118692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 119692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 120692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 121692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Sets the media route selector for filtering the routes that the user can select. 122692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 123692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @param selector The selector, must not be null. 124692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 125692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void setRouteSelector(@NonNull MediaRouteSelector selector) { 126692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (selector == null) { 127692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim throw new IllegalArgumentException("selector must not be null"); 128692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 129692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 130692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (!mSelector.equals(selector)) { 131692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mSelector = selector; 132692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 133692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (mAttachedToWindow) { 134692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRouter.removeCallback(mCallback); 135692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRouter.addCallback(selector, mCallback, 136692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); 137692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 138692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 139692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim refreshRoutes(); 140692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 141692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 142692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 143692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 144692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Called to filter the set of routes that should be included in the list. 145692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * <p> 146692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * The default implementation iterates over all routes in the provided list and 147692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * removes those for which {@link #onFilterRoute} returns false. 148692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * </p> 149692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 150692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @param routes The list of routes to filter in-place, never null. 151692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 152692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) { 153692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim for (int i = routes.size(); i-- > 0; ) { 154692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (!onFilterRoute(routes.get(i))) { 155692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim routes.remove(i); 156692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 157692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 158692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 159692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 160692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 161692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Returns true if the route should be included in the list. 162692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * <p> 163692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * The default implementation returns true for enabled non-default routes that 164692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * match the selector. Subclasses can override this method to filter routes 165692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * differently. 166692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * </p> 167692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * 168692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @param route The route to consider, never null. 169692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * @return True if the route should be included in the chooser dialog. 170692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 171692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) { 172692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return !route.isDefaultOrBluetooth() && route.isEnabled() 173692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim && route.matchesSelector(mSelector); 174692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 175692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 176692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 177692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void setTitle(CharSequence title) { 178692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mTitleView.setText(title); 179692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 180692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 181692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 182692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void setTitle(int titleId) { 183692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mTitleView.setText(titleId); 184692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 185692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 186692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 187692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim protected void onCreate(Bundle savedInstanceState) { 188692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim super.onCreate(savedInstanceState); 189692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 19092fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten setContentView(ApiHelper.inflateLibLayout(getContext(), ApiHelper.getLibTheme(getContext(), 19192fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten MediaRouterThemeHelper.getRouterThemeId(getContext())), 1920bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.layout.mr_chooser_dialog)); 193692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 194692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRoutes = new ArrayList<>(); 195692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mAdapter = new RouteAdapter(getContext(), mRoutes); 196692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mListView = (ListView)findViewById(R.id.mr_chooser_list); 197692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mListView.setAdapter(mAdapter); 198692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mListView.setOnItemClickListener(mAdapter); 199692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mListView.setEmptyView(findViewById(android.R.id.empty)); 200692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mTitleView = findViewById(R.id.mr_chooser_title); 201692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 202692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim updateLayout(); 203692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 204692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 205692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 206692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Sets the width of the dialog. Also called when configuration changes. 207692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 208692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim void updateLayout() { 20992fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()), 210692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim ViewGroup.LayoutParams.WRAP_CONTENT); 211692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 212692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 213692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 214692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onAttachedToWindow() { 215692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim super.onAttachedToWindow(); 216692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 217692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mAttachedToWindow = true; 218692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); 219692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim refreshRoutes(); 220692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 221692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 222692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 223692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onDetachedFromWindow() { 224692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mAttachedToWindow = false; 225692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRouter.removeCallback(mCallback); 226692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mHandler.removeMessages(MSG_UPDATE_ROUTES); 227692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 228692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim super.onDetachedFromWindow(); 229692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 230692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 231692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim /** 232692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim * Refreshes the list of routes that are shown in the chooser dialog. 233692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim */ 234692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void refreshRoutes() { 235692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (mAttachedToWindow) { 236692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim ArrayList<MediaRouter.RouteInfo> routes = new ArrayList<>(mRouter.getRoutes()); 237692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim onFilterRoutes(routes); 238692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim Collections.sort(routes, RouteComparator.sInstance); 239692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) { 240692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim updateRoutes(routes); 241692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } else { 242692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mHandler.removeMessages(MSG_UPDATE_ROUTES); 243692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes), 244692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mLastUpdateTime + UPDATE_ROUTES_DELAY_MS); 245692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 246692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 247692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 248692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 249692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim void updateRoutes(List<MediaRouter.RouteInfo> routes) { 250692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mLastUpdateTime = SystemClock.uptimeMillis(); 251692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRoutes.clear(); 252692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mRoutes.addAll(routes); 253692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mAdapter.notifyDataSetChanged(); 254692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 255692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 256692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo> 257692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim implements ListView.OnItemClickListener { 258692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final Drawable mDefaultIcon; 259692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final Drawable mTvIcon; 260692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final Drawable mSpeakerIcon; 261692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final Drawable mSpeakerGroupIcon; 262692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 263692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) { 264692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim super(context, 0, routes); 2650bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim 26692fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten TypedArray styledAttributes = ApiHelper.getLibTheme(context, 2670bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim MediaRouterThemeHelper.getRouterThemeId(context)).obtainStyledAttributes( 2680bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim new int[] { 2690bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.attr.mediaRouteDefaultIconDrawable, 2700bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.attr.mediaRouteTvIconDrawable, 2710bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.attr.mediaRouteSpeakerIconDrawable, 2720bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.attr.mediaRouteSpeakerGroupIconDrawable 2730bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim }); 2740bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim 275692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mDefaultIcon = styledAttributes.getDrawable(0); 276692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mTvIcon = styledAttributes.getDrawable(1); 277692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mSpeakerIcon = styledAttributes.getDrawable(2); 278692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim mSpeakerGroupIcon = styledAttributes.getDrawable(3); 279692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim styledAttributes.recycle(); 280692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 281692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 282692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 283692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public boolean areAllItemsEnabled() { 284692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return false; 285692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 286692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 287692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 288692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public boolean isEnabled(int position) { 289692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return getItem(position).isEnabled(); 290692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 291692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 292692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 293692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public View getView(int position, View convertView, ViewGroup parent) { 294692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim View view = convertView; 295692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (view == null) { 2960bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim view = ApiHelper.inflateLibLayout(getContext(), 29792fd9a9e07a33a01d12ef9c47f73b2c7193a5fd9Christofer Ã…kersten ApiHelper.getLibTheme(getContext(), 2980bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim MediaRouterThemeHelper.getRouterThemeId(getContext())), 2990bfa950527f7b5b44658d32346fd444073b8b6d8Sungsoo Lim R.layout.mr_chooser_list_item, parent, false); 300692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 301692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 302692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim MediaRouter.RouteInfo route = getItem(position); 303692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim TextView text1 = (TextView) view.findViewById(R.id.mr_chooser_route_name); 304692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim TextView text2 = (TextView) view.findViewById(R.id.mr_chooser_route_desc); 305692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text1.setText(route.getName()); 306692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim String description = route.getDescription(); 307692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim boolean isConnectedOrConnecting = 308692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim route.getConnectionState() == CONNECTION_STATE_CONNECTED 309692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim || route.getConnectionState() == CONNECTION_STATE_CONNECTING; 310692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (isConnectedOrConnecting && !TextUtils.isEmpty(description)) { 311692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text1.setGravity(Gravity.BOTTOM); 312692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text2.setVisibility(View.VISIBLE); 313692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text2.setText(description); 314692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } else { 315692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text1.setGravity(Gravity.CENTER_VERTICAL); 316692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text2.setVisibility(View.GONE); 317692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim text2.setText(""); 318692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 319692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim view.setEnabled(route.isEnabled()); 320692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 321692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim ImageView iconView = (ImageView) view.findViewById(R.id.mr_chooser_route_icon); 322692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (iconView != null) { 323692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim iconView.setImageDrawable(getIconDrawable(route)); 324692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 325692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return view; 326692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 327692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 328692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 329692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 330692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim MediaRouter.RouteInfo route = getItem(position); 331692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (route.isEnabled()) { 332692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim route.select(); 333692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim dismiss(); 334692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 335692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 336692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 337692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private Drawable getIconDrawable(MediaRouter.RouteInfo route) { 338692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim Uri iconUri = route.getIconUri(); 339692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (iconUri != null) { 340692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim try { 341692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim InputStream is = getContext().getContentResolver().openInputStream(iconUri); 342692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim Drawable drawable = Drawable.createFromStream(is, null); 343692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (drawable != null) { 344692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return drawable; 345692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 346692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } catch (IOException e) { 347692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim Log.w(TAG, "Failed to load " + iconUri, e); 348692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim // Falls back. 349692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 350692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 351692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return getDefaultIconDrawable(route); 352692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 353692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 354692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) { 355692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim // If the type of the receiver device is specified, use it. 356692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim switch (route.getDeviceType()) { 357692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim case MediaRouter.RouteInfo.DEVICE_TYPE_TV: 358692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return mTvIcon; 359692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER: 360692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return mSpeakerIcon; 361692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 362692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 363692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim // Otherwise, make the best guess based on other route information. 364692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim if (route instanceof MediaRouter.RouteGroup) { 365692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim // Only speakers can be grouped for now. 366692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return mSpeakerGroupIcon; 367692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 368692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return mDefaultIcon; 369692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 370692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 371692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 372692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim private final class MediaRouterCallback extends MediaRouter.Callback { 373692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim MediaRouterCallback() { 374692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 375692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 376692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 377692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { 378692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim refreshRoutes(); 379692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 380692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 381692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 382692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { 383692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim refreshRoutes(); 384692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 385692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 386692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 387692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { 388692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim refreshRoutes(); 389692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 390692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 391692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 392692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { 393692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim dismiss(); 394692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 395692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 396692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 397692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> { 398692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public static final RouteComparator sInstance = new RouteComparator(); 399692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim 400692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim @Override 401692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) { 402692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim return lhs.getName().compareToIgnoreCase(rhs.getName()); 403692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 404692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim } 405692a547730bbc95ad277d5214ef3d786ce1e499fSungsoo Lim} 406