Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6452d0c authored by Adam Powell's avatar Adam Powell Committed by Android (Google) Code Review
Browse files

Merge "More fun with MediaRouter" into jb-dev

parents 615fd3df 690ffb4e
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -675,6 +675,7 @@ package android {
    field public static final int maxWidth = 16843039; // 0x101011f
    field public static final int measureAllChildren = 16843018; // 0x101010a
    field public static final int measureWithLargestChild = 16843476; // 0x10102d4
    field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
    field public static final int menuCategory = 16843230; // 0x10101de
    field public static final int mimeType = 16842790; // 0x1010026
    field public static final int minDate = 16843583; // 0x101033f
@@ -1789,6 +1790,7 @@ package android {
    field public static final int Widget_DeviceDefault_Light_ListPopupWindow = 16974235; // 0x103019b
    field public static final int Widget_DeviceDefault_Light_ListView = 16974210; // 0x1030182
    field public static final int Widget_DeviceDefault_Light_ListView_DropDown = 16974205; // 0x103017d
    field public static final int Widget_DeviceDefault_Light_MediaRouteButton = 16974296; // 0x10301d8
    field public static final int Widget_DeviceDefault_Light_PopupMenu = 16974236; // 0x103019c
    field public static final int Widget_DeviceDefault_Light_PopupWindow = 16974211; // 0x1030183
    field public static final int Widget_DeviceDefault_Light_ProgressBar = 16974212; // 0x1030184
@@ -1814,6 +1816,7 @@ package android {
    field public static final int Widget_DeviceDefault_ListPopupWindow = 16974180; // 0x1030164
    field public static final int Widget_DeviceDefault_ListView = 16974158; // 0x103014e
    field public static final int Widget_DeviceDefault_ListView_DropDown = 16974153; // 0x1030149
    field public static final int Widget_DeviceDefault_MediaRouteButton = 16974295; // 0x10301d7
    field public static final int Widget_DeviceDefault_PopupMenu = 16974181; // 0x1030165
    field public static final int Widget_DeviceDefault_PopupWindow = 16974159; // 0x103014f
    field public static final int Widget_DeviceDefault_ProgressBar = 16974160; // 0x1030150
@@ -1905,6 +1908,7 @@ package android {
    field public static final int Widget_Holo_Light_ListPopupWindow = 16974043; // 0x10300db
    field public static final int Widget_Holo_Light_ListView = 16974018; // 0x10300c2
    field public static final int Widget_Holo_Light_ListView_DropDown = 16974013; // 0x10300bd
    field public static final int Widget_Holo_Light_MediaRouteButton = 16974294; // 0x10301d6
    field public static final int Widget_Holo_Light_PopupMenu = 16974044; // 0x10300dc
    field public static final int Widget_Holo_Light_PopupWindow = 16974019; // 0x10300c3
    field public static final int Widget_Holo_Light_ProgressBar = 16974020; // 0x10300c4
@@ -1930,6 +1934,7 @@ package android {
    field public static final int Widget_Holo_ListPopupWindow = 16973997; // 0x10300ad
    field public static final int Widget_Holo_ListView = 16973975; // 0x1030097
    field public static final int Widget_Holo_ListView_DropDown = 16973970; // 0x1030092
    field public static final int Widget_Holo_MediaRouteButton = 16974293; // 0x10301d5
    field public static final int Widget_Holo_PopupMenu = 16973998; // 0x10300ae
    field public static final int Widget_Holo_PopupWindow = 16973976; // 0x1030098
    field public static final int Widget_Holo_ProgressBar = 16973977; // 0x1030099
@@ -3671,6 +3676,20 @@ package android.app {
    method public android.view.Window startActivity(java.lang.String, android.content.Intent);
  }
  public class MediaRouteActionProvider extends android.view.ActionProvider {
    ctor public MediaRouteActionProvider(android.content.Context);
    method public android.view.View onCreateActionView();
    method public void setRouteTypes(int);
  }
  public class MediaRouteButton extends android.view.View {
    ctor public MediaRouteButton(android.content.Context);
    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
    ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
    method public int getRouteTypes();
    method public void setRouteTypes(int);
  }
  public class NativeActivity extends android.app.Activity implements android.view.InputQueue.Callback android.view.SurfaceHolder.Callback2 android.view.ViewTreeObserver.OnGlobalLayoutListener {
    ctor public NativeActivity();
    method public void onGlobalLayout();
@@ -11489,6 +11508,7 @@ package android.media {
  public class MediaRouter {
    method public void addCallback(int, android.media.MediaRouter.Callback);
    method public void addUserRoute(android.media.MediaRouter.UserRouteInfo);
    method public void clearUserRoutes();
    method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
    method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
    method public static android.media.MediaRouter forApplication(android.content.Context);
@@ -11496,10 +11516,11 @@ package android.media {
    method public int getCategoryCount();
    method public android.media.MediaRouter.RouteInfo getRouteAt(int);
    method public int getRouteCount();
    method public android.media.MediaRouter.RouteInfo getSelectedRoute(int);
    method public void removeCallback(android.media.MediaRouter.Callback);
    method public void removeUserRoute(android.media.MediaRouter.UserRouteInfo);
    method public void selectRoute(int, android.media.MediaRouter.RouteInfo);
    method public void setRouteVolume(int, float);
    method public void setSelectedRouteVolume(int, float);
    field public static final int ROUTE_TYPE_LIVE_AUDIO = 1; // 0x1
    field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
  }
@@ -11534,6 +11555,7 @@ package android.media {
    method public java.lang.CharSequence getName();
    method public java.lang.CharSequence getStatus();
    method public int getSupportedTypes();
    method public float getVolume();
  }
  public static class MediaRouter.SimpleCallback implements android.media.MediaRouter.Callback {
@@ -22775,7 +22797,8 @@ package android.view {
  public abstract class ActionProvider {
    ctor public ActionProvider(android.content.Context);
    method public boolean hasSubMenu();
    method public abstract android.view.View onCreateActionView();
    method public abstract deprecated android.view.View onCreateActionView();
    method public android.view.View onCreateActionView(android.view.MenuItem);
    method public boolean onPerformDefaultAction();
    method public void onPrepareSubMenu(android.view.SubMenu);
  }
+99 −0
Original line number Diff line number Diff line
/*
 * 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;

public class MediaRouteActionProvider extends ActionProvider {
    private static final String TAG = "MediaRouteActionProvider";

    private Context mContext;
    private MediaRouter mRouter;
    private MenuItem mMenuItem;
    private MediaRouteButton mView;
    private int mRouteTypes;
    private final RouterCallback mRouterCallback = new RouterCallback();

    public MediaRouteActionProvider(Context context) {
        super(context);
        mContext = context;
        mRouter = MediaRouter.forApplication(context);

        // 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);
    }

    public void setRouteTypes(int types) {
        if (types == mRouteTypes) {
            // Already registered; nothing to do.
            return;
        }
        if (mRouteTypes != 0) {
            mRouter.removeCallback(mRouterCallback);
        }
        mRouteTypes = types;
        if (mView != null) {
            mView.setRouteTypes(mRouteTypes);
        }
        mRouter.addCallback(types, mRouterCallback);
    }

    @Override
    public View onCreateActionView() {
        throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
    }

    @Override
    public View onCreateActionView(MenuItem item) {
        if (mMenuItem != null || mView != null) {
            Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
                    "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
                    "Abandoning the old one...");
        }
        mMenuItem = item;
        mView = new MediaRouteButton(mContext);
        mMenuItem.setVisible(mRouter.getRouteCount() > 1);
        mView.setRouteTypes(mRouteTypes);
        return mView;
    }

    @Override
    public boolean onPerformDefaultAction() {
        // Show routing dialog
        return true;
    }

    private class RouterCallback extends MediaRouter.SimpleCallback {
        @Override
        public void onRouteAdded(int type, RouteInfo info) {
            mMenuItem.setVisible(mRouter.getRouteCount() > 1);
        }

        @Override
        public void onRouteRemoved(int type, RouteInfo info) {
            mMenuItem.setVisible(mRouter.getRouteCount() > 1);
        }
    }
}
+284 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.internal.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SoundEffectConstants;
import android.view.View;

public class MediaRouteButton extends View {
    private static final String TAG = "MediaRouteButton";

    private MediaRouter mRouter;
    private final MediaRouteCallback mRouterCallback = new MediaRouteCallback();
    private int mRouteTypes;

    private Drawable mRemoteIndicator;
    private boolean mRemoteActive;
    private boolean mToggleMode;

    private int mMinWidth;
    private int mMinHeight;

    private static final int[] ACTIVATED_STATE_SET = {
        R.attr.state_activated
    };

    public MediaRouteButton(Context context) {
        this(context, null);
    }

    public MediaRouteButton(Context context, AttributeSet attrs) {
        this(context, null, com.android.internal.R.attr.mediaRouteButtonStyle);
    }

    public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mRouter = MediaRouter.forApplication(context);

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
        setRemoteIndicatorDrawable(a.getDrawable(
                com.android.internal.R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
        mMinWidth = a.getDimensionPixelSize(
                com.android.internal.R.styleable.MediaRouteButton_minWidth, 0);
        mMinHeight = a.getDimensionPixelSize(
                com.android.internal.R.styleable.MediaRouteButton_minHeight, 0);
        a.recycle();

        setClickable(true);
    }

    private void setRemoteIndicatorDrawable(Drawable d) {
        if (mRemoteIndicator != null) {
            mRemoteIndicator.setCallback(null);
            unscheduleDrawable(mRemoteIndicator);
        }
        mRemoteIndicator = d;
        if (d != null) {
            d.setCallback(this);
            d.setState(getDrawableState());
            d.setVisible(getVisibility() == VISIBLE, false);
        }

        refreshDrawableState();
    }

    @Override
    public boolean performClick() {
        // Send the appropriate accessibility events and call listeners
        boolean handled = super.performClick();
        if (!handled) {
            playSoundEffect(SoundEffectConstants.CLICK);
        }

        if (mToggleMode) {
            if (mRemoteActive) {
                mRouter.selectRoute(mRouteTypes, mRouter.getSystemAudioRoute());
            } else {
                final int N = mRouter.getRouteCount();
                for (int i = 0; i < N; i++) {
                    final RouteInfo route = mRouter.getRouteAt(i);
                    if ((route.getSupportedTypes() & mRouteTypes) != 0 &&
                            route != mRouter.getSystemAudioRoute()) {
                        mRouter.selectRoute(mRouteTypes, route);
                    }
                }
            }
        } else {
            Log.d(TAG, "TODO: Implement the dialog!");
        }

        return handled;
    }

    public void setRouteTypes(int types) {
        if (types == mRouteTypes) {
            // Already registered; nothing to do.
            return;
        }
        if (mRouteTypes != 0) {
            mRouter.removeCallback(mRouterCallback);
        }
        mRouteTypes = types;
        updateRemoteIndicator();
        updateRouteCount();
        mRouter.addCallback(types, mRouterCallback);
    }

    public int getRouteTypes() {
        return mRouteTypes;
    }

    void updateRemoteIndicator() {
        final boolean isRemote =
                mRouter.getSelectedRoute(mRouteTypes) != mRouter.getSystemAudioRoute();
        if (mRemoteActive != isRemote) {
            mRemoteActive = isRemote;
            refreshDrawableState();
        }
    }

    void updateRouteCount() {
        final int N = mRouter.getRouteCount();
        int count = 0;
        for (int i = 0; i < N; i++) {
            if ((mRouter.getRouteAt(i).getSupportedTypes() & mRouteTypes) != 0) {
                count++;
            }
        }

        setEnabled(count != 0);

        // Only allow toggling if we have more than just user routes
        mToggleMode = count == 2 && (mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_AUDIO) != 0;
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (mRemoteActive) {
            mergeDrawableStates(drawableState, ACTIVATED_STATE_SET);
        }
        return drawableState;
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        if (mRemoteIndicator != null) {
            int[] myDrawableState = getDrawableState();
            mRemoteIndicator.setState(myDrawableState);
            invalidate();
        }
    }

    @Override
    protected boolean verifyDrawable(Drawable who) {
        return super.verifyDrawable(who) || who == mRemoteIndicator;
    }

    @Override
    public void jumpDrawablesToCurrentState() {
        super.jumpDrawablesToCurrentState();
        if (mRemoteIndicator != null) mRemoteIndicator.jumpToCurrentState();
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (mRemoteIndicator != null) {
            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        final int minWidth = Math.max(mMinWidth,
                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicWidth() : 0);
        final int minHeight = Math.max(mMinHeight,
                mRemoteIndicator != null ? mRemoteIndicator.getIntrinsicHeight() : 0);

        int width;
        switch (widthMode) {
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case MeasureSpec.AT_MOST:
                width = Math.min(widthSize, minWidth + getPaddingLeft() + getPaddingRight());
                break;
            default:
            case MeasureSpec.UNSPECIFIED:
                width = minWidth + getPaddingLeft() + getPaddingRight();
                break;
        }

        int height;
        switch (heightMode) {
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case MeasureSpec.AT_MOST:
                height = Math.min(heightSize, minHeight + getPaddingTop() + getPaddingBottom());
                break;
            default:
            case MeasureSpec.UNSPECIFIED:
                height = minHeight + getPaddingTop() + getPaddingBottom();
                break;
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mRemoteIndicator == null) return;

        final int left = getPaddingLeft();
        final int right = getWidth() - getPaddingRight();
        final int top = getPaddingTop();
        final int bottom = getHeight() - getPaddingBottom();

        final int drawWidth = mRemoteIndicator.getIntrinsicWidth();
        final int drawHeight = mRemoteIndicator.getIntrinsicHeight();
        final int drawLeft = left + (right - left - drawWidth) / 2;
        final int drawTop = top + (bottom - top - drawHeight) / 2;

        mRemoteIndicator.setBounds(drawLeft, drawTop, drawLeft + drawWidth, drawTop + drawHeight);
        mRemoteIndicator.draw(canvas);
    }

    private class MediaRouteCallback extends MediaRouter.SimpleCallback {
        @Override
        public void onRouteSelected(int type, RouteInfo info) {
            updateRemoteIndicator();
        }

        @Override
        public void onRouteUnselected(int type, RouteInfo info) {
            updateRemoteIndicator();
        }

        @Override
        public void onRouteAdded(int type, RouteInfo info) {
            updateRouteCount();
        }

        @Override
        public void onRouteRemoved(int type, RouteInfo info) {
            updateRouteCount();
        }
    }
}
+25 −2
Original line number Diff line number Diff line
@@ -58,7 +58,8 @@ public abstract class ActionProvider {
    private SubUiVisibilityListener mSubUiVisibilityListener;

    /**
     * Creates a new instance.
     * Creates a new instance. ActionProvider classes should always implement a
     * constructor that takes a single Context parameter for inflating from menu XML.
     *
     * @param context Context for accessing resources.
     */
@@ -66,12 +67,34 @@ public abstract class ActionProvider {
    }

    /**
     * Factory method for creating new action views.
     * Factory method called by the Android framework to create new action views.
     *
     * <p>This method has been deprecated in favor of {@link #onCreateActionView(MenuItem)}.
     * Newer apps that wish to support platform versions prior to API 16 should also
     * implement this method to return a valid action view.</p>
     *
     * @return A new action view.
     *
     * @deprecated use {@link #onCreateActionView(MenuItem)}
     */
    public abstract View onCreateActionView();

    /**
     * Factory method called by the Android framework to create new action views.
     * This method returns a new action view for the given MenuItem.
     *
     * <p>If your ActionProvider implementation overrides the deprecated no-argument overload
     * {@link #onCreateActionView()}, overriding this method for devices running API 16 or later
     * is recommended but optional. The default implementation calls {@link #onCreateActionView()}
     * for compatibility with applications written for older platform versions.</p>
     *
     * @param forItem MenuItem to create the action view for
     * @return the new action view
     */
    public View onCreateActionView(MenuItem forItem) {
        return onCreateActionView();
    }

    /**
     * Performs an optional default action.
     * <p>
+1 −1
Original line number Diff line number Diff line
@@ -574,7 +574,7 @@ public final class MenuItemImpl implements MenuItem {
        if (mActionView != null) {
            return mActionView;
        } else if (mActionProvider != null) {
            mActionView = mActionProvider.onCreateActionView();
            mActionView = mActionProvider.onCreateActionView(this);
            return mActionView;
        } else {
            return null;
Loading