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 package android.support.v7.media; 17 18 import android.content.IntentFilter; 19 import android.os.Bundle; 20 import android.support.annotation.NonNull; 21 import android.support.annotation.Nullable; 22 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.List; 28 29 /** 30 * Describes the capabilities of routes that applications would like to discover and use. 31 * <p> 32 * This object is immutable once created using a {@link Builder} instance. 33 * </p> 34 * 35 * <h3>Example</h3> 36 * <pre> 37 * MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder() 38 * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) 39 * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 40 * .build(); 41 * 42 * MediaRouter router = MediaRouter.getInstance(context); 43 * router.addCallback(selector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 44 * </pre> 45 */ 46 public final class MediaRouteSelector { 47 static final String KEY_CONTROL_CATEGORIES = "controlCategories"; 48 49 private final Bundle mBundle; 50 List<String> mControlCategories; 51 52 /** 53 * An empty media route selector that will not match any routes. 54 */ 55 public static final MediaRouteSelector EMPTY = new MediaRouteSelector(new Bundle(), null); 56 MediaRouteSelector(Bundle bundle, List<String> controlCategories)57 MediaRouteSelector(Bundle bundle, List<String> controlCategories) { 58 mBundle = bundle; 59 mControlCategories = controlCategories; 60 } 61 62 /** 63 * Gets the list of {@link MediaControlIntent media control categories} in the selector. 64 * 65 * @return The list of categories. 66 */ getControlCategories()67 public List<String> getControlCategories() { 68 ensureControlCategories(); 69 return mControlCategories; 70 } 71 ensureControlCategories()72 void ensureControlCategories() { 73 if (mControlCategories == null) { 74 mControlCategories = mBundle.getStringArrayList(KEY_CONTROL_CATEGORIES); 75 if (mControlCategories == null || mControlCategories.isEmpty()) { 76 mControlCategories = Collections.<String>emptyList(); 77 } 78 } 79 } 80 81 /** 82 * Returns true if the selector contains the specified category. 83 * 84 * @param category The category to check. 85 * @return True if the category is present. 86 */ hasControlCategory(String category)87 public boolean hasControlCategory(String category) { 88 if (category != null) { 89 ensureControlCategories(); 90 final int categoryCount = mControlCategories.size(); 91 for (int i = 0; i < categoryCount; i++) { 92 if (mControlCategories.get(i).equals(category)) { 93 return true; 94 } 95 } 96 } 97 return false; 98 } 99 100 /** 101 * Returns true if the selector matches at least one of the specified control filters. 102 * 103 * @param filters The list of control filters to consider. 104 * @return True if a match is found. 105 */ matchesControlFilters(List<IntentFilter> filters)106 public boolean matchesControlFilters(List<IntentFilter> filters) { 107 if (filters != null) { 108 ensureControlCategories(); 109 final int categoryCount = mControlCategories.size(); 110 if (categoryCount != 0) { 111 final int filterCount = filters.size(); 112 for (int i = 0; i < filterCount; i++) { 113 final IntentFilter filter = filters.get(i); 114 if (filter != null) { 115 for (int j = 0; j < categoryCount; j++) { 116 if (filter.hasCategory(mControlCategories.get(j))) { 117 return true; 118 } 119 } 120 } 121 } 122 } 123 } 124 return false; 125 } 126 127 /** 128 * Returns true if this selector contains all of the capabilities described 129 * by the specified selector. 130 * 131 * @param selector The selector to be examined. 132 * @return True if this selector contains all of the capabilities described 133 * by the specified selector. 134 */ contains(MediaRouteSelector selector)135 public boolean contains(MediaRouteSelector selector) { 136 if (selector != null) { 137 ensureControlCategories(); 138 selector.ensureControlCategories(); 139 return mControlCategories.containsAll(selector.mControlCategories); 140 } 141 return false; 142 } 143 144 /** 145 * Returns true if the selector does not specify any capabilities. 146 */ isEmpty()147 public boolean isEmpty() { 148 ensureControlCategories(); 149 return mControlCategories.isEmpty(); 150 } 151 152 /** 153 * Returns true if the selector has all of the required fields. 154 */ isValid()155 public boolean isValid() { 156 ensureControlCategories(); 157 if (mControlCategories.contains(null)) { 158 return false; 159 } 160 return true; 161 } 162 163 @Override equals(Object o)164 public boolean equals(Object o) { 165 if (o instanceof MediaRouteSelector) { 166 MediaRouteSelector other = (MediaRouteSelector)o; 167 ensureControlCategories(); 168 other.ensureControlCategories(); 169 return mControlCategories.equals(other.mControlCategories); 170 } 171 return false; 172 } 173 174 @Override hashCode()175 public int hashCode() { 176 ensureControlCategories(); 177 return mControlCategories.hashCode(); 178 } 179 180 @Override toString()181 public String toString() { 182 StringBuilder result = new StringBuilder(); 183 result.append("MediaRouteSelector{ "); 184 result.append("controlCategories=").append( 185 Arrays.toString(getControlCategories().toArray())); 186 result.append(" }"); 187 return result.toString(); 188 } 189 190 /** 191 * Converts this object to a bundle for serialization. 192 * 193 * @return The contents of the object represented as a bundle. 194 */ asBundle()195 public Bundle asBundle() { 196 return mBundle; 197 } 198 199 /** 200 * Creates an instance from a bundle. 201 * 202 * @param bundle The bundle, or null if none. 203 * @return The new instance, or null if the bundle was null. 204 */ fromBundle(@ullable Bundle bundle)205 public static MediaRouteSelector fromBundle(@Nullable Bundle bundle) { 206 return bundle != null ? new MediaRouteSelector(bundle, null) : null; 207 } 208 209 /** 210 * Builder for {@link MediaRouteSelector media route selectors}. 211 */ 212 public static final class Builder { 213 private ArrayList<String> mControlCategories; 214 215 /** 216 * Creates an empty media route selector builder. 217 */ Builder()218 public Builder() { 219 } 220 221 /** 222 * Creates a media route selector descriptor builder whose initial contents are 223 * copied from an existing selector. 224 */ Builder(@onNull MediaRouteSelector selector)225 public Builder(@NonNull MediaRouteSelector selector) { 226 if (selector == null) { 227 throw new IllegalArgumentException("selector must not be null"); 228 } 229 230 selector.ensureControlCategories(); 231 if (!selector.mControlCategories.isEmpty()) { 232 mControlCategories = new ArrayList<String>(selector.mControlCategories); 233 } 234 } 235 236 /** 237 * Adds a {@link MediaControlIntent media control category} to the builder. 238 * 239 * @param category The category to add to the set of desired capabilities, such as 240 * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}. 241 * @return The builder instance for chaining. 242 */ 243 @NonNull addControlCategory(@onNull String category)244 public Builder addControlCategory(@NonNull String category) { 245 if (category == null) { 246 throw new IllegalArgumentException("category must not be null"); 247 } 248 249 if (mControlCategories == null) { 250 mControlCategories = new ArrayList<String>(); 251 } 252 if (!mControlCategories.contains(category)) { 253 mControlCategories.add(category); 254 } 255 return this; 256 } 257 258 /** 259 * Adds a list of {@link MediaControlIntent media control categories} to the builder. 260 * 261 * @param categories The list categories to add to the set of desired capabilities, 262 * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}. 263 * @return The builder instance for chaining. 264 */ 265 @NonNull addControlCategories(@onNull Collection<String> categories)266 public Builder addControlCategories(@NonNull Collection<String> categories) { 267 if (categories == null) { 268 throw new IllegalArgumentException("categories must not be null"); 269 } 270 271 if (!categories.isEmpty()) { 272 for (String category : categories) { 273 addControlCategory(category); 274 } 275 } 276 return this; 277 } 278 279 /** 280 * Adds the contents of an existing media route selector to the builder. 281 * 282 * @param selector The media route selector whose contents are to be added. 283 * @return The builder instance for chaining. 284 */ 285 @NonNull addSelector(@onNull MediaRouteSelector selector)286 public Builder addSelector(@NonNull MediaRouteSelector selector) { 287 if (selector == null) { 288 throw new IllegalArgumentException("selector must not be null"); 289 } 290 291 addControlCategories(selector.getControlCategories()); 292 return this; 293 } 294 295 /** 296 * Builds the {@link MediaRouteSelector media route selector}. 297 */ 298 @NonNull build()299 public MediaRouteSelector build() { 300 if (mControlCategories == null) { 301 return EMPTY; 302 } 303 Bundle bundle = new Bundle(); 304 bundle.putStringArrayList(KEY_CONTROL_CATEGORIES, mControlCategories); 305 return new MediaRouteSelector(bundle, mControlCategories); 306 } 307 } 308 }