/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v7.media;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Describes the capabilities of routes that applications would like to discover and use.
*
* This object is immutable once created using a {@link Builder} instance.
*
*
* Example
*
* MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
* .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
* .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
* .build();
*
* MediaRouter router = MediaRouter.getInstance(context);
* router.addCallback(selector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
*
*/
public final class MediaRouteSelector {
private static final String KEY_CONTROL_CATEGORIES = "controlCategories";
private final Bundle mBundle;
private List mControlCategories;
/**
* An empty media route selector that will not match any routes.
*/
public static final MediaRouteSelector EMPTY = new MediaRouteSelector(new Bundle(), null);
private MediaRouteSelector(Bundle bundle, List controlCategories) {
mBundle = bundle;
mControlCategories = controlCategories;
}
/**
* Gets the list of {@link MediaControlIntent media control categories} in the selector.
*
* @return The list of categories.
*/
public List getControlCategories() {
ensureControlCategories();
return mControlCategories;
}
private void ensureControlCategories() {
if (mControlCategories == null) {
mControlCategories = mBundle.getStringArrayList(KEY_CONTROL_CATEGORIES);
if (mControlCategories == null || mControlCategories.isEmpty()) {
mControlCategories = Collections.emptyList();
}
}
}
/**
* Returns true if the selector contains the specified category.
*
* @param category The category to check.
* @return True if the category is present.
*/
public boolean hasControlCategory(String category) {
if (category != null) {
ensureControlCategories();
final int categoryCount = mControlCategories.size();
for (int i = 0; i < categoryCount; i++) {
if (mControlCategories.get(i).equals(category)) {
return true;
}
}
}
return false;
}
/**
* Returns true if the selector matches at least one of the specified control filters.
*
* @param filters The list of control filters to consider.
* @return True if a match is found.
*/
public boolean matchesControlFilters(List filters) {
if (filters != null) {
ensureControlCategories();
final int categoryCount = mControlCategories.size();
if (categoryCount != 0) {
final int filterCount = filters.size();
for (int i = 0; i < filterCount; i++) {
final IntentFilter filter = filters.get(i);
if (filter != null) {
for (int j = 0; j < categoryCount; j++) {
if (filter.hasCategory(mControlCategories.get(j))) {
return true;
}
}
}
}
}
}
return false;
}
/**
* Returns true if this selector contains all of the capabilities described
* by the specified selector.
*
* @param selector The selector to be examined.
* @return True if this selector contains all of the capabilities described
* by the specified selector.
*/
public boolean contains(MediaRouteSelector selector) {
if (selector != null) {
ensureControlCategories();
selector.ensureControlCategories();
return mControlCategories.containsAll(selector.mControlCategories);
}
return false;
}
/**
* Returns true if the selector does not specify any capabilities.
*/
public boolean isEmpty() {
ensureControlCategories();
return mControlCategories.isEmpty();
}
/**
* Returns true if the selector has all of the required fields.
*/
public boolean isValid() {
ensureControlCategories();
if (mControlCategories.contains(null)) {
return false;
}
return true;
}
@Override
public boolean equals(Object o) {
if (o instanceof MediaRouteSelector) {
MediaRouteSelector other = (MediaRouteSelector)o;
ensureControlCategories();
other.ensureControlCategories();
return mControlCategories.equals(other.mControlCategories);
}
return false;
}
@Override
public int hashCode() {
ensureControlCategories();
return mControlCategories.hashCode();
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("MediaRouteSelector{ ");
result.append("controlCategories=").append(
Arrays.toString(getControlCategories().toArray()));
result.append(" }");
return result.toString();
}
/**
* Converts this object to a bundle for serialization.
*
* @return The contents of the object represented as a bundle.
*/
public Bundle asBundle() {
return mBundle;
}
/**
* Creates an instance from a bundle.
*
* @param bundle The bundle, or null if none.
* @return The new instance, or null if the bundle was null.
*/
public static MediaRouteSelector fromBundle(@Nullable Bundle bundle) {
return bundle != null ? new MediaRouteSelector(bundle, null) : null;
}
/**
* Builder for {@link MediaRouteSelector media route selectors}.
*/
public static final class Builder {
private ArrayList mControlCategories;
/**
* Creates an empty media route selector builder.
*/
public Builder() {
}
/**
* Creates a media route selector descriptor builder whose initial contents are
* copied from an existing selector.
*/
public Builder(@NonNull MediaRouteSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector must not be null");
}
selector.ensureControlCategories();
if (!selector.mControlCategories.isEmpty()) {
mControlCategories = new ArrayList(selector.mControlCategories);
}
}
/**
* Adds a {@link MediaControlIntent media control category} to the builder.
*
* @param category The category to add to the set of desired capabilities, such as
* {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
* @return The builder instance for chaining.
*/
@NonNull
public Builder addControlCategory(@NonNull String category) {
if (category == null) {
throw new IllegalArgumentException("category must not be null");
}
if (mControlCategories == null) {
mControlCategories = new ArrayList();
}
if (!mControlCategories.contains(category)) {
mControlCategories.add(category);
}
return this;
}
/**
* Adds a list of {@link MediaControlIntent media control categories} to the builder.
*
* @param categories The list categories to add to the set of desired capabilities,
* such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
* @return The builder instance for chaining.
*/
@NonNull
public Builder addControlCategories(@NonNull Collection categories) {
if (categories == null) {
throw new IllegalArgumentException("categories must not be null");
}
if (!categories.isEmpty()) {
for (String category : categories) {
addControlCategory(category);
}
}
return this;
}
/**
* Adds the contents of an existing media route selector to the builder.
*
* @param selector The media route selector whose contents are to be added.
* @return The builder instance for chaining.
*/
@NonNull
public Builder addSelector(@NonNull MediaRouteSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector must not be null");
}
addControlCategories(selector.getControlCategories());
return this;
}
/**
* Builds the {@link MediaRouteSelector media route selector}.
*/
@NonNull
public MediaRouteSelector build() {
if (mControlCategories == null) {
return EMPTY;
}
Bundle bundle = new Bundle();
bundle.putStringArrayList(KEY_CONTROL_CATEGORIES, mControlCategories);
return new MediaRouteSelector(bundle, mControlCategories);
}
}
}