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.VideoCallListener} to notify interested parties of incoming
27 * events.
28 */
29public class InCallVideoCallListenerNotifier {
30    /**
31     * Singleton instance of this class.
32     */
33    private static InCallVideoCallListenerNotifier sInstance = new InCallVideoCallListenerNotifier();
34
35    /**
36     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
37     * load factor before resizing, 1 means we only expect a single thread to
38     * access the map so make only a single shard
39     */
40    private final Set<SessionModificationListener> mSessionModificationListeners =
41            Collections.newSetFromMap(new ConcurrentHashMap<SessionModificationListener, Boolean>
42                    (8, 0.9f, 1));
43    private final Set<VideoEventListener> mVideoEventListeners = Collections.newSetFromMap(
44            new ConcurrentHashMap<VideoEventListener, Boolean>(8, 0.9f, 1));
45    private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap(
46            new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
47
48    /**
49     * Static singleton accessor method.
50     */
51    public static InCallVideoCallListenerNotifier getInstance() {
52        return sInstance;
53    }
54
55    /**
56     * Private constructor.  Instance should only be acquired through getInstance().
57     */
58    private InCallVideoCallListenerNotifier() {
59    }
60
61    /**
62     * Adds a new {@link SessionModificationListener}.
63     *
64     * @param listener The listener.
65     */
66    public void addSessionModificationListener(SessionModificationListener listener) {
67        Preconditions.checkNotNull(listener);
68        mSessionModificationListeners.add(listener);
69    }
70
71    /**
72     * Remove a {@link SessionModificationListener}.
73     *
74     * @param listener The listener.
75     */
76    public void removeSessionModificationListener(SessionModificationListener listener) {
77        if (listener != null) {
78            mSessionModificationListeners.remove(listener);
79        }
80    }
81
82    /**
83     * Adds a new {@link VideoEventListener}.
84     *
85     * @param listener The listener.
86     */
87    public void addVideoEventListener(VideoEventListener listener) {
88        Preconditions.checkNotNull(listener);
89        mVideoEventListeners.add(listener);
90    }
91
92    /**
93     * Remove a {@link VideoEventListener}.
94     *
95     * @param listener The listener.
96     */
97    public void removeVideoEventListener(VideoEventListener listener) {
98        if (listener != null) {
99            mVideoEventListeners.remove(listener);
100        }
101    }
102
103    /**
104     * Adds a new {@link SurfaceChangeListener}.
105     *
106     * @param listener The listener.
107     */
108    public void addSurfaceChangeListener(SurfaceChangeListener listener) {
109        Preconditions.checkNotNull(listener);
110        mSurfaceChangeListeners.add(listener);
111    }
112
113    /**
114     * Remove a {@link SurfaceChangeListener}.
115     *
116     * @param listener The listener.
117     */
118    public void removeSurfaceChangeListener(SurfaceChangeListener listener) {
119        if (listener != null) {
120            mSurfaceChangeListeners.remove(listener);
121        }
122    }
123
124    /**
125     * Inform listeners of an upgrade to video request for a call.
126     *
127     * @param call The call.
128     */
129    public void upgradeToVideoRequest(Call call) {
130        for (SessionModificationListener listener : mSessionModificationListeners) {
131            listener.onUpgradeToVideoRequest(call);
132        }
133    }
134
135    /**
136     * Inform listeners of a successful response to a video request for a call.
137     *
138     * @param call The call.
139     */
140    public void upgradeToVideoSuccess(Call call) {
141        for (SessionModificationListener listener : mSessionModificationListeners) {
142            listener.onUpgradeToVideoSuccess(call);
143        }
144    }
145
146    /**
147     * Inform listeners of an unsuccessful response to a video request for a call.
148     *
149     * @param call The call.
150     */
151    public void upgradeToVideoFail(Call call) {
152        for (SessionModificationListener listener : mSessionModificationListeners) {
153            listener.onUpgradeToVideoFail(call);
154        }
155    }
156
157    /**
158     * Inform listeners of a downgrade to audio.
159     *
160     * @param call The call.
161     */
162    public void downgradeToAudio(Call call) {
163        for (SessionModificationListener listener : mSessionModificationListeners) {
164            listener.onDowngradeToAudio(call);
165        }
166    }
167
168    /**
169     * Inform listeners of a downgrade to audio.
170     *
171     * @param call The call.
172     * @param paused The paused state.
173     */
174    public void peerPausedStateChanged(Call call, boolean paused) {
175        for (VideoEventListener listener : mVideoEventListeners) {
176            listener.onPeerPauseStateChanged(call, paused);
177        }
178    }
179
180    /**
181     * Inform listeners of a change to peer dimensions.
182     *
183     * @param call The call.
184     * @param width New peer width.
185     * @param height New peer height.
186     */
187    public void peerDimensionsChanged(Call call, int width, int height) {
188        for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
189            listener.onUpdatePeerDimensions(call, width, height);
190        }
191    }
192
193    /**
194     * Inform listeners of a change to camera dimensions.
195     *
196     * @param call The call.
197     * @param width The new camera video width.
198     * @param height The new camera video height.
199     */
200    public void cameraDimensionsChanged(Call call, int width, int height) {
201        for (SurfaceChangeListener listener : mSurfaceChangeListeners) {
202            listener.onCameraDimensionsChange(call, width, height);
203        }
204    }
205
206    /**
207     * Listener interface for any class that wants to be notified of upgrade to video and downgrade
208     * to audio session modification requests.
209     */
210    public interface SessionModificationListener {
211        /**
212         * Called when a peer request is received to upgrade an audio-only call to a video call.
213         *
214         * @param call The call the request was received for.
215         */
216        public void onUpgradeToVideoRequest(Call call);
217
218        /**
219         * Called when a request to a peer to upgrade an audio-only call to a video call is
220         * successful.
221         *
222         * @param call The call the request was successful for.
223         */
224        public void onUpgradeToVideoSuccess(Call call);
225
226        /**
227         * Called when a request to a peer to upgrade an audio-only call to a video call is
228         * NOT successful. This can be if the peer chooses rejects the the video call, or if the
229         * peer does not support video calling, or if there is some error in sending the request.
230         *
231         * @param call The call the request was successful for.
232         */
233        public void onUpgradeToVideoFail(Call call);
234
235        /**
236         * Called when a call has been downgraded to audio-only.
237         *
238         * @param call The call which was downgraded to audio-only.
239         */
240        public void onDowngradeToAudio(Call call);
241    }
242
243    /**
244     * Listener interface for any class that wants to be notified of video events, including pause
245     * and un-pause of peer video.
246     */
247    public interface VideoEventListener {
248        /**
249         * Called when the peer pauses or un-pauses video transmission.
250         *
251         * @param call   The call which paused or un-paused video transmission.
252         * @param paused {@code True} when the video transmission is paused, {@code false}
253         *               otherwise.
254         */
255        public void onPeerPauseStateChanged(Call call, boolean paused);
256    }
257
258    /**
259     * Listener interface for any class that wants to be notified of changes to the video surfaces.
260     */
261    public interface SurfaceChangeListener {
262        /**
263         * Called when the peer video feed changes dimensions. This can occur when the peer rotates
264         * their device, changing the aspect ratio of the video signal.
265         *
266         * @param call The call which experienced a peer video
267         * @param width
268         * @param height
269         */
270        public void onUpdatePeerDimensions(Call call, int width, int height);
271
272        /**
273         * Called when the local camera changes dimensions.  This occurs when a change in camera
274         * occurs.
275         *
276         * @param call The call which experienced the camera dimension change.
277         * @param width The new camera video width.
278         * @param height The new camera video height.
279         */
280        public void onCameraDimensionsChange(Call call, int width, int height);
281    }
282}
283