1/*
2 * Copyright (C) 2014 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
17package com.android.incallui;
18
19import com.google.common.base.Preconditions;
20
21import java.util.Collections;
22import java.util.Set;
23import java.util.concurrent.ConcurrentHashMap;
24
25/**
26 * Class used by {@link InCallService.VideoCallCallback} to notify interested parties of incoming
27 * events.
28 */
29public class InCallVideoCallCallbackNotifier {
30    /**
31     * Singleton instance of this class.
32     */
33    private static InCallVideoCallCallbackNotifier sInstance =
34            new InCallVideoCallCallbackNotifier();
35
36    /**
37     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
38     * load factor before resizing, 1 means we only expect a single thread to
39     * access the map so make only a single shard
40     */
41    private final Set<SessionModificationListener> mSessionModificationListeners =
42            Collections.newSetFromMap(new ConcurrentHashMap<SessionModificationListener, Boolean>
43                    (8, 0.9f, 1));
44    private final Set<VideoEventListener> mVideoEventListeners = Collections.newSetFromMap(
45            new ConcurrentHashMap<VideoEventListener, Boolean>(8, 0.9f, 1));
46    private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap(
47            new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
48
49    /**
50     * Static singleton accessor method.
51     */
52    public static InCallVideoCallCallbackNotifier getInstance() {
53        return sInstance;
54    }
55
56    /**
57     * Private constructor.  Instance should only be acquired through getInstance().
58     */
59    private InCallVideoCallCallbackNotifier() {
60    }
61
62    /**
63     * Adds a new {@link SessionModificationListener}.
64     *
65     * @param listener The listener.
66     */
67    public void addSessionModificationListener(SessionModificationListener listener) {
68        Preconditions.checkNotNull(listener);
69        mSessionModificationListeners.add(listener);
70    }
71
72    /**
73     * Remove a {@link SessionModificationListener}.
74     *
75     * @param listener The listener.
76     */
77    public void removeSessionModificationListener(SessionModificationListener listener) {
78        if (listener != null) {
79            mSessionModificationListeners.remove(listener);
80        }
81    }
82
83    /**
84     * Adds a new {@link VideoEventListener}.
85     *
86     * @param listener The listener.
87     */
88    public void addVideoEventListener(VideoEventListener listener) {
89        Preconditions.checkNotNull(listener);
90        mVideoEventListeners.add(listener);
91    }
92
93    /**
94     * Remove a {@link VideoEventListener}.
95     *
96     * @param listener The listener.
97     */
98    public void removeVideoEventListener(VideoEventListener listener) {
99        if (listener != null) {
100            mVideoEventListeners.remove(listener);
101        }
102    }
103
104    /**
105     * Adds a new {@link SurfaceChangeListener}.
106     *
107     * @param listener The listener.
108     */
109    public void addSurfaceChangeListener(SurfaceChangeListener listener) {
110        Preconditions.checkNotNull(listener);
111        mSurfaceChangeListeners.add(listener);
112    }
113
114    /**
115     * Remove a {@link SurfaceChangeListener}.
116     *
117     * @param listener The listener.
118     */
119    public void removeSurfaceChangeListener(SurfaceChangeListener listener) {
120        if (listener != null) {
121            mSurfaceChangeListeners.remove(listener);
122        }
123    }
124
125    /**
126     * Inform listeners of an upgrade to video request for a call.
127     * @param call The call.
128     * @param videoState The video state we want to upgrade to.
129     */
130    public void upgradeToVideoRequest(Call call, int videoState) {
131        Log.d(this, "upgradeToVideoRequest call = " + call + " new video state = " + videoState);
132        for (SessionModificationListener listener : mSessionModificationListeners) {
133            listener.onUpgradeToVideoRequest(call, videoState);
134        }
135    }
136
137    /**
138     * Inform listeners of a successful response to a video request for a call.
139     *
140     * @param call The call.
141     */
142    public void upgradeToVideoSuccess(Call call) {
143        for (SessionModificationListener listener : mSessionModificationListeners) {
144            listener.onUpgradeToVideoSuccess(call);
145        }
146    }
147
148    /**
149     * Inform listeners of an unsuccessful response to a video request for a call.
150     *
151     * @param call The call.
152     */
153    public void upgradeToVideoFail(int status, Call call) {
154        for (SessionModificationListener listener : mSessionModificationListeners) {
155            listener.onUpgradeToVideoFail(status, call);
156        }
157    }
158
159    /**
160     * Inform listeners of a downgrade to audio.
161     *
162     * @param call The call.
163     */
164    public void downgradeToAudio(Call call) {
165        for (SessionModificationListener listener : mSessionModificationListeners) {
166            listener.onDowngradeToAudio(call);
167        }
168    }
169
170    /**
171     * Inform listeners of a call session event.
172     *
173     * @param event The call session event.
174     */
175    public void callSessionEvent(int event) {
176        for (VideoEventListener listener : mVideoEventListeners) {
177            listener.onCallSessionEvent(event);
178        }
179    }
180
181    /**
182     * Inform listeners of a downgrade to audio.
183     *
184     * @param call The call.
185     * @param paused The paused state.
186     */
187    public void peerPausedStateChanged(Call call, boolean paused) {
188        for (VideoEventListener listener : mVideoEventListeners) {
189            listener.onPeerPauseStateChanged(call, paused);
190        }
191    }
192
193    /**
194     * Inform listeners of any change in the video quality of the call
195     *
196     * @param call The call.
197     * @param videoQuality The updated video quality of the call.
198     */
199    public void videoQualityChanged(Call call, int videoQuality) {
200        for (VideoEventListener listener : mVideoEventListeners) {
201            listener.onVideoQualityChanged(call, videoQuality);
202        }
203    }
204
205    /**
206     * Inform listeners of a change to peer dimensions.
207     *
208     * @param call The call.
209     * @param width New peer width.
210     * @param height New peer height.
211     */
212    public void peerDimensionsChanged(Call call, int width, int height) {
213        for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
214            listener.onUpdatePeerDimensions(call, width, height);
215        }
216    }
217
218    /**
219     * Inform listeners of a change to camera dimensions.
220     *
221     * @param call The call.
222     * @param width The new camera video width.
223     * @param height The new camera video height.
224     */
225    public void cameraDimensionsChanged(Call call, int width, int height) {
226        for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
227            listener.onCameraDimensionsChange(call, width, height);
228        }
229    }
230
231    /**
232     * Inform listeners of a change to call data usage.
233     *
234     * @param dataUsage data usage value
235     */
236    public void callDataUsageChanged(long dataUsage) {
237        for (VideoEventListener listener : mVideoEventListeners) {
238            listener.onCallDataUsageChange(dataUsage);
239        }
240    }
241
242    /**
243     * Listener interface for any class that wants to be notified of upgrade to video and downgrade
244     * to audio session modification requests.
245     */
246    public interface SessionModificationListener {
247        /**
248         * Called when a peer request is received to upgrade an audio-only call to a video call.
249         *
250         * @param call The call the request was received for.
251         * @param videoState The video state that the request wants to upgrade to.
252         */
253        public void onUpgradeToVideoRequest(Call call, int videoState);
254
255        /**
256         * Called when a request to a peer to upgrade an audio-only call to a video call is
257         * successful.
258         *
259         * @param call The call the request was successful for.
260         */
261        public void onUpgradeToVideoSuccess(Call call);
262
263        /**
264         * Called when a request to a peer to upgrade an audio-only call to a video call is
265         * NOT successful. This can be if the peer chooses rejects the the video call, or if the
266         * peer does not support video calling, or if there is some error in sending the request.
267         *
268         * @param call The call the request was successful for.
269         */
270        public void onUpgradeToVideoFail(int status, Call call);
271
272        /**
273         * Called when a call has been downgraded to audio-only.
274         *
275         * @param call The call which was downgraded to audio-only.
276         */
277        public void onDowngradeToAudio(Call call);
278    }
279
280    /**
281     * Listener interface for any class that wants to be notified of video events, including pause
282     * and un-pause of peer video, video quality changes.
283     */
284    public interface VideoEventListener {
285        /**
286         * Called when the peer pauses or un-pauses video transmission.
287         *
288         * @param call   The call which paused or un-paused video transmission.
289         * @param paused {@code True} when the video transmission is paused, {@code false}
290         *               otherwise.
291         */
292        public void onPeerPauseStateChanged(Call call, boolean paused);
293
294        /**
295         * Called when the video quality changes.
296         *
297         * @param call   The call whose video quality changes.
298         * @param videoCallQuality - values are QUALITY_HIGH, MEDIUM, LOW and UNKNOWN.
299         */
300        public void onVideoQualityChanged(Call call, int videoCallQuality);
301
302        /*
303         * Called when call data usage value is requested or when call data usage value is updated
304         * because of a call state change
305         *
306         * @param dataUsage call data usage value
307         */
308        public void onCallDataUsageChange(long dataUsage);
309
310        /**
311         * Called when call session event is raised.
312         *
313         * @param event The call session event.
314         */
315        public void onCallSessionEvent(int event);
316    }
317
318    /**
319     * Listener interface for any class that wants to be notified of changes to the video surfaces.
320     */
321    public interface SurfaceChangeListener {
322        /**
323         * Called when the peer video feed changes dimensions. This can occur when the peer rotates
324         * their device, changing the aspect ratio of the video signal.
325         *
326         * @param call The call which experienced a peer video
327         * @param width
328         * @param height
329         */
330        public void onUpdatePeerDimensions(Call call, int width, int height);
331
332        /**
333         * Called when the local camera changes dimensions.  This occurs when a change in camera
334         * occurs.
335         *
336         * @param call The call which experienced the camera dimension change.
337         * @param width The new camera video width.
338         * @param height The new camera video height.
339         */
340        public void onCameraDimensionsChange(Call call, int width, int height);
341    }
342}
343