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