1567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown/* 2567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Copyright (C) 2013 The Android Open Source Project 3567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 4567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * you may not use this file except in compliance with the License. 6567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * You may obtain a copy of the License at 7567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 8567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 10567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Unless required by applicable law or agreed to in writing, software 11567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * See the License for the specific language governing permissions and 14567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * limitations under the License. 15567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 16567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownpackage android.support.v7.media; 17567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 18567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownimport android.content.Context; 19567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownimport android.media.AudioManager; 20567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownimport android.os.Build; 21567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 22567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownimport java.lang.ref.WeakReference; 23567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 24567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown/** 25567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Provides access to features of the remote control client. 26567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 27567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Hidden for now but we might want to make this available to applications 28567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * in the future. 29567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 30567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brownabstract class RemoteControlClientCompat { 31567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown protected final Context mContext; 32567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown protected final Object mRcc; 33567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown protected VolumeCallback mVolumeCallback; 34567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 35567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown protected RemoteControlClientCompat(Context context, Object rcc) { 36567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mContext = context; 37567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRcc = rcc; 38567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 39567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 40567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public static RemoteControlClientCompat obtain(Context context, Object rcc) { 41567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (Build.VERSION.SDK_INT >= 16) { 42567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return new JellybeanImpl(context, rcc); 43567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 44567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return new LegacyImpl(context, rcc); 45567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 46567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 47567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public Object getRemoteControlClient() { 48567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return mRcc; 49567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 50567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 51567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 52567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Sets the current playback information. 53567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Must be called at least once to attach to the remote control client. 54567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 55567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param info The playback information. Must not be null. 56567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 57567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void setPlaybackInfo(PlaybackInfo info) { 58567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 59567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 60567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 61567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Sets a callback to receive volume change requests from the remote control client. 62567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 63567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param callback The volume callback to use or null if none. 64567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 65567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void setVolumeCallback(VolumeCallback callback) { 66567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mVolumeCallback = callback; 67567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 68567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 69567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 70567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Specifies information about the playback. 71567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 72567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public static final class PlaybackInfo { 73567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public int volume; 74567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public int volumeMax; 75567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public int volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED; 76567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public int playbackStream = AudioManager.STREAM_MUSIC; 77567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public int playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE; 78567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 79567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 80567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 81567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Called when volume updates are requested by the remote control client. 82567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 83567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public interface VolumeCallback { 84567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 85567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Called when the volume should be increased or decreased. 86567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 87567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param direction An integer indicating whether the volume is to be increased 88567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * (positive value) or decreased (negative value). 89567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * For bundled changes, the absolute value indicates the number of changes 90567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * in the same direction, e.g. +3 corresponds to three "volume up" changes. 91567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 92567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(int direction); 93567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 94567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 95567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Called when the volume for the route should be set to the given value. 96567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 97567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param volume An integer indicating the new volume value that should be used, 98567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * always between 0 and the value set by {@link PlaybackInfo#volumeMax}. 99567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 100567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(int volume); 101567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 102567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 103567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 104567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Legacy implementation for platform versions prior to Jellybean. 105567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Does nothing. 106567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 107567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown static class LegacyImpl extends RemoteControlClientCompat { 108567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public LegacyImpl(Context context, Object rcc) { 109567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown super(context, rcc); 110567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 111567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 112567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 113567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 114567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Implementation for Jellybean. 115567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 116567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * The basic idea of this implementation is to attach the RCC to a UserRouteInfo 117567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * in order to hook up stream metadata and volume callbacks because there is no 118567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * other API available to do so in this platform version. The UserRouteInfo itself 119567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * is not attached to the MediaRouter so it is transparent to the user. 120567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 121567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown static class JellybeanImpl extends RemoteControlClientCompat { 122567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final Object mRouterObj; 123567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final Object mUserRouteCategoryObj; 124567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final Object mUserRouteObj; 125567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private boolean mRegistered; 126567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 127567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public JellybeanImpl(Context context, Object rcc) { 128567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown super(context, rcc); 129567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 130567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRouterObj = MediaRouterJellybean.getMediaRouter(context); 131567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory( 132567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRouterObj, "", false); 133567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj = MediaRouterJellybean.createUserRoute( 134567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRouterObj, mUserRouteCategoryObj); 135567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 136567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 137567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 138567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void setPlaybackInfo(PlaybackInfo info) { 139567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolume( 140567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj, info.volume); 141567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeMax( 142567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj, info.volumeMax); 143567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeHandling( 144567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj, info.volumeHandling); 145567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setPlaybackStream( 146567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj, info.playbackStream); 147567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setPlaybackType( 148567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mUserRouteObj, info.playbackType); 149567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 150567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mRegistered) { 151567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRegistered = true; 152567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj, 153567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.createVolumeCallback( 154567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new VolumeCallbackWrapper(this))); 155567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc); 156567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 157567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 158567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 159567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private static final class VolumeCallbackWrapper 160567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown implements MediaRouterJellybean.VolumeCallback { 161567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Unfortunately, the framework never unregisters its volume observer from 162567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the audio service so the UserRouteInfo object may leak along with 163567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // any callbacks that we attach to it. Use a weak reference to prevent 164567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the volume callback from holding strong references to anything important. 165567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final WeakReference<JellybeanImpl> mImplWeak; 166567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 167567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public VolumeCallbackWrapper(JellybeanImpl impl) { 168567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mImplWeak = new WeakReference<JellybeanImpl>(impl); 169567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 170567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 171567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 172567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(Object routeObj, int direction) { 173567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown JellybeanImpl impl = mImplWeak.get(); 174567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (impl != null && impl.mVolumeCallback != null) { 175567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown impl.mVolumeCallback.onVolumeUpdateRequest(direction); 176567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 177567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 178567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 179567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 180567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(Object routeObj, int volume) { 181567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown JellybeanImpl impl = mImplWeak.get(); 182567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (impl != null && impl.mVolumeCallback != null) { 183567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown impl.mVolumeCallback.onVolumeSetRequest(volume); 184567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 185567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 186567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 187567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 188567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown} 189