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

Commit 45996965 authored by Adam Powell's avatar Adam Powell
Browse files

Volume control in MediaRouter dialogs

MediaRouter dialogs now intercept the volume keys for altering the
current volume. The status icon indicates if the slider/buttons are
currently controlling the local device volume or a remote device's
volume.

Group volume for user routes is handled by using the
RemoteControlClient supplied by the first route in the group.

Change-Id: I40a0d054847ed5acce7a4c3b669487841b4dca15
parent 39d5c617
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11582,6 +11582,7 @@ package android.media {
  }
  public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
    method public android.media.RemoteControlClient getRemoteControlClient();
    method public void setIconDrawable(android.graphics.drawable.Drawable);
    method public void setIconResource(int);
    method public void setName(java.lang.CharSequence);
+141 −47
Original line number Diff line number Diff line
@@ -25,12 +25,16 @@ import android.app.MediaRouteActionProvider;
import android.app.MediaRouteButton;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteCategory;
import android.media.MediaRouter.RouteGroup;
import android.media.MediaRouter.RouteInfo;
import android.media.MediaRouter.UserRouteInfo;
import android.media.RemoteControlClient;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,6 +45,7 @@ import android.widget.Checkable;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;

import java.util.ArrayList;
@@ -67,6 +72,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
    };

    MediaRouter mRouter;
    AudioManager mAudio;
    private int mRouteTypes;

    private LayoutInflater mInflater;
@@ -74,8 +80,12 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
    private View.OnClickListener mExtendedSettingsListener;
    private RouteAdapter mAdapter;
    private ListView mListView;
    private SeekBar mVolumeSlider;
    private ImageView mVolumeIcon;

    final RouteComparator mComparator = new RouteComparator();
    final MediaRouterCallback mCallback = new MediaRouterCallback();
    private boolean mIgnoreVolumeChanges;

    public MediaRouteChooserDialogFragment() {
        setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
@@ -89,6 +99,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
        mAudio = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
    }

    @Override
@@ -98,20 +109,13 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
            mLauncherListener.onDetached(this);
        }
        if (mAdapter != null) {
            mRouter.removeCallback(mAdapter.mCallback);
            mAdapter = null;
        }
        mInflater = null;
        mRouter.removeCallback(mCallback);
        mRouter = null;
    }

    /**
     * Implemented by the MediaRouteButton that launched this dialog
     */
    public interface LauncherListener {
        public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
    }

    public void setExtendedSettingsClickListener(View.OnClickListener listener) {
        mExtendedSettingsListener = listener;
    }
