Skip to content

Commit 706a869

Browse files
adampAndroid (Google) Code Review
authored andcommitted
Merge "Make MediaRouter UI more robust around route count changes" into jb-dev
2 parents d7a04de + 39d5c61 commit 706a869

File tree

5 files changed

+106
-23
lines changed

5 files changed

+106
-23
lines changed

api/current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22822,6 +22822,12 @@ package android.view {
2282222822
method public boolean onPerformDefaultAction();
2282322823
method public void onPrepareSubMenu(android.view.SubMenu);
2282422824
method public boolean overridesItemVisibility();
22825+
method public void refreshVisibility();
22826+
method public void setVisibilityListener(android.view.ActionProvider.VisibilityListener);
22827+
}
22828+
22829+
public static abstract interface ActionProvider.VisibilityListener {
22830+
method public abstract void onActionProviderVisibilityChanged(boolean);
2282522831
}
2282622832

2282722833
public final class Choreographer {

core/java/android/app/MediaRouteActionProvider.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
import android.content.Context;
2222
import android.content.ContextWrapper;
2323
import android.media.MediaRouter;
24+
import android.media.MediaRouter.RouteInfo;
2425
import android.util.Log;
2526
import android.view.ActionProvider;
2627
import android.view.MenuItem;
2728
import android.view.View;
2829

30+
import java.lang.ref.WeakReference;
31+
2932
public class MediaRouteActionProvider extends ActionProvider {
3033
private static final String TAG = "MediaRouteActionProvider";
3134

@@ -35,11 +38,13 @@ public class MediaRouteActionProvider extends ActionProvider {
3538
private MediaRouteButton mView;
3639
private int mRouteTypes;
3740
private View.OnClickListener mExtendedSettingsListener;
41+
private RouterCallback mCallback;
3842

3943
public MediaRouteActionProvider(Context context) {
4044
super(context);
4145
mContext = context;
4246
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
47+
mCallback = new RouterCallback(this);
4348

4449
// Start with live audio by default.
4550
// TODO Update this when new route types are added; segment by API level
@@ -48,7 +53,14 @@ public MediaRouteActionProvider(Context context) {
4853
}
4954

5055
public void setRouteTypes(int types) {
56+
if (mRouteTypes == types) return;
57+
if (mRouteTypes != 0) {
58+
mRouter.removeCallback(mCallback);
59+
}
5160
mRouteTypes = types;
61+
if (types != 0) {
62+
mRouter.addCallback(types, mCallback);
63+
}
5264
if (mView != null) {
5365
mView.setRouteTypes(mRouteTypes);
5466
}
@@ -68,7 +80,6 @@ public View onCreateActionView(MenuItem item) {
6880
}
6981
mMenuItem = item;
7082
mView = new MediaRouteButton(mContext);
71-
mMenuItem.setVisible(mRouter.getRouteCount() > 1);
7283
mView.setRouteTypes(mRouteTypes);
7384
mView.setExtendedSettingsClickListener(mExtendedSettingsListener);
7485
return mView;
@@ -123,4 +134,34 @@ public boolean overridesItemVisibility() {
123134
public boolean isVisible() {
124135
return mRouter.getRouteCount() > 1;
125136
}
137+
138+
private static class RouterCallback extends MediaRouter.SimpleCallback {
139+
private WeakReference<MediaRouteActionProvider> mAp;
140+
141+
RouterCallback(MediaRouteActionProvider ap) {
142+
mAp = new WeakReference<MediaRouteActionProvider>(ap);
143+
}
144+
145+
@Override
146+
public void onRouteAdded(MediaRouter router, RouteInfo info) {
147+
final MediaRouteActionProvider ap = mAp.get();
148+
if (ap == null) {
149+
router.removeCallback(this);
150+
return;
151+
}
152+
153+
ap.refreshVisibility();
154+
}
155+
156+
@Override
157+
public void onRouteRemoved(MediaRouter router, RouteInfo info) {
158+
final MediaRouteActionProvider ap = mAp.get();
159+
if (ap == null) {
160+
router.removeCallback(this);
161+
return;
162+
}
163+
164+
ap.refreshVisibility();
165+
}
166+
}
126167
}

core/java/android/view/ActionProvider.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package android.view;
1818

1919
import android.content.Context;
20+
import android.util.Log;
2021

2122
/**
2223
* An ActionProvider defines rich menu interaction in a single component.
@@ -55,7 +56,9 @@
5556
* @see MenuItem#getActionProvider()
5657
*/
5758
public abstract class ActionProvider {
59+
private static final String TAG = "ActionProvider";
5860
private SubUiVisibilityListener mSubUiVisibilityListener;
61+
private VisibilityListener mVisibilityListener;
5962

6063
/**
6164
* Creates a new instance. ActionProvider classes should always implement a
@@ -121,6 +124,18 @@ public boolean isVisible() {
121124
return true;
122125
}
123126

127+
/**
128+
* If this ActionProvider is associated with an item in a menu,
129+
* refresh the visibility of the item based on {@link #overridesItemVisibility()} and
130+
* {@link #isVisible()}. If {@link #overridesItemVisibility()} returns false, this call
131+
* will have no effect.
132+
*/
133+
public void refreshVisibility() {
134+
if (mVisibilityListener != null && overridesItemVisibility()) {
135+
mVisibilityListener.onActionProviderVisibilityChanged(isVisible());
136+
}
137+
}
138+
124139
/**
125140
* Performs an optional default action.
126141
* <p>
@@ -206,10 +221,35 @@ public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
206221
mSubUiVisibilityListener = listener;
207222
}
208223

224+
/**
225+
* Set a listener to be notified when this ActionProvider's overridden visibility changes.
226+
* This should only be used by MenuItem implementations.
227+
*
228+
* @param listener listener to set
229+
*/
230+
public void setVisibilityListener(VisibilityListener listener) {
231+
if (mVisibilityListener != null) {
232+
Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
233+
"when one is already set. Are you reusing this " + getClass().getSimpleName() +
234+
" instance while it is still in use somewhere else?");
235+
}
236+
mVisibilityListener = listener;
237+
}
238+
209239
/**
210240
* @hide Internal use only
211241
*/
212242
public interface SubUiVisibilityListener {
213243
public void onSubUiVisibilityChanged(boolean isVisible);
214244
}
245+
246+
/**
247+
* Listens to changes in visibility as reported by {@link ActionProvider#refreshVisibility()}.
248+
*
249+
* @see ActionProvider#overridesItemVisibility()
250+
* @see ActionProvider#isVisible()
251+
*/
252+
public interface VisibilityListener {
253+
public void onActionProviderVisibilityChanged(boolean isVisible);
254+
}
215255
}

core/java/com/android/internal/view/menu/MenuItemImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,9 +589,17 @@ public ActionProvider getActionProvider() {
589589
}
590590

591591
public MenuItem setActionProvider(ActionProvider actionProvider) {
592+
if (mActionProvider != null) {
593+
mActionProvider.setVisibilityListener(null);
594+
}
592595
mActionView = null;
593596
mActionProvider = actionProvider;
594597
mMenu.onItemsChanged(true); // Measurement can be changed
598+
mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
599+
@Override public void onActionProviderVisibilityChanged(boolean isVisible) {
600+
mMenu.onItemVisibleChanged(MenuItemImpl.this);
601+
}
602+
});
595603
return this;
596604
}
597605

media/java/android/media/MediaRouter.java

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.ArrayList;
3030
import java.util.HashMap;
3131
import java.util.List;
32+
import java.util.concurrent.CopyOnWriteArrayList;
3233

3334
/**
3435
* MediaRouter allows applications to control the routing of media channels
@@ -48,7 +49,8 @@ static class Static {
4849
final Resources mResources;
4950
final IAudioService mAudioService;
5051
final Handler mHandler;
51-
final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
52+
final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
53+
new CopyOnWriteArrayList<CallbackInfo>();
5254

5355
final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
5456
final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
@@ -497,69 +499,55 @@ static void updateRoute(final RouteInfo info) {
497499
}
498500

499501
static void dispatchRouteSelected(int type, RouteInfo info) {
500-
final int count = sStatic.mCallbacks.size();
501-
for (int i = 0; i < count; i++) {
502-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
502+
for (CallbackInfo cbi : sStatic.mCallbacks) {
503503
if ((cbi.type & type) != 0) {
504504
cbi.cb.onRouteSelected(cbi.router, type, info);
505505
}
506506
}
507507
}
508508

509509
static void dispatchRouteUnselected(int type, RouteInfo info) {
510-
final int count = sStatic.mCallbacks.size();
511-
for (int i = 0; i < count; i++) {
512-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
510+
for (CallbackInfo cbi : sStatic.mCallbacks) {
513511
if ((cbi.type & type) != 0) {
514512
cbi.cb.onRouteUnselected(cbi.router, type, info);
515513
}
516514
}
517515
}
518516

519517
static void dispatchRouteChanged(RouteInfo info) {
520-
final int count = sStatic.mCallbacks.size();
521-
for (int i = 0; i < count; i++) {
522-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
518+
for (CallbackInfo cbi : sStatic.mCallbacks) {
523519
if ((cbi.type & info.mSupportedTypes) != 0) {
524520
cbi.cb.onRouteChanged(cbi.router, info);
525521
}
526522
}
527523
}
528524

529525
static void dispatchRouteAdded(RouteInfo info) {
530-
final int count = sStatic.mCallbacks.size();
531-
for (int i = 0; i < count; i++) {
532-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
526+
for (CallbackInfo cbi : sStatic.mCallbacks) {
533527
if ((cbi.type & info.mSupportedTypes) != 0) {
534528
cbi.cb.onRouteAdded(cbi.router, info);
535529
}
536530
}
537531
}
538532

539533
static void dispatchRouteRemoved(RouteInfo info) {
540-
final int count = sStatic.mCallbacks.size();
541-
for (int i = 0; i < count; i++) {
542-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
534+
for (CallbackInfo cbi : sStatic.mCallbacks) {
543535
if ((cbi.type & info.mSupportedTypes) != 0) {
544536
cbi.cb.onRouteRemoved(cbi.router, info);
545537
}
546538
}
547539
}
548540

549541
static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
550-
final int count = sStatic.mCallbacks.size();
551-
for (int i = 0; i < count; i++) {
552-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
542+
for (CallbackInfo cbi : sStatic.mCallbacks) {
553543
if ((cbi.type & group.mSupportedTypes) != 0) {
554544
cbi.cb.onRouteGrouped(cbi.router, info, group, index);
555545
}
556546
}
557547
}
558548

559549
static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
560-
final int count = sStatic.mCallbacks.size();
561-
for (int i = 0; i < count; i++) {
562-
final CallbackInfo cbi = sStatic.mCallbacks.get(i);
550+
for (CallbackInfo cbi : sStatic.mCallbacks) {
563551
if ((cbi.type & group.mSupportedTypes) != 0) {
564552
cbi.cb.onRouteUngrouped(cbi.router, info, group);
565553
}

0 commit comments

Comments
 (0)