blob: f18b0bfe3575e5fc5ef5648d455987acf4821286 [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);
alision34673e62013-06-25 14:40:07 -0400199 view.setFragment(this);
alision1005ba12013-06-19 13:52:44 -0400200 view.setModel(model);
201 view.getHolder().addCallback(this);
alision84813a12013-05-27 17:40:39 -0400202
alision1005ba12013-06-19 13:52:44 -0400203 callStatusTxt = (TextView) rootView.findViewById(R.id.call_status_txt);
alision84813a12013-05-27 17:40:39 -0400204
alision1005ba12013-06-19 13:52:44 -0400205 hangup_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_hangup);
206 call_icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_call);
alision84813a12013-05-27 17:40:39 -0400207
alision1005ba12013-06-19 13:52:44 -0400208 // Do nothing here, the view is not initialized yet.
209 return rootView;
210 }
alision85992112013-05-29 12:18:08 -0400211
alision1005ba12013-06-19 13:52:44 -0400212 private void initNormalStateDisplay() {
213 Log.i(TAG, "Start normal display");
alision84813a12013-05-27 17:40:39 -0400214
alision1005ba12013-06-19 13:52:44 -0400215 callStatusTxt.setText("0 min");
alision84813a12013-05-27 17:40:39 -0400216
alision1005ba12013-06-19 13:52:44 -0400217 getBubbleFor(myself, model.width / 2, model.height / 2);
alision907bde72013-06-20 14:40:37 -0400218
alision806e18e2013-06-21 15:30:17 -0400219 int angle_part = 360 / mCalls.size();
220 double dX = 0;
221 double dY = 0;
222 int radiusCalls = model.width / 2 - 150;
223 for (int i = 0; i < mCalls.size(); ++i) {
alision34673e62013-06-25 14:40:07 -0400224 dX = Math.cos(Math.toRadians(angle_part * i - 90)) * radiusCalls;
225 dY = Math.sin(Math.toRadians(angle_part * i - 90)) * radiusCalls;
alision806e18e2013-06-21 15:30:17 -0400226 getBubbleFor(mCalls.get(i), (int) (model.width / 2 + dX), (int) (model.height / 2 + dY));
227 }
alision1005ba12013-06-19 13:52:44 -0400228
229 model.clearAttractors();
alision806e18e2013-06-21 15:30:17 -0400230 model.addAttractor(new Attractor(new PointF(model.width / 1.1f, model.height * .1f), ATTRACTOR_SIZE, new Attractor.Callback() {
alision1005ba12013-06-19 13:52:44 -0400231 @Override
232 public boolean onBubbleSucked(Bubble b) {
233 Log.w(TAG, "Bubble sucked ! ");
alision34673e62013-06-25 14:40:07 -0400234 if (mCalls.size() == 1) {
alision806e18e2013-06-21 15:30:17 -0400235 mCallbacks.onCallEnded(b.associated_call);
236 } else {
237 try {
238 mCallbacks.getService().detachParticipant(b.associated_call.getCallId());
239 } catch (RemoteException e) {
240 e.printStackTrace();
241 }
242 }
alision34673e62013-06-25 14:40:07 -0400243
alision1005ba12013-06-19 13:52:44 -0400244 bubbleRemoved(b);
245 return true;
246 }
247 }, hangup_icon));
alision84813a12013-05-27 17:40:39 -0400248
alision1005ba12013-06-19 13:52:44 -0400249 }
alision85704182013-05-29 15:23:03 -0400250
alision1005ba12013-06-19 13:52:44 -0400251 private void initIncomingCallDisplay() {
252 Log.i(TAG, "Start incoming display");
alision84813a12013-05-27 17:40:39 -0400253
alision1005ba12013-06-19 13:52:44 -0400254 callStatusTxt.setText("Incomming call");
alision84813a12013-05-27 17:40:39 -0400255
alision806e18e2013-06-21 15:30:17 -0400256 Bubble contact_bubble = getBubbleFor(mCalls.get(0), model.width / 2, model.height / 2);
257 contacts.put(mCalls.get(0).getContact(), contact_bubble);
alision85704182013-05-29 15:23:03 -0400258
alision1005ba12013-06-19 13:52:44 -0400259 model.clearAttractors();
260 model.addAttractor(new Attractor(new PointF(4 * model.width / 5, model.height / 2), ATTRACTOR_SIZE, new Attractor.Callback() {
261 @Override
262 public boolean onBubbleSucked(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400263 mCallbacks.onCallAccepted(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400264 return false;
265 }
266 }, call_icon));
267 model.addAttractor(new Attractor(new PointF(model.width / 5, model.height / 2), ATTRACTOR_SIZE, new Attractor.Callback() {
268 @Override
269 public boolean onBubbleSucked(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400270 mCallbacks.onCallRejected(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400271 bubbleRemoved(b);
272 return true;
273 }
274 }, hangup_icon));
275 }
alision85704182013-05-29 15:23:03 -0400276
alision1005ba12013-06-19 13:52:44 -0400277 private void initOutGoingCallDisplay() {
278 Log.i(TAG, "Start outgoing display");
alision85704182013-05-29 15:23:03 -0400279
alision1005ba12013-06-19 13:52:44 -0400280 callStatusTxt.setText("Calling...");
alision85704182013-05-29 15:23:03 -0400281
alision1005ba12013-06-19 13:52:44 -0400282 // TODO off-thread image loading
alision1005ba12013-06-19 13:52:44 -0400283 getBubbleFor(myself, model.width / 2, model.height / 2);
alision85704182013-05-29 15:23:03 -0400284
alision806e18e2013-06-21 15:30:17 -0400285 getBubbleFor(mCalls.get(0), (int) (model.width / 2), (int) (model.height / 3));
alision907bde72013-06-20 14:40:37 -0400286
alision1005ba12013-06-19 13:52:44 -0400287 model.clearAttractors();
alision806e18e2013-06-21 15:30:17 -0400288 model.addAttractor(new Attractor(new PointF(model.width / 1.1f, model.height * .1f), 40, new Attractor.Callback() {
alision1005ba12013-06-19 13:52:44 -0400289 @Override
290 public boolean onBubbleSucked(Bubble b) {
291 Log.w(TAG, "Bubble sucked ! ");
alision806e18e2013-06-21 15:30:17 -0400292 mCallbacks.onCallEnded(mCalls.get(0));
alision1005ba12013-06-19 13:52:44 -0400293 bubbleRemoved(b);
294 return true;
295 }
296 }, hangup_icon));
297 }
alision85704182013-05-29 15:23:03 -0400298
alision1005ba12013-06-19 13:52:44 -0400299 /**
300 * Retrieves or create a bubble for a given contact. If the bubble exists, it is moved to the new location.
301 *
alision806e18e2013-06-21 15:30:17 -0400302 * @param call
303 * The call associated to a contact
alision1005ba12013-06-19 13:52:44 -0400304 * @param x
305 * Initial or new x position.
306 * @param y
307 * Initial or new y position.
308 * @return Bubble corresponding to the contact.
309 */
alision806e18e2013-06-21 15:30:17 -0400310 private Bubble getBubbleFor(SipCall call, float x, float y) {
311 Bubble contact_bubble = contacts.get(call.getContact());
alision1005ba12013-06-19 13:52:44 -0400312 if (contact_bubble != null) {
313 contact_bubble.attractor.set(x, y);
314 return contact_bubble;
315 }
alision84813a12013-05-27 17:40:39 -0400316
alision34673e62013-06-25 14:40:07 -0400317 contact_bubble = new Bubble(getActivity(), call, x, y, BUBBLE_SIZE);
alision84813a12013-05-27 17:40:39 -0400318
alision1005ba12013-06-19 13:52:44 -0400319 model.addBubble(contact_bubble);
alision806e18e2013-06-21 15:30:17 -0400320 contacts.put(call.getContact(), contact_bubble);
alision84813a12013-05-27 17:40:39 -0400321
alision1005ba12013-06-19 13:52:44 -0400322 return contact_bubble;
323 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000324
alision1005ba12013-06-19 13:52:44 -0400325 /**
326 * Should be called when a bubble is removed from the model
327 */
328 void bubbleRemoved(Bubble b) {
alision806e18e2013-06-21 15:30:17 -0400329 if (b.associated_call == null) {
alision1005ba12013-06-19 13:52:44 -0400330 return;
331 }
alision806e18e2013-06-21 15:30:17 -0400332 contacts.remove(b.associated_call.getContact());
alision1005ba12013-06-19 13:52:44 -0400333 }
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000334
alision1005ba12013-06-19 13:52:44 -0400335 public void changeCallState(String callID, String newState) {
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000336
alision1005ba12013-06-19 13:52:44 -0400337 Log.w(TAG, "Changing call state of " + callID);
alision806e18e2013-06-21 15:30:17 -0400338 if (mCalls.size() == 1) {
339 if (callID.equals(mCalls.get(0).getCallId()))
340 mCalls.get(0).setCallState(newState);
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000341
alision806e18e2013-06-21 15:30:17 -0400342 if (mCalls.get(0).isOngoing()) {
343 initNormalStateDisplay();
344 }
345 } else {
346 for (int i = 0; i < mCalls.size(); ++i) {
347 mCalls.get(i).printCallInfo();
348
349 if (callID.equals(mCalls.get(i).getCallId()))
350 mCalls.get(i).setCallState(newState);
351
352 }
alision1005ba12013-06-19 13:52:44 -0400353 }
354 }
alision84813a12013-05-27 17:40:39 -0400355
alision1005ba12013-06-19 13:52:44 -0400356 public boolean draggingBubble() {
357 return view == null ? false : view.isDraggingBubble();
358 }
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000359
alision1005ba12013-06-19 13:52:44 -0400360 @Override
361 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
alision806e18e2013-06-21 15:30:17 -0400362 // Log.i(TAG, "Init fragment " + mCall.getCallId());
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000363
alision806e18e2013-06-21 15:30:17 -0400364 // mCall.printCallInfo();
365 if (mCalls.size() == 1) {
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000366
alision806e18e2013-06-21 15:30:17 -0400367 if (mCalls.get(0).isIncoming() && mCalls.get(0).isRinging()) {
368 initIncomingCallDisplay();
369 } else {
370 if (mCalls.get(0).isRinging()) {
alision1005ba12013-06-19 13:52:44 -0400371 initOutGoingCallDisplay();
372 }
alision806e18e2013-06-21 15:30:17 -0400373 try {
374 if (mCalls.get(0).isOutGoing() && mCallbacks.getService().getCall(mCalls.get(0).getCallId()) == null) {
375 mCallbacks.getService().placeCall(mCalls.get(0));
376 initOutGoingCallDisplay();
377 } else if (mCalls.get(0).isOutGoing() && mCalls.get(0).isRinging()) {
378 initOutGoingCallDisplay();
379 }
380 } catch (RemoteException e) {
381 Log.e(TAG, e.toString());
382 }
alision1005ba12013-06-19 13:52:44 -0400383 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000384
alision806e18e2013-06-21 15:30:17 -0400385 if (mCalls.get(0).isOngoing()) {
386 initNormalStateDisplay();
387 }
388 } else {
alision1005ba12013-06-19 13:52:44 -0400389 initNormalStateDisplay();
390 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000391
alision1005ba12013-06-19 13:52:44 -0400392 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000393
alision1005ba12013-06-19 13:52:44 -0400394 @Override
395 public void surfaceCreated(SurfaceHolder holder) {
396 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000397
alision1005ba12013-06-19 13:52:44 -0400398 @Override
399 public void surfaceDestroyed(SurfaceHolder holder) {
400 }
Adrien Béraud13cde0b2013-05-30 20:27:20 +1000401
alisione38001f2013-06-04 14:14:39 -0400402 public BubblesView getBubbleView() {
403 return view;
alisione38001f2013-06-04 14:14:39 -0400404
alision1005ba12013-06-19 13:52:44 -0400405 }
alision84813a12013-05-27 17:40:39 -0400406
alision84813a12013-05-27 17:40:39 -0400407}