blob: 82b357beccc2b8e99ee5e9ad6918f5a3251f4093 [file] [log] [blame]
alisionfde875f2013-05-28 17:01:54 -04001/*
Alexandre Lisionc1024c02014-01-06 11:12:53 -05002 * Copyright (C) 2004-2014 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 */
Alexandre Lision064e1e02013-10-01 16:18:42 -040031package org.sflphone.fragments;
alisionfde875f2013-05-28 17:01:54 -040032
alisionfde875f2013-05-28 17:01:54 -040033import android.app.Activity;
alision465ceba2013-07-04 09:24:30 -040034import android.content.ClipData;
35import android.content.ClipData.Item;
alisionb1763882013-06-18 17:30:51 -040036import android.content.Context;
37import android.content.Intent;
alision465ceba2013-07-04 09:24:30 -040038import android.graphics.Color;
Alexandre Lision183bf452014-01-17 11:21:59 -050039import android.os.*;
alisionfde875f2013-05-28 17:01:54 -040040import android.util.Log;
alision465ceba2013-07-04 09:24:30 -040041import android.view.DragEvent;
alisionfde875f2013-05-28 17:01:54 -040042import android.view.LayoutInflater;
43import android.view.View;
alision465ceba2013-07-04 09:24:30 -040044import android.view.View.DragShadowBuilder;
45import android.view.View.OnDragListener;
alisionfde875f2013-05-28 17:01:54 -040046import android.view.ViewGroup;
Alexandre Lision183bf452014-01-17 11:21:59 -050047import android.widget.*;
alision465ceba2013-07-04 09:24:30 -040048import android.widget.AdapterView.OnItemClickListener;
49import android.widget.AdapterView.OnItemLongClickListener;
Alexandre Lision183bf452014-01-17 11:21:59 -050050import org.sflphone.R;
51import org.sflphone.client.CallActivity;
52import org.sflphone.client.HomeActivity;
Alexandre Lision183bf452014-01-17 11:21:59 -050053import org.sflphone.model.Conference;
Alexandre Lision183bf452014-01-17 11:21:59 -050054import org.sflphone.service.ISipService;
alisionfde875f2013-05-28 17:01:54 -040055
Alexandre Lision183bf452014-01-17 11:21:59 -050056import java.util.ArrayList;
57import java.util.HashMap;
58import java.util.Observable;
59import java.util.Observer;
60
Alexandre Lision5f144b82014-02-11 09:59:36 -050061public class CallListFragment extends CallableWrapperFragment {
Alexandre Lision183bf452014-01-17 11:21:59 -050062
Alexandre Lisionf02190d2013-12-12 17:26:12 -050063 private static final String TAG = CallListFragment.class.getSimpleName();
alisionfde875f2013-05-28 17:01:54 -040064
65 private Callbacks mCallbacks = sDummyCallbacks;
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -050066 private TextView mConversationsTitleTextView;
67 CallListAdapter mConferenceAdapter;
alisionfde875f2013-05-28 17:01:54 -040068
alision1005ba12013-06-19 13:52:44 -040069 public static final int REQUEST_TRANSFER = 10;
70 public static final int REQUEST_CONF = 20;
71
alisionfde875f2013-05-28 17:01:54 -040072 /**
73 * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity.
74 */
75 private static Callbacks sDummyCallbacks = new Callbacks() {
76
77 @Override
78 public ISipService getService() {
Alexandre Lisionf02190d2013-12-12 17:26:12 -050079 Log.i(TAG, "I'm a dummy");
alisionfde875f2013-05-28 17:01:54 -040080 return null;
81 }
alision85992112013-05-29 12:18:08 -040082
alisionfde875f2013-05-28 17:01:54 -040083 };
84
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -050085 @Override
Alexandre Lision48b49eb2014-02-11 13:37:33 -050086 public void callStateChanged(String callID, String state) {
87 Log.i(TAG, "callStateChanged" + callID + " " + state);
Alexandre Lision183bf452014-01-17 11:21:59 -050088 updateLists();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -050089 }
90
91 @Override
Alexandre Lision48b49eb2014-02-11 13:37:33 -050092 public void confCreated(String id) {
Alexandre Lision183bf452014-01-17 11:21:59 -050093 Log.i(TAG, "confCreated");
94 updateLists();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -050095 }
96
97 @Override
Alexandre Lision48b49eb2014-02-11 13:37:33 -050098 public void confRemoved(String id) {
Alexandre Lision183bf452014-01-17 11:21:59 -050099 Log.i(TAG, "confRemoved");
100 updateLists();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500101 }
102
103 @Override
Alexandre Lision48b49eb2014-02-11 13:37:33 -0500104 public void confChanged(String id, String state) {
Alexandre Lision183bf452014-01-17 11:21:59 -0500105 Log.i(TAG, "confChanged");
106 updateLists();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500107 }
108
109 @Override
Alexandre Lision48b49eb2014-02-11 13:37:33 -0500110 public void recordingChanged(String callID, String filename) {
Alexandre Lision5f144b82014-02-11 09:59:36 -0500111 Log.i(TAG, "confChanged");
112 updateLists();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500113 }
114
alisionfde875f2013-05-28 17:01:54 -0400115 /**
116 * The Activity calling this fragment has to implement this interface
alisionfde875f2013-05-28 17:01:54 -0400117 */
118 public interface Callbacks {
119 public ISipService getService();
alisionfde875f2013-05-28 17:01:54 -0400120 }
121
122 @Override
123 public void onAttach(Activity activity) {
124 super.onAttach(activity);
125
126 if (!(activity instanceof Callbacks)) {
127 throw new IllegalStateException("Activity must implement fragment's callbacks.");
128 }
129
130 mCallbacks = (Callbacks) activity;
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500131
132 }
133
134 private Runnable mUpdateTimeTask = new Runnable() {
135 public void run() {
136 final long start = SystemClock.uptimeMillis();
137 long millis = SystemClock.uptimeMillis() - start;
138 int seconds = (int) (millis / 1000);
139 int minutes = seconds / 60;
140 seconds = seconds % 60;
141
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500142 mConferenceAdapter.notifyDataSetChanged();
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500143 mHandler.postAtTime(this, start + (((minutes * 60) + seconds + 1) * 1000));
144 }
145 };
146
147 private Handler mHandler = new Handler();
148
149 @Override
150 public void onResume() {
151 super.onResume();
152 if (mCallbacks.getService() != null) {
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500153
Alexandre Lision183bf452014-01-17 11:21:59 -0500154 updateLists();
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500155 if (!mConferenceAdapter.isEmpty()) {
Alexandre Lision183bf452014-01-17 11:21:59 -0500156 mHandler.postDelayed(mUpdateTimeTask, 0);
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500157 }
158 }
159
160 }
161
162 @SuppressWarnings("unchecked")
163 // No proper solution with HashMap runtime cast
Alexandre Lision183bf452014-01-17 11:21:59 -0500164 public void updateLists() {
Alexandre Lision183bf452014-01-17 11:21:59 -0500165 try {
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500166 HashMap<String, Conference> confs = (HashMap<String, Conference>) mCallbacks.getService().getConferenceList();
167 String newTitle = getResources().getQuantityString(R.plurals.home_conferences_title, confs.size(), confs.size());
168 mConversationsTitleTextView.setText(newTitle);
169 mConferenceAdapter.updateDataset(new ArrayList<Conference>(confs.values()));
Alexandre Lision183bf452014-01-17 11:21:59 -0500170 } catch (RemoteException e) {
171 e.printStackTrace();
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500172 }
alisionfde875f2013-05-28 17:01:54 -0400173 }
174
175 @Override
176 public void onDetach() {
177 super.onDetach();
178 mCallbacks = sDummyCallbacks;
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500179
alisionfde875f2013-05-28 17:01:54 -0400180 }
181
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500182 @Override
183 public void onCreate(Bundle savedInstanceState) {
184 super.onCreate(savedInstanceState);
185 }
186
187 @Override
188 public void onPause() {
189 super.onPause();
190 mHandler.removeCallbacks(mUpdateTimeTask);
191 }
192
193 @Override
194 public void onActivityCreated(Bundle savedInstanceState) {
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500195 super.onActivityCreated(savedInstanceState);
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500196 }
alisionb1763882013-06-18 17:30:51 -0400197
alisionfde875f2013-05-28 17:01:54 -0400198 @Override
199 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500200 Log.i(TAG, "onCreateView");
201 View inflatedView = inflater.inflate(R.layout.frag_call_list, container, false);
alisionfde875f2013-05-28 17:01:54 -0400202
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500203 mConversationsTitleTextView = (TextView) inflatedView.findViewById(R.id.confs_counter);
alisionb1763882013-06-18 17:30:51 -0400204
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500205 mConferenceAdapter = new CallListAdapter(getActivity());
206 ((ListView) inflatedView.findViewById(R.id.confs_list)).setAdapter(mConferenceAdapter);
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500207 ((ListView) inflatedView.findViewById(R.id.confs_list)).setOnItemClickListener(callClickListener);
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500208 ((ListView) inflatedView.findViewById(R.id.confs_list)).setOnItemLongClickListener(mItemLongClickListener);
209
210 return inflatedView;
211 }
212
213 OnItemClickListener callClickListener = new OnItemClickListener() {
214
215 @Override
216 public void onItemClick(AdapterView<?> arg0, View v, int arg2, long arg3) {
Alexandre Lision183bf452014-01-17 11:21:59 -0500217 Intent intent = new Intent().setClass(getActivity(), CallActivity.class);
218 intent.putExtra("resuming", true);
219 intent.putExtra("conference", (Conference) v.getTag());
220 startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL);
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500221 }
222 };
223
224 private OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
225
226 @Override
227 public boolean onItemLongClick(AdapterView<?> adptv, View view, int pos, long arg3) {
228 final Vibrator vibe = (Vibrator) view.getContext().getSystemService(Context.VIBRATOR_SERVICE);
229 vibe.vibrate(80);
230 Intent i = new Intent();
231 Bundle b = new Bundle();
232 b.putParcelable("conference", (Conference) adptv.getAdapter().getItem(pos));
233 i.putExtra("bconference", b);
234
235 DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
236 ClipData data = ClipData.newIntent("conference", i);
237 view.startDrag(data, shadowBuilder, view, 0);
238 return false;
239 }
240
241 };
242
243 public class CallListAdapter extends BaseAdapter implements Observer {
244
245 private ArrayList<Conference> calls;
246
247 private Context mContext;
248
249 public CallListAdapter(Context act) {
250 super();
251 mContext = act;
252 calls = new ArrayList<Conference>();
253
254 }
255
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500256 public void updateDataset(ArrayList<Conference> list) {
257 calls.clear();
258 calls.addAll(list);
259 notifyDataSetChanged();
260 }
261
262 @Override
263 public int getCount() {
264 return calls.size();
265 }
266
267 @Override
268 public Conference getItem(int position) {
269 return calls.get(position);
270 }
271
272 @Override
273 public long getItemId(int position) {
274 return 0;
275 }
276
277 @Override
278 public View getView(int position, View convertView, ViewGroup parent) {
279 if (convertView == null)
280 convertView = LayoutInflater.from(mContext).inflate(R.layout.item_calllist, null);
281
282 Conference call = calls.get(position);
283 if (call.getParticipants().size() == 1) {
284 ((TextView) convertView.findViewById(R.id.call_title)).setText(call.getParticipants().get(0).getContact().getmDisplayName());
285
Alexandre Lision945e4612014-01-15 17:40:31 -0500286 long duration = System.currentTimeMillis() / 1000 - (call.getParticipants().get(0).getTimestampStart_());
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500287
288 ((TextView) convertView.findViewById(R.id.call_time)).setText(String.format("%d:%02d:%02d", duration / 3600, (duration % 3600) / 60,
289 (duration % 60)));
290 } else {
291// String tmp = "Conference with " + call.getParticipants().size() + " participants";
292 ((TextView) convertView.findViewById(R.id.call_title)).setText(getString(R.string.home_conf_item, call.getParticipants().size()));
293 }
294 // ((TextView) convertView.findViewById(R.id.num_participants)).setText("" + call.getParticipants().size());
295 ((TextView) convertView.findViewById(R.id.call_status)).setText(call.getState());
296
297 convertView.setOnDragListener(dragListener);
298 convertView.setTag(call);
299
300 return convertView;
301 }
302
303 @Override
304 public void update(Observable observable, Object data) {
305 Log.i(TAG, "Updating views...");
306 notifyDataSetChanged();
307 }
308
alisionfde875f2013-05-28 17:01:54 -0400309 }
310
alision465ceba2013-07-04 09:24:30 -0400311 OnDragListener dragListener = new OnDragListener() {
312
Alexandre Lisionf02190d2013-12-12 17:26:12 -0500313 @SuppressWarnings("deprecation")
314 // deprecated in API 16....
alision465ceba2013-07-04 09:24:30 -0400315 @Override
316 public boolean onDrag(View v, DragEvent event) {
317 switch (event.getAction()) {
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500318 case DragEvent.ACTION_DRAG_STARTED:
319 // Do nothing
320 // Log.w(TAG, "ACTION_DRAG_STARTED");
321 break;
322 case DragEvent.ACTION_DRAG_ENTERED:
323 // Log.w(TAG, "ACTION_DRAG_ENTERED");
324 v.setBackgroundColor(Color.GREEN);
325 break;
326 case DragEvent.ACTION_DRAG_EXITED:
327 // Log.w(TAG, "ACTION_DRAG_EXITED");
328 v.setBackgroundDrawable(getResources().getDrawable(R.drawable.item_generic_selector));
329 break;
330 case DragEvent.ACTION_DROP:
331 // Log.w(TAG, "ACTION_DROP");
332 View view = (View) event.getLocalState();
alision465ceba2013-07-04 09:24:30 -0400333
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500334 Item i = event.getClipData().getItemAt(0);
335 Intent intent = i.getIntent();
336 intent.setExtrasClassLoader(Conference.class.getClassLoader());
alision465ceba2013-07-04 09:24:30 -0400337
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500338 Conference initial = (Conference) view.getTag();
339 Conference target = (Conference) v.getTag();
alisionf2ae4362013-07-05 16:16:12 -0400340
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500341 if (initial == target) {
342 return true;
343 }
alision465ceba2013-07-04 09:24:30 -0400344
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500345 DropActionsChoice dialog = DropActionsChoice.newInstance();
346 Bundle b = new Bundle();
347 b.putParcelable("call_initial", initial);
348 b.putParcelable("call_targeted", target);
349 dialog.setArguments(b);
350 dialog.setTargetFragment(CallListFragment.this, 0);
Alexandre Lisionf9885fb2014-01-21 11:57:51 -0500351 dialog.show(getFragmentManager(), "dialog");
alision465ceba2013-07-04 09:24:30 -0400352
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500353 // view.setBackgroundColor(Color.WHITE);
354 // v.setBackgroundColor(Color.BLACK);
355 break;
356 case DragEvent.ACTION_DRAG_ENDED:
357 // Log.w(TAG, "ACTION_DRAG_ENDED");
358 View view1 = (View) event.getLocalState();
359 view1.setVisibility(View.VISIBLE);
360 v.setBackgroundDrawable(getResources().getDrawable(R.drawable.item_generic_selector));
361 default:
362 break;
alision465ceba2013-07-04 09:24:30 -0400363 }
364 return true;
365 }
366
367 };
368
alisionb1763882013-06-18 17:30:51 -0400369 @Override
370 public void onActivityResult(int requestCode, int resultCode, Intent data) {
371 super.onActivityResult(requestCode, resultCode, data);
Alexandre Lision183bf452014-01-17 11:21:59 -0500372 Conference transfer;
alision1005ba12013-06-19 13:52:44 -0400373 if (requestCode == REQUEST_TRANSFER) {
374 switch (resultCode) {
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500375 case 0:
376 Conference c = data.getParcelableExtra("target");
377 transfer = data.getParcelableExtra("transfer");
378 try {
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500379 mCallbacks.getService().attendedTransfer(transfer.getParticipants().get(0).getCallId(), c.getParticipants().get(0).getCallId());
Alexandre Lisioncd6a82d2014-01-17 12:10:23 -0500380 mConferenceAdapter.notifyDataSetChanged();
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500381 } catch (RemoteException e) {
382 // TODO Auto-generated catch block
383 e.printStackTrace();
384 }
385 Toast.makeText(getActivity(), getString(R.string.home_transfer_complet), Toast.LENGTH_LONG).show();
386 break;
alision1005ba12013-06-19 13:52:44 -0400387
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500388 case 1:
389 String to = data.getStringExtra("to_number");
390 transfer = data.getParcelableExtra("transfer");
391 try {
392 Toast.makeText(getActivity(), getString(R.string.home_transfering, transfer.getParticipants().get(0).getContact().getmDisplayName(), to),
393 Toast.LENGTH_SHORT).show();
394 mCallbacks.getService().transfer(transfer.getParticipants().get(0).getCallId(), to);
395 mCallbacks.getService().hangUp(transfer.getParticipants().get(0).getCallId());
396 } catch (RemoteException e) {
397 // TODO Auto-generated catch block
398 e.printStackTrace();
399 }
400 break;
alision1005ba12013-06-19 13:52:44 -0400401
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500402 default:
403 break;
alisionb1763882013-06-18 17:30:51 -0400404 }
alision907bde72013-06-20 14:40:37 -0400405 } else if (requestCode == REQUEST_CONF) {
alision1005ba12013-06-19 13:52:44 -0400406 switch (resultCode) {
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500407 case 0:
408 Conference call_to_add = data.getParcelableExtra("transfer");
409 Conference call_target = data.getParcelableExtra("target");
alision1005ba12013-06-19 13:52:44 -0400410
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500411 bindCalls(call_to_add, call_target);
412 break;
alision1005ba12013-06-19 13:52:44 -0400413
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500414 default:
415 break;
alision1005ba12013-06-19 13:52:44 -0400416 }
alisionb1763882013-06-18 17:30:51 -0400417 }
418 }
419
alision465ceba2013-07-04 09:24:30 -0400420 private void bindCalls(Conference call_to_add, Conference call_target) {
421 try {
422
Alexandre Lision183bf452014-01-17 11:21:59 -0500423 Log.i(TAG, "joining calls:" + call_to_add.getId() + " and " + call_target.getId());
Alexandre Lisiona9ee4eb2014-01-15 16:20:35 -0500424
alision465ceba2013-07-04 09:24:30 -0400425 if (call_target.hasMultipleParticipants() && !call_to_add.hasMultipleParticipants()) {
426
427 mCallbacks.getService().addParticipant(call_to_add.getParticipants().get(0), call_target.getId());
428
429 } else if (call_target.hasMultipleParticipants() && call_to_add.hasMultipleParticipants()) {
430
431 // We join two conferences
432 mCallbacks.getService().joinConference(call_to_add.getId(), call_target.getId());
433
434 } else if (!call_target.hasMultipleParticipants() && call_to_add.hasMultipleParticipants()) {
435
436 mCallbacks.getService().addParticipant(call_target.getParticipants().get(0), call_to_add.getId());
437
438 } else {
439 // We join two single calls to create a conf
440 mCallbacks.getService().joinParticipant(call_to_add.getParticipants().get(0).getCallId(),
441 call_target.getParticipants().get(0).getCallId());
442 }
443
444 } catch (RemoteException e) {
445 // TODO Auto-generated catch block
446 e.printStackTrace();
447 }
448 }
449
alisionfde875f2013-05-28 17:01:54 -0400450}