1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 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 17 package android.support.v7.app; 18 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.support.annotation.NonNull; 23 import android.support.v7.media.MediaRouter; 24 import android.support.v7.media.MediaRouteSelector; 25 import android.support.v7.mediarouter.R; 26 import android.text.TextUtils; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.view.Window; 31 import android.widget.AdapterView; 32 import android.widget.ArrayAdapter; 33 import android.widget.ListView; 34 import android.widget.TextView; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.Comparator; 39 import java.util.List; 40 41 /** 42 * This class implements the route chooser dialog for {@link MediaRouter}. 43 * <p> 44 * This dialog allows the user to choose a route that matches a given selector. 45 * </p> 46 * 47 * @see MediaRouteButton 48 * @see MediaRouteActionProvider 49 */ 50 public class MediaRouteChooserDialog extends Dialog { 51 private final MediaRouter mRouter; 52 private final MediaRouterCallback mCallback; 53 54 private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; 55 private ArrayList<MediaRouter.RouteInfo> mRoutes; 56 private RouteAdapter mAdapter; 57 private ListView mListView; 58 private boolean mAttachedToWindow; 59 MediaRouteChooserDialog(Context context)60 public MediaRouteChooserDialog(Context context) { 61 this(context, 0); 62 } 63 MediaRouteChooserDialog(Context context, int theme)64 public MediaRouteChooserDialog(Context context, int theme) { 65 super(MediaRouterThemeHelper.createThemedContext(context), theme); 66 context = getContext(); 67 68 mRouter = MediaRouter.getInstance(context); 69 mCallback = new MediaRouterCallback(); 70 } 71 72 /** 73 * Gets the media route selector for filtering the routes that the user can select. 74 * 75 * @return The selector, never null. 76 */ 77 @NonNull getRouteSelector()78 public MediaRouteSelector getRouteSelector() { 79 return mSelector; 80 } 81 82 /** 83 * Sets the media route selector for filtering the routes that the user can select. 84 * 85 * @param selector The selector, must not be null. 86 */ setRouteSelector(@onNull MediaRouteSelector selector)87 public void setRouteSelector(@NonNull MediaRouteSelector selector) { 88 if (selector == null) { 89 throw new IllegalArgumentException("selector must not be null"); 90 } 91 92 if (!mSelector.equals(selector)) { 93 mSelector = selector; 94 95 if (mAttachedToWindow) { 96 mRouter.removeCallback(mCallback); 97 mRouter.addCallback(selector, mCallback, 98 MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); 99 } 100 101 refreshRoutes(); 102 } 103 } 104 105 /** 106 * Called to filter the set of routes that should be included in the list. 107 * <p> 108 * The default implementation iterates over all routes in the provided list and 109 * removes those for which {@link #onFilterRoute} returns false. 110 * </p> 111 * 112 * @param routes The list of routes to filter in-place, never null. 113 */ onFilterRoutes(@onNull List<MediaRouter.RouteInfo> routes)114 public void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) { 115 for (int i = routes.size(); i-- > 0; ) { 116 if (!onFilterRoute(routes.get(i))) { 117 routes.remove(i); 118 } 119 } 120 } 121 122 /** 123 * Returns true if the route should be included in the list. 124 * <p> 125 * The default implementation returns true for enabled non-default routes that 126 * match the selector. Subclasses can override this method to filter routes 127 * differently. 128 * </p> 129 * 130 * @param route The route to consider, never null. 131 * @return True if the route should be included in the chooser dialog. 132 */ onFilterRoute(@onNull MediaRouter.RouteInfo route)133 public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) { 134 return !route.isDefault() && route.isEnabled() && route.matchesSelector(mSelector); 135 } 136 137 @Override onCreate(Bundle savedInstanceState)138 protected void onCreate(Bundle savedInstanceState) { 139 super.onCreate(savedInstanceState); 140 141 getWindow().requestFeature(Window.FEATURE_LEFT_ICON); 142 143 setContentView(R.layout.mr_media_route_chooser_dialog); 144 setTitle(R.string.mr_media_route_chooser_title); 145 146 // Must be called after setContentView. 147 getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, 148 MediaRouterThemeHelper.getThemeResource( 149 getContext(), R.attr.mediaRouteOffDrawable)); 150 151 mRoutes = new ArrayList<MediaRouter.RouteInfo>(); 152 mAdapter = new RouteAdapter(getContext(), mRoutes); 153 mListView = (ListView)findViewById(R.id.media_route_list); 154 mListView.setAdapter(mAdapter); 155 mListView.setOnItemClickListener(mAdapter); 156 mListView.setEmptyView(findViewById(android.R.id.empty)); 157 } 158 159 @Override onAttachedToWindow()160 public void onAttachedToWindow() { 161 super.onAttachedToWindow(); 162 163 mAttachedToWindow = true; 164 mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); 165 refreshRoutes(); 166 } 167 168 @Override onDetachedFromWindow()169 public void onDetachedFromWindow() { 170 mAttachedToWindow = false; 171 mRouter.removeCallback(mCallback); 172 173 super.onDetachedFromWindow(); 174 } 175 176 /** 177 * Refreshes the list of routes that are shown in the chooser dialog. 178 */ refreshRoutes()179 public void refreshRoutes() { 180 if (mAttachedToWindow) { 181 mRoutes.clear(); 182 mRoutes.addAll(mRouter.getRoutes()); 183 onFilterRoutes(mRoutes); 184 Collections.sort(mRoutes, RouteComparator.sInstance); 185 mAdapter.notifyDataSetChanged(); 186 } 187 } 188 189 private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo> 190 implements ListView.OnItemClickListener { 191 private final LayoutInflater mInflater; 192 RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes)193 public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) { 194 super(context, 0, routes); 195 mInflater = LayoutInflater.from(context); 196 } 197 198 @Override areAllItemsEnabled()199 public boolean areAllItemsEnabled() { 200 return false; 201 } 202 203 @Override isEnabled(int position)204 public boolean isEnabled(int position) { 205 return getItem(position).isEnabled(); 206 } 207 208 @Override getView(int position, View convertView, ViewGroup parent)209 public View getView(int position, View convertView, ViewGroup parent) { 210 View view = convertView; 211 if (view == null) { 212 view = mInflater.inflate(R.layout.mr_media_route_list_item, parent, false); 213 } 214 MediaRouter.RouteInfo route = getItem(position); 215 TextView text1 = (TextView)view.findViewById(android.R.id.text1); 216 TextView text2 = (TextView)view.findViewById(android.R.id.text2); 217 text1.setText(route.getName()); 218 String description = route.getDescription(); 219 if (TextUtils.isEmpty(description)) { 220 text2.setVisibility(View.GONE); 221 text2.setText(""); 222 } else { 223 text2.setVisibility(View.VISIBLE); 224 text2.setText(description); 225 } 226 view.setEnabled(route.isEnabled()); 227 return view; 228 } 229 230 @Override onItemClick(AdapterView<?> parent, View view, int position, long id)231 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 232 MediaRouter.RouteInfo route = getItem(position); 233 if (route.isEnabled()) { 234 route.select(); 235 dismiss(); 236 } 237 } 238 } 239 240 private final class MediaRouterCallback extends MediaRouter.Callback { 241 @Override onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info)242 public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { 243 refreshRoutes(); 244 } 245 246 @Override onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info)247 public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { 248 refreshRoutes(); 249 } 250 251 @Override onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info)252 public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { 253 refreshRoutes(); 254 } 255 256 @Override onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route)257 public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { 258 dismiss(); 259 } 260 } 261 262 private static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> { 263 public static final RouteComparator sInstance = new RouteComparator(); 264 265 @Override compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs)266 public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) { 267 return lhs.getName().compareTo(rhs.getName()); 268 } 269 } 270 } 271