/*
* Copyright (C) 2012 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.app;
import android.content.Context;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.util.Log;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
/**
* The media route action provider displays a {@link MediaRouteButton media route button}
* in the application's {@link ActionBar} to allow the user to select routes and
* to control the currently selected route.
* <p>
* The application must specify the kinds of routes that the user should be allowed
* to select by specifying the route types with the {@link #setRouteTypes} method.
* </p><p>
* Refer to {@link MediaRouteButton} for a description of the button that will
* appear in the action bar menu. Note that instead of disabling the button
* when no routes are available, the action provider will instead make the
* menu item invisible. In this way, the button will only be visible when it
* is possible for the user to discover and select a matching route.
* </p>
*/
public class MediaRouteActionProvider extends ActionProvider {
private static final String TAG = "MediaRouteActionProvider";
private final Context mContext;
private final MediaRouter mRouter;
private final MediaRouterCallback mCallback;
private int mRouteTypes;
private MediaRouteButton mButton;
private View.OnClickListener mExtendedSettingsListener;
public MediaRouteActionProvider(Context context) {
super(context);
mContext = context;
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new MediaRouterCallback(this);
// Start with live audio by default.
// TODO Update this when new route types are added; segment by API level
// when different route types were added.
setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
}
/**
* Sets the types of routes that will be shown in the media route chooser dialog
* launched by this button.
*
* @param types The route types to match.
*/
public void setRouteTypes(int types) {
if (mRouteTypes != types) {
// FIXME: We currently have no way of knowing whether the action provider
// is still needed by the UI. Unfortunately this means the action provider
// may leak callbacks until garbage collection occurs. This may result in
// media route providers doing more work than necessary in the short term
// while trying to discover routes that are no longer of interest to the
// application. To solve this problem, the action provider will need some
// indication from the framework that it is being destroyed.
if (mRouteTypes != 0) {
mRouter.removeCallback(mCallback);
}
mRouteTypes = types;
if (types != 0) {
mRouter.addCallback(types, mCallback,
MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
}
refreshRoute();
if (mButton != null) {
mButton.setRouteTypes(mRouteTypes);
}
}
}
public void setExtendedSettingsClickListener(View.OnClickListener listener) {
mExtendedSettingsListener = listener;
if (mButton != null) {
mButton.setExtendedSettingsClickListener(listener);
}
}
@Override
@SuppressWarnings("deprecation")
public View onCreateActionView() {
throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
}
@Override
public View onCreateActionView(MenuItem item) {
if (mButton != null) {
Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
"with a menu item. Don't reuse MediaRouteActionProvider instances! " +
"Abandoning the old one...");
}
mButton = new MediaRouteButton(mContext);
mButton.setCheatSheetEnabled(true);
mButton.setRouteTypes(mRouteTypes);
mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
mButton.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT));
return mButton;
}
@Override
public boolean onPerformDefaultAction() {
if (mButton != null) {
return mButton.showDialogInternal();
}
return false;
}
@Override
public boolean overridesItemVisibility() {
return true;
}
@Override
public boolean isVisible() {
return mRouter.isRouteAvailable(mRouteTypes,
MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
}
private void refreshRoute() {
refreshVisibility();
}
private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
private final WeakReference<MediaRouteActionProvider> mProviderWeak;
public MediaRouterCallback(MediaRouteActionProvider provider) {
mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
}
@Override
public void onRouteAdded(MediaRouter router, RouteInfo info) {
refreshRoute(router);
}
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
refreshRoute(router);
}
@Override
public void onRouteChanged(MediaRouter router, RouteInfo info) {
refreshRoute(router);
}
private void refreshRoute(MediaRouter router) {
MediaRouteActionProvider provider = mProviderWeak.get();
if (provider != null) {
provider.refreshRoute();
} else {
router.removeCallback(this);
}
}
}
}