@@ -120,14 +124,70 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
        mRouteTypes = types;
    }

    void updateVolume() {
        final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
        final boolean defaultAudioSelected = selectedRoute == mRouter.getSystemAudioRoute();
        final boolean selectedSystemRoute =
                selectedRoute.getCategory() == mRouter.getSystemAudioCategory();
        mVolumeIcon.setImageResource(defaultAudioSelected ?
                R.drawable.ic_audio_vol : R.drawable.ic_media_route_on_holo_dark);

        mIgnoreVolumeChanges = true;
        mVolumeSlider.setEnabled(true);
        if (selectedSystemRoute) {
            // Use the standard media audio stream
            mVolumeSlider.setMax(mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
            mVolumeSlider.setProgress(mAudio.getStreamVolume(AudioManager.STREAM_MUSIC));
        } else {
            final RouteInfo firstSelected;
            if (selectedRoute instanceof RouteGroup) {
                firstSelected = ((RouteGroup) selectedRoute).getRouteAt(0);
            } else {
                firstSelected = selectedRoute;
            }

            RemoteControlClient rcc = null;
            if (firstSelected instanceof UserRouteInfo) {
                rcc = ((UserRouteInfo) firstSelected).getRemoteControlClient();
            }

            if (rcc == null) {
                // No RemoteControlClient? Assume volume can't be controlled.
                // Disable the slider and show it at max volume.
                mVolumeSlider.setMax(1);
                mVolumeSlider.setProgress(1);
                mVolumeSlider.setEnabled(false);
            } else {
                // TODO: Connect this to the remote control volume
            }
        }
        mIgnoreVolumeChanges = false;
    }

    void changeVolume(int newValue) {
        if (mIgnoreVolumeChanges) return;

        RouteCategory selectedCategory = mRouter.getSelectedRoute(mRouteTypes).getCategory();
        if (selectedCategory == mRouter.getSystemAudioCategory()) {
            final int maxVolume = mAudio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            newValue = Math.max(0, Math.min(newValue, maxVolume));
            mAudio.setStreamVolume(AudioManager.STREAM_MUSIC, newValue, 0);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        mInflater = inflater;
        final View layout = inflater.inflate(R.layout.media_route_chooser_layout, container, false);
        final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);

        mVolumeIcon = (ImageView) layout.findViewById(R.id.volume_icon);
        mVolumeSlider = (SeekBar) layout.findViewById(R.id.volume_slider);
        updateVolume();
        mVolumeSlider.setOnSeekBarChangeListener(new VolumeSliderChangeListener());

        if (mExtendedSettingsListener != null) {
            final View extendedSettingsButton = layout.findViewById(R.id.extended_settings);
            extendedSettingsButton.setVisibility(View.VISIBLE);
            extendedSettingsButton.setOnClickListener(mExtendedSettingsListener);
        }
@@ -138,7 +198,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
        list.setOnItemClickListener(mAdapter);

        mListView = list;
        mRouter.addCallback(mRouteTypes, mAdapter.mCallback);
        mRouter.addCallback(mRouteTypes, mCallback);

        mAdapter.scrollToSelectedItem();

@@ -174,7 +234,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {

        private int mSelectedItemPosition = -1;
        private final ArrayList<Object> mItems = new ArrayList<Object>();
        final MediaRouterCallback mCallback = new MediaRouterCallback();

        private RouteCategory mCategoryEditingGroups;
        private RouteGroup mEditingGroup;
@@ -443,10 +502,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
            holder.text1.setText(cat.getName(getActivity()));
        }

        public int getSelectedRoutePosition() {
            return mSelectedItemPosition;
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final int type = getItemViewType(position);
@@ -523,46 +578,49 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
                scrollToEditingGroup();
            }
        }
    }

    class MediaRouterCallback extends MediaRouter.Callback {
        @Override
        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
                update();
            mAdapter.update();
            updateVolume();
        }

        @Override
        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
                update();
            mAdapter.update();
        }

        @Override
        public void onRouteAdded(MediaRouter router, RouteInfo info) {
                update();
            mAdapter.update();
            updateVolume();
        }

        @Override
        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
                if (info == mEditingGroup) {
                    finishGrouping();
            if (info == mAdapter.mEditingGroup) {
                mAdapter.finishGrouping();
            }
                update();
            mAdapter.update();
            updateVolume();
        }

        @Override
        public void onRouteChanged(MediaRouter router, RouteInfo info) {
                notifyDataSetChanged();
            mAdapter.notifyDataSetChanged();
        }

        @Override
        public void onRouteGrouped(MediaRouter router, RouteInfo info,
                RouteGroup group, int index) {
                update();
            mAdapter.update();
        }

        @Override
        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
                update();
            }
            mAdapter.update();
        }
    }

@@ -587,5 +645,41 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
                super.onBackPressed();
            }
        }
        
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mVolumeSlider.isEnabled()) {
                mVolumeSlider.incrementProgressBy(-1);
                return true;
            } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mVolumeSlider.isEnabled()) {
                mVolumeSlider.incrementProgressBy(1);
                return true;
            } else {
                return super.onKeyDown(keyCode, event);
            }
        }
    }

    /**
     * Implemented by the MediaRouteButton that launched this dialog
     */
    public interface LauncherListener {
        public void onDetached(MediaRouteChooserDialogFragment detachedFragment);
    }

    class VolumeSliderChangeListener implements SeekBar.OnSeekBarChangeListener {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            changeVolume(progress);
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }

    }
}
+3 −0
Original line number Diff line number Diff line
@@ -1168,6 +1168,9 @@
  <java-symbol type="attr" name="externalRouteEnabledDrawable" />
  <java-symbol type="id" name="extended_settings" />
  <java-symbol type="id" name="check" />
  <java-symbol type="id" name="volume_slider" />
  <java-symbol type="id" name="volume_icon" />
  <java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
  <java-symbol type="layout" name="media_route_chooser_layout" />
  <java-symbol type="layout" name="media_route_list_item_top_header" />
  <java-symbol type="layout" name="media_route_list_item_section_header" />
+17 −0
Original line number Diff line number Diff line
@@ -197,6 +197,13 @@ public class MediaRouter {
        return sStatic.mDefaultAudio;
    }

    /**
     * @hide for use by framework routing UI
     */
    public RouteCategory getSystemAudioCategory() {
        return sStatic.mSystemCategory;
    }

    /**
     * Return the currently selected route for the given types
     *
@@ -739,6 +746,16 @@ public class MediaRouter {
            mRcc = rcc;
        }

        /**
         * Retrieve the RemoteControlClient associated with this route, if one has been set.
         *
         * @return the RemoteControlClient associated with this route
         * @see #setRemoteControlClient(RemoteControlClient)
         */
        public RemoteControlClient getRemoteControlClient() {
            return mRcc;
        }

        /**
         * Set an icon that will be used to represent this route.
         * The system may use this icon in picker UIs or similar.