Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.support.v7.media;
     17 
     18 import android.content.Context;
     19 import android.media.AudioManager;
     20 import android.os.Build;
     21 
     22 import java.lang.ref.WeakReference;
     23 
     24 /**
     25  * Provides access to features of the remote control client.
     26  *
     27  * Hidden for now but we might want to make this available to applications
     28  * in the future.
     29  */
     30 abstract class RemoteControlClientCompat {
     31     protected final Context mContext;
     32     protected final Object mRcc;
     33     protected VolumeCallback mVolumeCallback;
     34 
     35     protected RemoteControlClientCompat(Context context, Object rcc) {
     36         mContext = context;
     37         mRcc = rcc;
     38     }
     39 
     40     public static RemoteControlClientCompat obtain(Context context, Object rcc) {
     41         if (Build.VERSION.SDK_INT >= 16) {
     42             return new JellybeanImpl(context, rcc);
     43         }
     44         return new LegacyImpl(context, rcc);
     45     }
     46 
     47     public Object getRemoteControlClient() {
     48         return mRcc;
     49     }
     50 
     51     /**
     52      * Sets the current playback information.
     53      * Must be called at least once to attach to the remote control client.
     54      *
     55      * @param info The playback information.  Must not be null.
     56      */
     57     public void setPlaybackInfo(PlaybackInfo info) {
     58     }
     59 
     60     /**
     61      * Sets a callback to receive volume change requests from the remote control client.
     62      *
     63      * @param callback The volume callback to use or null if none.
     64      */
     65     public void setVolumeCallback(VolumeCallback callback) {
     66         mVolumeCallback = callback;
     67     }
     68 
     69     /**
     70      * Specifies information about the playback.
     71      */
     72     public static final class PlaybackInfo {
     73         public int volume;
     74         public int volumeMax;
     75         public int volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
     76         public int playbackStream = AudioManager.STREAM_MUSIC;
     77         public int playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
     78     }
     79 
     80     /**
     81      * Called when volume updates are requested by the remote control client.
     82      */
     83     public interface VolumeCallback {
     84         /**
     85          * Called when the volume should be increased or decreased.
     86          *
     87          * @param direction An integer indicating whether the volume is to be increased
     88          * (positive value) or decreased (negative value).
     89          * For bundled changes, the absolute value indicates the number of changes
     90          * in the same direction, e.g. +3 corresponds to three "volume up" changes.
     91          */
     92         public void onVolumeUpdateRequest(int direction);
     93 
     94         /**
     95          * Called when the volume for the route should be set to the given value.
     96          *
     97          * @param volume An integer indicating the new volume value that should be used,
     98          * always between 0 and the value set by {@link PlaybackInfo#volumeMax}.
     99          */
    100         public void onVolumeSetRequest(int volume);
    101     }
    102 
    103     /**
    104      * Legacy implementation for platform versions prior to Jellybean.
    105      * Does nothing.
    106      */
    107     static class LegacyImpl extends RemoteControlClientCompat {
    108         public LegacyImpl(Context context, Object rcc) {
    109             super(context, rcc);
    110         }
    111     }
    112 
    113     /**
    114      * Implementation for Jellybean.
    115      *
    116      * The basic idea of this implementation is to attach the RCC to a UserRouteInfo
    117      * in order to hook up stream metadata and volume callbacks because there is no
    118      * other API available to do so in this platform version.  The UserRouteInfo itself
    119      * is not attached to the MediaRouter so it is transparent to the user.
    120      */
    121     static class JellybeanImpl extends RemoteControlClientCompat {
    122         private final Object mRouterObj;
    123         private final Object mUserRouteCategoryObj;
    124         private final Object mUserRouteObj;
    125         private boolean mRegistered;
    126 
    127         public JellybeanImpl(Context context, Object rcc) {
    128             super(context, rcc);
    129 
    130             mRouterObj = MediaRouterJellybean.getMediaRouter(context);
    131             mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
    132                     mRouterObj, "", false);
    133             mUserRouteObj = MediaRouterJellybean.createUserRoute(
    134                     mRouterObj, mUserRouteCategoryObj);
    135         }
    136 
    137         @Override
    138         public void setPlaybackInfo(PlaybackInfo info) {
    139             MediaRouterJellybean.UserRouteInfo.setVolume(
    140                     mUserRouteObj, info.volume);
    141             MediaRouterJellybean.UserRouteInfo.setVolumeMax(
    142                     mUserRouteObj, info.volumeMax);
    143             MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
    144                     mUserRouteObj, info.volumeHandling);
    145             MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
    146                     mUserRouteObj, info.playbackStream);
    147             MediaRouterJellybean.UserRouteInfo.setPlaybackType(
    148                     mUserRouteObj, info.playbackType);
    149 
    150             if (!mRegistered) {
    151                 mRegistered = true;
    152                 MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj,
    153                         MediaRouterJellybean.createVolumeCallback(
    154                                 new VolumeCallbackWrapper(this)));
    155                 MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc);
    156             }
    157         }
    158 
    159         private static final class VolumeCallbackWrapper
    160                 implements MediaRouterJellybean.VolumeCallback {
    161             // Unfortunately, the framework never unregisters its volume observer from
    162             // the audio service so the UserRouteInfo object may leak along with
    163             // any callbacks that we attach to it.  Use a weak reference to prevent
    164             // the volume callback from holding strong references to anything important.
    165             private final WeakReference<JellybeanImpl> mImplWeak;
    166 
    167             public VolumeCallbackWrapper(JellybeanImpl impl) {
    168                 mImplWeak = new WeakReference<JellybeanImpl>(impl);
    169             }
    170 
    171             @Override
    172             public void onVolumeUpdateRequest(Object routeObj, int direction) {
    173                 JellybeanImpl impl = mImplWeak.get();
    174                 if (impl != null && impl.mVolumeCallback != null) {
    175                     impl.mVolumeCallback.onVolumeUpdateRequest(direction);
    176                 }
    177             }
    178 
    179             @Override
    180             public void onVolumeSetRequest(Object routeObj, int volume) {
    181                 JellybeanImpl impl = mImplWeak.get();
    182                 if (impl != null && impl.mVolumeCallback != null) {
    183                     impl.mVolumeCallback.onVolumeSetRequest(volume);
    184                 }
    185             }
    186         }
    187     }
    188 }
    189