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