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 }