Merge "Make MediaRouter UI more robust around route count changes" into jb-dev

This commit is contained in:
Adam Powell
2012-06-17 19:32:07 -07:00
committed by Android (Google) Code Review
5 changed files with 106 additions and 23 deletions

View File

@@ -22822,6 +22822,12 @@ package android.view {
method public boolean onPerformDefaultAction(); method public boolean onPerformDefaultAction();
method public void onPrepareSubMenu(android.view.SubMenu); method public void onPrepareSubMenu(android.view.SubMenu);
method public boolean overridesItemVisibility(); method public boolean overridesItemVisibility();
method public void refreshVisibility();
method public void setVisibilityListener(android.view.ActionProvider.VisibilityListener);
}
public static abstract interface ActionProvider.VisibilityListener {
method public abstract void onActionProviderVisibilityChanged(boolean);
} }
public final class Choreographer { public final class Choreographer {

View File

@@ -21,11 +21,14 @@ import com.android.internal.app.MediaRouteChooserDialogFragment;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.media.MediaRouter; import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.util.Log; import android.util.Log;
import android.view.ActionProvider; import android.view.ActionProvider;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import java.lang.ref.WeakReference;
public class MediaRouteActionProvider extends ActionProvider { public class MediaRouteActionProvider extends ActionProvider {
private static final String TAG = "MediaRouteActionProvider"; private static final String TAG = "MediaRouteActionProvider";
@@ -35,11 +38,13 @@ public class MediaRouteActionProvider extends ActionProvider {
private MediaRouteButton mView; private MediaRouteButton mView;
private int mRouteTypes; private int mRouteTypes;
private View.OnClickListener mExtendedSettingsListener; private View.OnClickListener mExtendedSettingsListener;
private RouterCallback mCallback;
public MediaRouteActionProvider(Context context) { public MediaRouteActionProvider(Context context) {
super(context); super(context);
mContext = context; mContext = context;
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mCallback = new RouterCallback(this);
// Start with live audio by default. // Start with live audio by default.
// TODO Update this when new route types are added; segment by API level // TODO Update this when new route types are added; segment by API level
@@ -48,7 +53,14 @@ public class MediaRouteActionProvider extends ActionProvider {
} }
public void setRouteTypes(int types) { public void setRouteTypes(int types) {
if (mRouteTypes == types) return;
if (mRouteTypes != 0) {
mRouter.removeCallback(mCallback);
}
mRouteTypes = types; mRouteTypes = types;
if (types != 0) {
mRouter.addCallback(types, mCallback);
}
if (mView != null) { if (mView != null) {
mView.setRouteTypes(mRouteTypes); mView.setRouteTypes(mRouteTypes);
} }
@@ -68,7 +80,6 @@ public class MediaRouteActionProvider extends ActionProvider {
} }
mMenuItem = item; mMenuItem = item;
mView = new MediaRouteButton(mContext); mView = new MediaRouteButton(mContext);
mMenuItem.setVisible(mRouter.getRouteCount() > 1);
mView.setRouteTypes(mRouteTypes); mView.setRouteTypes(mRouteTypes);
mView.setExtendedSettingsClickListener(mExtendedSettingsListener); mView.setExtendedSettingsClickListener(mExtendedSettingsListener);
return mView; return mView;
@@ -123,4 +134,34 @@ public class MediaRouteActionProvider extends ActionProvider {
public boolean isVisible() { public boolean isVisible() {
return mRouter.getRouteCount() > 1; return mRouter.getRouteCount() > 1;
} }
private static class RouterCallback extends MediaRouter.SimpleCallback {
private WeakReference<MediaRouteActionProvider> mAp;
RouterCallback(MediaRouteActionProvider ap) {
mAp = new WeakReference<MediaRouteActionProvider>(ap);
}
@Override
public void onRouteAdded(MediaRouter router, RouteInfo info) {
final MediaRouteActionProvider ap = mAp.get();
if (ap == null) {
router.removeCallback(this);
return;
}
ap.refreshVisibility();
}
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
final MediaRouteActionProvider ap = mAp.get();
if (ap == null) {
router.removeCallback(this);
return;
}
ap.refreshVisibility();
}
}
} }

View File

@@ -17,6 +17,7 @@
package android.view; package android.view;
import android.content.Context; import android.content.Context;
import android.util.Log;
/** /**
* An ActionProvider defines rich menu interaction in a single component. * An ActionProvider defines rich menu interaction in a single component.
@@ -55,7 +56,9 @@ import android.content.Context;
* @see MenuItem#getActionProvider() * @see MenuItem#getActionProvider()
*/ */
public abstract class ActionProvider { public abstract class ActionProvider {
private static final String TAG = "ActionProvider";
private SubUiVisibilityListener mSubUiVisibilityListener; private SubUiVisibilityListener mSubUiVisibilityListener;
private VisibilityListener mVisibilityListener;
/** /**
* Creates a new instance. ActionProvider classes should always implement a * Creates a new instance. ActionProvider classes should always implement a
@@ -121,6 +124,18 @@ public abstract class ActionProvider {
return true; return true;
} }
/**
* If this ActionProvider is associated with an item in a menu,
* refresh the visibility of the item based on {@link #overridesItemVisibility()} and
* {@link #isVisible()}. If {@link #overridesItemVisibility()} returns false, this call
* will have no effect.
*/
public void refreshVisibility() {
if (mVisibilityListener != null && overridesItemVisibility()) {
mVisibilityListener.onActionProviderVisibilityChanged(isVisible());
}
}
/** /**
* Performs an optional default action. * Performs an optional default action.
* <p> * <p>
@@ -206,10 +221,35 @@ public abstract class ActionProvider {
mSubUiVisibilityListener = listener; mSubUiVisibilityListener = listener;
} }
/**
* Set a listener to be notified when this ActionProvider's overridden visibility changes.
* This should only be used by MenuItem implementations.
*
* @param listener listener to set
*/
public void setVisibilityListener(VisibilityListener listener) {
if (mVisibilityListener != null) {
Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
"when one is already set. Are you reusing this " + getClass().getSimpleName() +
" instance while it is still in use somewhere else?");
}
mVisibilityListener = listener;
}
/** /**
* @hide Internal use only * @hide Internal use only
*/ */
public interface SubUiVisibilityListener { public interface SubUiVisibilityListener {
public void onSubUiVisibilityChanged(boolean isVisible); public void onSubUiVisibilityChanged(boolean isVisible);
} }
/**
* Listens to changes in visibility as reported by {@link ActionProvider#refreshVisibility()}.
*
* @see ActionProvider#overridesItemVisibility()
* @see ActionProvider#isVisible()
*/
public interface VisibilityListener {
public void onActionProviderVisibilityChanged(boolean isVisible);
}
} }

View File

@@ -589,9 +589,17 @@ public final class MenuItemImpl implements MenuItem {
} }
public MenuItem setActionProvider(ActionProvider actionProvider) { public MenuItem setActionProvider(ActionProvider actionProvider) {
if (mActionProvider != null) {
mActionProvider.setVisibilityListener(null);
}
mActionView = null; mActionView = null;
mActionProvider = actionProvider; mActionProvider = actionProvider;
mMenu.onItemsChanged(true); // Measurement can be changed mMenu.onItemsChanged(true); // Measurement can be changed
mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
@Override public void onActionProviderVisibilityChanged(boolean isVisible) {
mMenu.onItemVisibleChanged(MenuItemImpl.this);
}
});
return this; return this;
} }

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* MediaRouter allows applications to control the routing of media channels * MediaRouter allows applications to control the routing of media channels
@@ -48,7 +49,8 @@ public class MediaRouter {
final Resources mResources; final Resources mResources;
final IAudioService mAudioService; final IAudioService mAudioService;
final Handler mHandler; final Handler mHandler;
final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>(); final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
new CopyOnWriteArrayList<CallbackInfo>();
final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>(); final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
@@ -497,9 +499,7 @@ public class MediaRouter {
} }
static void dispatchRouteSelected(int type, RouteInfo info) { static void dispatchRouteSelected(int type, RouteInfo info) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & type) != 0) { if ((cbi.type & type) != 0) {
cbi.cb.onRouteSelected(cbi.router, type, info); cbi.cb.onRouteSelected(cbi.router, type, info);
} }
@@ -507,9 +507,7 @@ public class MediaRouter {
} }
static void dispatchRouteUnselected(int type, RouteInfo info) { static void dispatchRouteUnselected(int type, RouteInfo info) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & type) != 0) { if ((cbi.type & type) != 0) {
cbi.cb.onRouteUnselected(cbi.router, type, info); cbi.cb.onRouteUnselected(cbi.router, type, info);
} }
@@ -517,9 +515,7 @@ public class MediaRouter {
} }
static void dispatchRouteChanged(RouteInfo info) { static void dispatchRouteChanged(RouteInfo info) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & info.mSupportedTypes) != 0) { if ((cbi.type & info.mSupportedTypes) != 0) {
cbi.cb.onRouteChanged(cbi.router, info); cbi.cb.onRouteChanged(cbi.router, info);
} }
@@ -527,9 +523,7 @@ public class MediaRouter {
} }
static void dispatchRouteAdded(RouteInfo info) { static void dispatchRouteAdded(RouteInfo info) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & info.mSupportedTypes) != 0) { if ((cbi.type & info.mSupportedTypes) != 0) {
cbi.cb.onRouteAdded(cbi.router, info); cbi.cb.onRouteAdded(cbi.router, info);
} }
@@ -537,9 +531,7 @@ public class MediaRouter {
} }
static void dispatchRouteRemoved(RouteInfo info) { static void dispatchRouteRemoved(RouteInfo info) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & info.mSupportedTypes) != 0) { if ((cbi.type & info.mSupportedTypes) != 0) {
cbi.cb.onRouteRemoved(cbi.router, info); cbi.cb.onRouteRemoved(cbi.router, info);
} }
@@ -547,9 +539,7 @@ public class MediaRouter {
} }
static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) { static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & group.mSupportedTypes) != 0) { if ((cbi.type & group.mSupportedTypes) != 0) {
cbi.cb.onRouteGrouped(cbi.router, info, group, index); cbi.cb.onRouteGrouped(cbi.router, info, group, index);
} }
@@ -557,9 +547,7 @@ public class MediaRouter {
} }
static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) { static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
final int count = sStatic.mCallbacks.size(); for (CallbackInfo cbi : sStatic.mCallbacks) {
for (int i = 0; i < count; i++) {
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
if ((cbi.type & group.mSupportedTypes) != 0) { if ((cbi.type & group.mSupportedTypes) != 0) {
cbi.cb.onRouteUngrouped(cbi.router, info, group); cbi.cb.onRouteUngrouped(cbi.router, info, group);
} }