blob: a502bab72228d7f3dbc6424d0d56469ffa96a96c [file] [log] [blame]
alisionfde875f2013-05-28 17:01:54 -04001/*
alision2ec64f92013-06-17 17:28:58 -04002 * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
alisionfde875f2013-05-28 17:01:54 -04003 *
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
alision806e18e2013-06-21 15:30:17 -040034import java.util.ArrayList;
alision84813a12013-05-27 17:40:39 -040035import java.util.HashMap;
36
37import android.app.Activity;
38import android.app.Fragment;
39import android.graphics.Bitmap;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100040import android.graphics.BitmapFactory;
alision84813a12013-05-27 17:40:39 -040041import android.graphics.PointF;
42import android.os.Bundle;
43import android.os.RemoteException;
alision84813a12013-05-27 17:40:39 -040044import android.util.Log;
45import android.view.LayoutInflater;
Adrien Béraud13cde0b2013-05-30 20:27:20 +100046import android.view.SurfaceHolder;
47import android.view.SurfaceHolder.Callback;
alision84813a12013-05-27 17:40:39 -040048import android.view.View;
49import android.view.ViewGroup;
50import android.widget.TextView;
51
52import com.savoirfairelinux.sflphone.R;
53import com.savoirfairelinux.sflphone.adapters.ContactPictureLoader;
54import com.savoirfairelinux.sflphone.model.Attractor;
55import com.savoirfairelinux.sflphone.model.Bubble;
56import com.savoirfairelinux.sflphone.model.BubbleModel;
57import com.savoirfairelinux.sflphone.model.BubblesView;
58import com.savoirfairelinux.sflphone.model.CallContact;
59import com.savoirfairelinux.sflphone.model.SipCall;
alision84813a12013-05-27 17:40:39 -040060import com.savoirfairelinux.sflphone.service.ISipService;
61
Adrien Béraud13cde0b2013-05-30 20:27:20 +100062public class CallFragment extends Fragment implements Callback {
alision84813a12013-05-27 17:40:39 -040063
alision1005ba12013-06-19 13:52:44 -040064 static final String TAG = "CallFragment";
alision84813a12013-05-27 17:40:39 -040065
alision1005ba12013-06-19 13:52:44 -040066 static final float BUBBLE_SIZE = 75;
67 static final float ATTRACTOR_SIZE = 40;
alision84813a12013-05-27 17:40:39 -040068
alision806e18e2013-06-21 15:30:17 -040069 private ArrayList<SipCall> mCalls;
alision84813a12013-05-27 17:40:39 -040070
alision1005ba12013-06-19 13:52:44 -040071 private TextView callStatusTxt;
72 private BubblesView view;
73 private BubbleModel model;
alision84813a12013-05-27 17:40:39 -040074
alision1005ba12013-06-19 13:52:44 -040075 private Callbacks mCallbacks = sDummyCallbacks;
alision84813a12013-05-27 17:40:39 -040076
alision1005ba12013-06-19 13:52:44 -040077 private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>();
alision85704182013-05-29 15:23:03 -040078
alision806e18e2013-06-21 15:30:17 -040079 private SipCall myself;
alision85704182013-05-29 15:23:03 -040080
alision1005ba12013-06-19 13:52:44 -040081 private Bitmap hangup_icon;
82 private Bitmap call_icon;
alision85704182013-05-29 15:23:03 -040083
alision1005ba12013-06-19 13:52:44 -040084 @Override
85 public void onCreate(Bundle savedBundle) {
86 super.onCreate(savedBundle);
87 model = new BubbleModel(getResources().getDisplayMetrics().density);
88 Bundle b = getArguments();
alision84813a12013-05-27 17:40:39 -040089
alision806e18e2013-06-21 15:30:17 -040090 mCalls = b.getParcelableArrayList("CallsInfo");
alision84813a12013-05-27 17:40:39 -040091
alision1005ba12013-06-19 13:52:44 -040092 }
alision85992112013-05-29 12:18:08 -040093
alision1005ba12013-06-19 13:52:44 -040094 /**
95 * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
96 */
97 private static Callbacks sDummyCallbacks = new Callbacks() {
98 @Override
99 public void onSendMessage(SipCall call, String msg) {
100 }
alision85992112013-05-29 12:18:08 -0400101
alision1005ba12013-06-19 13:52:44 -0400102 @Override
103 public void callContact(SipCall call) {
104 }
alision85992112013-05-29 12:18:08 -0400105
alision1005ba12013-06-19 13:52:44 -0400106 @Override
107 public void onCallAccepted(SipCall call) {
108 }
alision85704182013-05-29 15:23:03 -0400109
alision1005ba12013-06-19 13:52:44 -0400110 @Override
111 public void onCallRejected(SipCall call) {
112 }
alision85992112013-05-29 12:18:08 -0400113
alision1005ba12013-06-19 13:52:44 -0400114 @Override
115 public void onCallEnded(SipCall call) {
116 }
alision85704182013-05-29 15:23:03 -0400117
alision1005ba12013-06-19 13:52:44 -0400118 @Override
119 public void onCallSuspended(SipCall call) {
120 }
alision85992112013-05-29 12:18:08 -0400121
alision1005ba12013-06-19 13:52:44 -0400122 @Override
123 public void onCallResumed(SipCall call) {
124 }
alision85704182013-05-29 15:23:03 -0400125
alision1005ba12013-06-19 13:52:44 -0400126 @Override
127 public void onCalltransfered(SipCall call, String to) {
128 }
alision85992112013-05-29 12:18:08 -0400129
alision1005ba12013-06-19 13:52:44 -0400130 @Override
131 public void onRecordCall(SipCall call) {
132 }
alision85704182013-05-29 15:23:03 -0400133
alision1005ba12013-06-19 13:52:44 -0400134 @Override
135 public ISipService getService() {
136 return null;
137 }
138 };
alision907bde72013-06-20 14:40:37 -0400139
alision1005ba12013-06-19 13:52:44 -0400140 /**
141 * The Activity calling this fragment has to implement this interface
142 *
143 */
144 public interface Callbacks {
alision85992112013-05-29 12:18:08 -0400145
alision1005ba12013-06-19 13:52:44 -0400146 public ISipService getService();
alision85704182013-05-29 15:23:03 -0400147
alision1005ba12013-06-19 13:52:44 -0400148 public void callContact(SipCall call);
alision85992112013-05-29 12:18:08 -0400149
alision1005ba12013-06-19 13:52:44 -0400150 public void onCallAccepted(SipCall call);
alision85704182013-05-29 15:23:03 -0400151
alision1005ba12013-06-19 13:52:44 -0400152 public void onCallRejected(SipCall call);
alision85992112013-05-29 12:18:08 -0400153
alision1005ba12013-06-19 13:52:44 -0400154 public void onCallEnded(SipCall call);
alision85704182013-05-29 15:23:03 -0400155
alision1005ba12013-06-19 13:52:44 -0400156 public void onCallSuspended(SipCall call);
alision85704182013-05-29 15:23:03 -0400157
alision1005ba12013-06-19 13:52:44 -0400158 public void onCallResumed(SipCall call);
alision84813a12013-05-27 17:40:39 -0400159
alision1005ba12013-06-19 13:52:44 -0400160 public void onCalltransfered(SipCall call, String to);
alision84813a12013-05-27 17:40:39 -0400161
alision1005ba12013-06-19 13:52:44 -0400162 public void onRecordCall(SipCall call);
alision85704182013-05-29 15:23:03 -0400163
alision1005ba12013-06-19 13:52:44 -0400164 public void onSendMessage(SipCall call, String msg);
165 }
alision85992112013-05-29 12:18:08 -0400166
alision1005ba12013-06-19 13:52:44 -0400167 @Override
168 public void onAttach(Activity activity) {
169 super.onAttach(activity);
alision85992112013-05-29 12:18:08 -0400170
alision1005ba12013-06-19 13:52:44 -0400171 if (!(activity instanceof Callbacks)) {
172 throw new IllegalStateException("Activity must implement fragment's callbacks.");
173 }
alision85992112013-05-29 12:18:08 -0400174
alision1005ba12013-06-19 13:52:44 -0400175 // rootView.requestDisallowInterceptTouchEvent(true);
alision85992112013-05-29 12:18:08 -0400176
alision1005ba12013-06-19 13:52:44 -0400177 mCallbacks = (Callbacks) activity;
alision806e18e2013-06-21 15:30:17 -0400178 myself = SipCall.SipCallBuilder.buildMyselfCall(activity.getContentResolver(), "");
alision85992112013-05-29 12:18:08 -0400179
alision1005ba12013-06-19 13:52:44 -0400180 }
alision85992112013-05-29 12:18:08 -0400181
alision1005ba12013-06-19 13:52:44 -0400182 @Override
183 public void onDetach() {
184 super.onDetach();
185 mCallbacks = sDummyCallbacks;
186 // rootView.requestDisallowInterceptTouchEvent(false);
187 }
alision907bde72013-06-20 14:40:37 -0400188
alision1005ba12013-06-19 13:52:44 -0400189 @Override
alision907bde72013-06-20 14:40:37 -0400190 public void onStop() {
alision1005ba12013-06-19 13:52:44 -0400191 super.onStop();
192 }
alision85992112013-05-29 12:18:08 -0400193
alision1005ba12013-06-19 13:52:44 -0400194 @Override
195 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
196 ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_call, container, false);
alision85992112013-05-29 12:18:08 -0400197
alision1005ba12013-06-19 13:52:44 -0400198 view = (BubblesView) rootView.findViewById(R.id.main_view);
199 view.setModel(model);
200 view.getHolder().addCallback(this);
alision84813a12013-05-27 17:40:39 -0400201
alision1005ba12013-06-19 13:52:44 -0400202 callStatusTxt = (TextView) rootView.findViewById(R.id.call_status_txt);
alision84813a12013-05-27 17:40:39 -0400203
alision1005ba12013-06-19 13:52:44 -0400204 hangup_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_hangup);
205 call_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_call);
alision84813a12013-05-27 17:40:39 -0400206
alision1005ba12013-06-19 13:52:44 -0400207 // Do nothing here, the view is not initialized yet.
208 return rootView;
209 }
alision85992112013-05-29 12:18:08 -0400210
alision1005ba12013-06-19 13:52:44 -0400211 private void initNormalStateDisplay() {
212 Log.i(TAG, "Start normal display");
alision84813a12013-05-27 17:40:39 -0400213
alision1005ba12013-06-19 13:52:44 -0400214 callStatusTxt.setText("0 min");
alision84813a12013-05-27 17:40:39 -0400215
alision1005ba12013-06-19 13:52:44 -0400216 getBubbleFor(myself, model.width / 2, model.height / 2);
alision907bde72013-06-20 14:40:37 -0400217
alision806e18e2013-06-21 15:30:17 -0400218 int angle_part = 360 / mCalls.size();
219 double dX = 0;
220 double dY = 0;
221 int radiusCalls = model.width / 2 - 150;
222 for (int i = 0; i < mCalls.size(); ++i) {
223 dX = Math.cos(Math.toRadians(angle_part * i) - 90) * radiusCalls;
224 dY = Math.sin(Math.toRadians(angle_part * i) - 90) * radiusCalls;
225 getBubbleFor(mCalls.get(i), (int) (model.width / 2 + dX), (int) (model.height / 2 + dY));
226 }
alision1005ba12013-06-19 13:52:44 -0400227
228 model.clearAttractors();
alision806e18e2013-06-21 15:30:17 -0400229 model.addAttractor(new Attractor(new PointF(model.width / 1.1f, model.height * .1f), ATTRACTOR_SIZE, new Attractor.Callback() {
alision1005ba12013-06-19 13:52:44 -0400230 @Override
231 public boolean onBubbleSucked(Bubble b) {
232 Log.w(TAG, "Bubble sucked ! ");
alision806e18e2013-06-21 15:30:17 -0400233 if(mCalls.size() == 1){
234 mCallbacks.onCallEnded(b.associated_call);
235 } else {
236 try {
237 mCallbacks.getService().detachParticipant(b.associated_call.getCallId());
238 } catch (RemoteException e) {
239 e.printStackTrace();
240 }
241 }
242
alision1005ba12013-06-19 13:52:44 -0400243 bubbleRemoved(b);
244 return true;
245 }
246 }, hangup_icon));
alision84813a12013-05-27 17:40:39 -0400247
alision1005ba12013-06-19 13:52:44 -0400248 }
alision85704182013-05-29 15:23:03 -0400249
alision1005ba12013-06-19 13:52:44 -0400250 private void initIncomingCallDisplay() {
251 Log.i(TAG, "Start incoming display");
alision84813a12013-05-27 17:40:39 -0400252
alision1005ba12013-06-19 13:52:44 -0400253 callStatusTxt.setText("Incomming call");
alision84813a12013-05-27 17:40:39 -0400254
alision806e18e2013-06-21 15:30:17 -0400255 Bubble contact_bubble = getBubbleFor(mCalls.get(0), model.width / 2, model.height / 2);
256 contacts.put(mCalls.get(0).getContact(), contact_bubble);
alision85704182013-05-29 15:23:03 -0400257
alision1005ba12013-06-19 13:52:44 -0400258 model.clearAttractors();
259 model.addAttractor(new Attractor(new PointF(4 * model.width / 5, model.height / 2), ATTRACTOR_SIZE, new Attractor.Callback() {
260 @Override
261 public boolean onBubbleSucked(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400262 mCallbacks.onCallAccepted(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400263 return false;
264 }
265 }, call_icon));
266 model.addAttractor(new Attractor(new PointF(model.width / 5, model.height / 2), ATTRACTOR_SIZE, new Attractor.Callback() {
267 @Override
268 public boolean onBubbleSucked(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400269 mCallbacks.onCallRejected(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400270 bubbleRemoved(b);
271 return true;
272 }
273 }, hangup_icon));
274 }
alision85704182013-05-29 15:23:03 -0400275
alision1005ba12013-06-19 13:52:44 -0400276 private void initOutGoingCallDisplay() {
277 Log.i(TAG, "Start outgoing display");
alision85704182013-05-29 15:23:03 -0400278
alision1005ba12013-06-19 13:52:44 -0400279 callStatusTxt.setText("Calling...");
alision85704182013-05-29 15:23:03 -0400280
alision1005ba12013-06-19 13:52:44 -0400281 // TODO off-thread image loading
alision1005ba12013-06-19 13:52:44 -0400282 getBubbleFor(myself, model.width / 2, model.height / 2);
alision85704182013-05-29 15:23:03 -0400283
alision806e18e2013-06-21 15:30:17 -0400284 getBubbleFor(mCalls.get(0), (int) (model.width / 2), (int) (model.height / 3));
alision907bde72013-06-20 14:40:37 -0400285
alision1005ba12013-06-19 13:52:44 -0400286 model.clearAttractors();
alision806e18e2013-06-21 15:30:17 -0400287 model.addAttractor(new Attractor(new PointF(model.width / 1.1f, model.height * .1f), 40, new Attractor.Callback() {
alision1005ba12013-06-19 13:52:44 -0400288 @Override
289 public boolean onBubbleSucked(Bubble b) {
290 Log.w(TAG, "Bubble sucked ! ");
alision806e18e2013-06-21 15:30:17 -0400291 mCallbacks.onCallEnded(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400292 bubbleRemoved(b);
293 return true;
294 }
295 }, hangup_icon));
296 }
alision85704182013-05-29 15:23:03 -0400297
alision1005ba12013-06-19 13:52:44 -0400298 /**
299 * Retrieves or create a bubble for a given contact. If the bubble exists, it is moved to the new location.
300 *
alision806e18e2013-06-21 15:30:17 -0400301 * @param call
302 * The call associated to a contact
alision1005ba12013-06-19 13:52:44 -0400303 * @param x
304 * Initial or new x position.
305 * @param y
306 * Initial or new y position.
307 * @return Bubble corresponding to the contact.
308 */
alision806e18e2013-06-21 15:30:17 -0400309 private Bubble getBubbleFor(SipCall call, float x, float y) {
310 Bubble contact_bubble = contacts.get(call.getContact());
alision1005ba12013-06-19 13:52:44 -0400311 if (contact_bubble != null) {
312 contact_bubble.attractor.set(x, y);
313 return contact_bubble;
314 }
alision84813a12013-05-27 17:40:39 -0400315
alision1005ba12013-06-19 13:52:44 -0400316 // TODO off-thread image loading
alision806e18e2013-06-21 15:30:17 -0400317 if (call.getContact().getPhoto_id() > 0) {
318 Bitmap photo = ContactPictureLoader.loadContactPhoto(getActivity().getContentResolver(), call.getContact().getId());
319 contact_bubble = new Bubble(call, x, y, BUBBLE_SIZE, photo);
alision1005ba12013-06-19 13:52:44 -0400320 } else {
alision806e18e2013-06-21 15:30:17 -0400321 contact_bubble = new Bubble(call, x, y, BUBBLE_SIZE, getActivity(), R.drawable.ic_contact_picture);
alision1005ba12013-06-19 13:52:44 -0400322 }
alision84813a12013-05-27 17:40:39 -0400323
alision1005ba12013-06-19 13:52:44 -0400324 model.addBubble(contact_bubble);
alision806e18e2013-06-21 15:30:17 -0400325 contacts.put(call.getContact(), contact_bubble);
alision84813a12013-05-27 17:40:39 -0400326
alision1005ba12013-06-19 13:52:44 -0400327 return contact_bubble;
328 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000329
alision1005ba12013-06-19 13:52:44 -0400330 /**
331 * Should be called when a bubble is removed from the model
332 */
333 void bubbleRemoved(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400334 if (b.associated_call == null) {
alision1005ba12013-06-19 13:52:44 -0400335 return;
336 }
alision806e18e2013-06-21 15:30:17 -0400337 contacts.remove(b.associated_call.getContact());
alision1005ba12013-06-19 13:52:44 -0400338 }
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000339
alision1005ba12013-06-19 13:52:44 -0400340 public void changeCallState(String callID, String newState) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000341
alision1005ba12013-06-19 13:52:44 -0400342 Log.w(TAG, "Changing call state of " + callID);
alision806e18e2013-06-21 15:30:17 -0400343 if (mCalls.size() == 1) {
344 if (callID.equals(mCalls.get(0).getCallId()))
345 mCalls.get(0).setCallState(newState);
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000346
alision806e18e2013-06-21 15:30:17 -0400347 if (mCalls.get(0).isOngoing()) {
348 initNormalStateDisplay();
349 }
350 } else {
351 for (int i = 0; i < mCalls.size(); ++i) {
352 mCalls.get(i).printCallInfo();
353
354 if (callID.equals(mCalls.get(i).getCallId()))
355 mCalls.get(i).setCallState(newState);
356
357 }
alision1005ba12013-06-19 13:52:44 -0400358 }
359 }
alision84813a12013-05-27 17:40:39 -0400360
alision1005ba12013-06-19 13:52:44 -0400361 public boolean draggingBubble() {
362 return view == null ? false : view.isDraggingBubble();
363 }
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000364
alision1005ba12013-06-19 13:52:44 -0400365 @Override
366 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
alision806e18e2013-06-21 15:30:17 -0400367 // Log.i(TAG, "Init fragment " + mCall.getCallId());
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000368
alision806e18e2013-06-21 15:30:17 -0400369 // mCall.printCallInfo();
370 if (mCalls.size() == 1) {
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000371
alision806e18e2013-06-21 15:30:17 -0400372 if (mCalls.get(0).isIncoming() && mCalls.get(0).isRinging()) {
373 initIncomingCallDisplay();
374 } else {
375 if (mCalls.get(0).isRinging()) {
alision1005ba12013-06-19 13:52:44 -0400376 initOutGoingCallDisplay();
377 }
alision806e18e2013-06-21 15:30:17 -0400378 try {
379 if (mCalls.get(0).isOutGoing() && mCallbacks.getService().getCall(mCalls.get(0).getCallId()) == null) {
380 mCallbacks.getService().placeCall(mCalls.get(0));
381 initOutGoingCallDisplay();
382 } else if (mCalls.get(0).isOutGoing() && mCalls.get(0).isRinging()) {
383 initOutGoingCallDisplay();
384 }
385 } catch (RemoteException e) {
386 Log.e(TAG, e.toString());
387 }
alision1005ba12013-06-19 13:52:44 -0400388 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000389
alision806e18e2013-06-21 15:30:17 -0400390 if (mCalls.get(0).isOngoing()) {
391 initNormalStateDisplay();
392 }
393 } else {
alision1005ba12013-06-19 13:52:44 -0400394 initNormalStateDisplay();
395 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000396
alision1005ba12013-06-19 13:52:44 -0400397 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000398
alision1005ba12013-06-19 13:52:44 -0400399 @Override
400 public void surfaceCreated(SurfaceHolder holder) {
401 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000402
alision1005ba12013-06-19 13:52:44 -0400403 @Override
404 public void surfaceDestroyed(SurfaceHolder holder) {
405 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000406
alisione38001f2013-06-04 14:14:39 -0400407 public BubblesView getBubbleView() {
408 return view;
alisione38001f2013-06-04 14:14:39 -0400409
alision1005ba12013-06-19 13:52:44 -0400410 }
alision84813a12013-05-27 17:40:39 -0400411
alision84813a12013-05-27 17:40:39 -0400412}