blob: fd1287f5a9358a54c3bff98b9f0cfeb6cc0edae3 [file] [log] [blame]
alisionfde875f2013-05-28 17:01:54 -04001/*
2 * Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
3 *
4 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Additional permission under GNU GPL version 3 section 7:
21 *
22 * If you modify this program, or any covered work, by linking or
23 * combining it with the OpenSSL project's OpenSSL library (or a
24 * modified version of that library), containing parts covered by the
25 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
26 * grants you additional permission to convey the resulting work.
27 * Corresponding Source for a non-source form of such a combination
28 * shall include the source code for the parts of OpenSSL used as well
29 * as that of the covered work.
30 */
31
alision84813a12013-05-27 17:40:39 -040032package com.savoirfairelinux.sflphone.fragments;
33
34import java.util.HashMap;
35
36import android.app.Activity;
37import android.app.Fragment;
38import android.graphics.Bitmap;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100039import android.graphics.BitmapFactory;
alision84813a12013-05-27 17:40:39 -040040import android.graphics.PointF;
41import android.os.Bundle;
42import android.os.RemoteException;
43import android.util.DisplayMetrics;
44import android.util.Log;
45import android.view.LayoutInflater;
46import android.view.View;
47import android.view.ViewGroup;
48import android.widget.TextView;
49
50import com.savoirfairelinux.sflphone.R;
51import com.savoirfairelinux.sflphone.adapters.ContactPictureLoader;
52import com.savoirfairelinux.sflphone.model.Attractor;
53import com.savoirfairelinux.sflphone.model.Bubble;
54import com.savoirfairelinux.sflphone.model.BubbleModel;
55import com.savoirfairelinux.sflphone.model.BubblesView;
56import com.savoirfairelinux.sflphone.model.CallContact;
57import com.savoirfairelinux.sflphone.model.SipCall;
alision84813a12013-05-27 17:40:39 -040058import com.savoirfairelinux.sflphone.service.ISipService;
59
60public class CallFragment extends Fragment {
alision84813a12013-05-27 17:40:39 -040061
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100062 static final String TAG = "CallFragment";
alision84813a12013-05-27 17:40:39 -040063
Adrien Béraudc9c424d2013-05-30 17:47:35 +100064 static final float BUBBLE_SIZE = 75;
65 static final float ATTRACTOR_SIZE = 40;
alision84813a12013-05-27 17:40:39 -040066
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100067 private SipCall mCall;
alision84813a12013-05-27 17:40:39 -040068
Adrien Béraudc9c424d2013-05-30 17:47:35 +100069 private ViewGroup rootView;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100070 private BubblesView view;
71 private BubbleModel model;
72 private PointF screenCenter;
73 private DisplayMetrics metrics;
alision84813a12013-05-27 17:40:39 -040074
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100075 private Callbacks mCallbacks = sDummyCallbacks;
alision84813a12013-05-27 17:40:39 -040076
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100077 private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>();
alision85704182013-05-29 15:23:03 -040078
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100079 private TextView contact_name_txt;
alision85704182013-05-29 15:23:03 -040080
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100081 private CallContact myself = CallContact.ContactBuilder.buildUserContact("Me");
alision85704182013-05-29 15:23:03 -040082
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100083 private Bitmap hangup_icon;
84 private Bitmap call_icon;
alision84813a12013-05-27 17:40:39 -040085
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100086 @Override
87 public void onCreate(Bundle savedBundle) {
88 super.onCreate(savedBundle);
89 model = new BubbleModel(getResources().getDisplayMetrics().density);
90 metrics = getResources().getDisplayMetrics();
91 screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3);
alision84813a12013-05-27 17:40:39 -040092
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100093 Bundle b = getArguments();
alision85992112013-05-29 12:18:08 -040094
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100095 mCall = b.getParcelable("CallInfo");
96 //mCall.
97 }
alision85992112013-05-29 12:18:08 -040098
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100099 /**
100 * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
101 */
102 private static Callbacks sDummyCallbacks = new Callbacks() {
103 @Override
104 public void onSendMessage(SipCall call, String msg) {
105 }
alision85992112013-05-29 12:18:08 -0400106
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000107 @Override
108 public void callContact(SipCall call) {
109 }
alision85704182013-05-29 15:23:03 -0400110
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000111 @Override
112 public void onCallAccepted(SipCall call) {
113 }
alision85992112013-05-29 12:18:08 -0400114
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000115 @Override
116 public void onCallRejected(SipCall call) {
117 }
alision85704182013-05-29 15:23:03 -0400118
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000119 @Override
120 public void onCallEnded(SipCall call) {
121 }
alision85992112013-05-29 12:18:08 -0400122
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000123 @Override
124 public void onCallSuspended(SipCall call) {
125 }
alision85704182013-05-29 15:23:03 -0400126
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000127 @Override
128 public void onCallResumed(SipCall call) {
129 }
alision85992112013-05-29 12:18:08 -0400130
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000131 @Override
132 public void onCalltransfered(SipCall call, String to) {
133 }
alision85704182013-05-29 15:23:03 -0400134
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000135 @Override
136 public void onRecordCall(SipCall call) {
137 }
alision85992112013-05-29 12:18:08 -0400138
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000139 @Override
140 public ISipService getService() {
141 return null;
142 }
143 };
alision85704182013-05-29 15:23:03 -0400144
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000145 /**
146 * The Activity calling this fragment has to implement this interface
147 *
148 */
149 public interface Callbacks {
alision85992112013-05-29 12:18:08 -0400150
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000151 public ISipService getService();
alision85704182013-05-29 15:23:03 -0400152
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000153 public void callContact(SipCall call);
alision85992112013-05-29 12:18:08 -0400154
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000155 public void onCallAccepted(SipCall call);
alision85704182013-05-29 15:23:03 -0400156
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000157 public void onCallRejected(SipCall call);
alision85704182013-05-29 15:23:03 -0400158
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000159 public void onCallEnded(SipCall call);
alision84813a12013-05-27 17:40:39 -0400160
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000161 public void onCallSuspended(SipCall call);
alision84813a12013-05-27 17:40:39 -0400162
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000163 public void onCallResumed(SipCall call);
alision85704182013-05-29 15:23:03 -0400164
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000165 public void onCalltransfered(SipCall call, String to);
alision85992112013-05-29 12:18:08 -0400166
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000167 public void onRecordCall(SipCall call);
alision85992112013-05-29 12:18:08 -0400168
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000169 public void onSendMessage(SipCall call, String msg);
170 }
alision85992112013-05-29 12:18:08 -0400171
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000172 @Override
173 public void onAttach(Activity activity) {
174 super.onAttach(activity);
alision85992112013-05-29 12:18:08 -0400175
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000176 if (!(activity instanceof Callbacks)) {
177 throw new IllegalStateException("Activity must implement fragment's callbacks.");
178 }
alision85992112013-05-29 12:18:08 -0400179
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000180 //rootView.requestDisallowInterceptTouchEvent(true);
181
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000182 mCallbacks = (Callbacks) activity;
183 }
alision85992112013-05-29 12:18:08 -0400184
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000185 @Override
186 public void onDetach() {
187 super.onDetach();
188 mCallbacks = sDummyCallbacks;
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000189 //rootView.requestDisallowInterceptTouchEvent(false);
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000190 }
alision85992112013-05-29 12:18:08 -0400191
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000192 @Override
193 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000194 rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
195 //rootView.requestDisallowInterceptTouchEvent(true);
alision85992112013-05-29 12:18:08 -0400196
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000197 view = (BubblesView) rootView.findViewById(R.id.main_view);
198 view.setModel(model);
alision84813a12013-05-27 17:40:39 -0400199
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000200 hangup_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_hangup);
201 call_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_call);
alision84813a12013-05-27 17:40:39 -0400202
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000203 Log.i(TAG, "Starting fragment for call " + mCall.getCallId());
alision84813a12013-05-27 17:40:39 -0400204
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000205 mCall.printCallInfo();
alision85992112013-05-29 12:18:08 -0400206
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000207 if (mCall.isIncoming() && mCall.isRinging()) {
208 initIncomingCallDisplay();
209 } else {
210 if (mCall.isRinging()) {
211 initOutGoingCallDisplay();
212 }
213 try {
214 if (mCall.isOutGoing() && mCallbacks.getService().getCall(mCall.getCallId()) == null) {
215 mCallbacks.getService().placeCall(mCall);
216 initOutGoingCallDisplay();
217 } else if(mCall.isOutGoing() && mCall.isRinging()){
218 initOutGoingCallDisplay();
219 }
220 } catch (RemoteException e) {
221 Log.e(TAG, e.toString());
222 }
223 }
alision84813a12013-05-27 17:40:39 -0400224
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000225 if(mCall.isOngoing()){
226 initNormalStateDisplay();
227 }
alision84813a12013-05-27 17:40:39 -0400228
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000229 return rootView;
230 }
alision84813a12013-05-27 17:40:39 -0400231
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000232 private void initNormalStateDisplay() {
233 Log.i(TAG, "Start normal display");
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000234
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000235 Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
236 Bubble me = getBubbleFor(myself, screenCenter.x, screenCenter.y * 3 / 2);
alision84813a12013-05-27 17:40:39 -0400237
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000238 model.clearAttractors();
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000239 model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), ATTRACTOR_SIZE, new Attractor.Callback() {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000240 @Override
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000241 public boolean onBubbleSucked(Bubble b) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000242 Log.w(TAG, "Bubble sucked ! ");
243 mCallbacks.onCallEnded(mCall);
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000244 bubbleRemoved(b);
245 return true;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000246 }
247 }, hangup_icon));
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000248 }
alision84813a12013-05-27 17:40:39 -0400249
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000250 private void initIncomingCallDisplay() {
251 Log.i(TAG, "Start incoming display");
alision85704182013-05-29 15:23:03 -0400252
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000253 Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
254 contacts.put(mCall.getContacts().get(0), contact_bubble);
alision85704182013-05-29 15:23:03 -0400255
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000256 model.clearAttractors();
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000257 model.addAttractor(new Attractor(new PointF(4 * metrics.widthPixels / 5, screenCenter.y), ATTRACTOR_SIZE, new Attractor.Callback() {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000258 @Override
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000259 public boolean onBubbleSucked(Bubble b) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000260 mCallbacks.onCallAccepted(mCall);
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000261 return false;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000262 }
263 }, call_icon));
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000264 model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 5, screenCenter.y), ATTRACTOR_SIZE, new Attractor.Callback() {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000265 @Override
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000266 public boolean onBubbleSucked(Bubble b) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000267 mCallbacks.onCallRejected(mCall);
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000268 bubbleRemoved(b);
269 return true;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000270 }
271 }, hangup_icon));
272 }
alision85704182013-05-29 15:23:03 -0400273
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000274 private void initOutGoingCallDisplay() {
275 Log.i(TAG, "Start outgoing display");
276 // TODO off-thread image loading
277 Bubble contact_bubble = getBubbleFor(mCall.getContacts().get(0), screenCenter.x, screenCenter.y);
alision85704182013-05-29 15:23:03 -0400278
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000279 model.clearAttractors();
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000280 model.addAttractor(new Attractor(new PointF(metrics.widthPixels / 2, metrics.heightPixels * .8f), 40, new Attractor.Callback() {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000281 @Override
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000282 public boolean onBubbleSucked(Bubble b) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000283 Log.w(TAG, "Bubble sucked ! ");
284 mCallbacks.onCallEnded(mCall);
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000285 bubbleRemoved(b);
286 return true;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000287 }
288 }, hangup_icon));
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000289 }
alision85704182013-05-29 15:23:03 -0400290
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000291 /**
292 * Retrieves or create a bubble for a given contact.
293 * If the bubble exists, it is moved to the new location.
294 *
295 * @param contact The contact
296 * @param x Initial or new x position.
297 * @param y Initial or new y position.
298 * @return Bubble corresponding to the contact.
299 */
300 private Bubble getBubbleFor(CallContact contact, float x, float y) {
301 Bubble contact_bubble = contacts.get(contact);
302 if(contact_bubble != null) {
303 contact_bubble.attractor.set(x, y);
304 return contact_bubble;
305 }
alision84813a12013-05-27 17:40:39 -0400306
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000307 // TODO off-thread image loading
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000308 if (contact.getPhoto_id() > 0) {
309 Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), mCall.getContacts().get(0).getId());
310 contact_bubble = new Bubble(x, y, BUBBLE_SIZE, photo);
311 } else {
312 contact_bubble = new Bubble(x, y, BUBBLE_SIZE, getActivity(), R.drawable.ic_contact_picture);
313 }
314 contact_bubble.contact = contact;
alision84813a12013-05-27 17:40:39 -0400315
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000316 model.addBubble(contact_bubble);
317 contacts.put(contact, contact_bubble);
alision84813a12013-05-27 17:40:39 -0400318
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000319 return contact_bubble;
320 }
321
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000322 /**
323 * Should be called when a bubble is removed from the model
324 */
325 void bubbleRemoved(Bubble b) {
326 if(b.contact == null) {
327 return;
328 }
329
330 contacts.remove(b.contact);
331 }
332
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000333 public void changeCallState(String callID, String newState) {
334
335 Log.w(TAG, "Changing call state of "+callID);
336 mCall.printCallInfo();
337 if(!callID.equals(mCall.getCallId()))
338 return;
339
340 mCall.setCallState(newState);
341 if(mCall.isOngoing()){
342 initNormalStateDisplay();
343 }
344 }
alision84813a12013-05-27 17:40:39 -0400345
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000346 public boolean draggingBubble()
347 {
348 return view == null ? false : view.isDraggingBubble();
349 }
350
alision84813a12013-05-27 17:40:39 -0400351}