/* $Id$ */ /* * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.pjsip.pjsua2.app; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.pjsip.pjsua2.*; public class MainActivity extends Activity implements Handler.Callback, MyAppObserver { public static MyApp app = null; public static MyCall currentCall = null; public static MyAccount account = null; public static AccountConfig accCfg = null; private ListView buddyListView; private SimpleAdapter buddyListAdapter; private int buddyListSelectedIdx = -1; ArrayList> buddyList; private String lastRegStatus = ""; private final Handler handler = new Handler(this); public class MSG_TYPE { public final static int INCOMING_CALL = 1; public final static int CALL_STATE = 2; public final static int REG_STATE = 3; public final static int BUDDY_STATE = 4; } private HashMap putData(String uri, String status) { HashMap item = new HashMap(); item.put("uri", uri); item.put("status", status); return item; } private void showCallActivity() { Intent intent = new Intent(this, CallActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (app == null) { app = new MyApp(); /* Wait for GDB to init */ if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { try { Thread.sleep(5000); } catch (InterruptedException e) {} } app.init(this, getFilesDir().getAbsolutePath()); } if (app.accList.size() == 0) { accCfg = new AccountConfig(); accCfg.setIdUri("sip:localhost"); account = app.addAcc(accCfg); } else { account = app.accList.get(0); accCfg = account.cfg; } buddyList = new ArrayList>(); for (int i = 0; i < account.buddyList.size(); i++) { buddyList.add(putData(account.buddyList.get(i).cfg.getUri(), account.buddyList.get(i).getStatusText())); } String[] from = { "uri", "status" }; int[] to = { android.R.id.text1, android.R.id.text2 }; buddyListAdapter = new SimpleAdapter(this, buddyList, android.R.layout.simple_list_item_2, from, to); buddyListView = (ListView) findViewById(R.id.listViewBuddy);; buddyListView.setAdapter(buddyListAdapter); buddyListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, final View view, int position, long id) { view.setSelected(true); buddyListSelectedIdx = position; } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_acc_config: dlgAccountSetting(); break; case R.id.action_quit: Message m = Message.obtain(handler, 0); m.sendToTarget(); break; default: break; } return true; } @Override public boolean handleMessage(Message m) { if (m.what == 0) { app.deinit(); finish(); Runtime.getRuntime().gc(); android.os.Process.killProcess(android.os.Process.myPid()); } else if (m.what == MSG_TYPE.CALL_STATE) { CallInfo ci = (CallInfo) m.obj; /* Forward the message to CallActivity */ if (CallActivity.handler_ != null) { Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_STATE, ci); m2.sendToTarget(); } if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) currentCall = null; } else if (m.what == MSG_TYPE.BUDDY_STATE) { MyBuddy buddy = (MyBuddy) m.obj; int idx = account.buddyList.indexOf(buddy); if (idx >= 0) { buddyList.get(idx).put("status", buddy.getStatusText()); buddyListAdapter.notifyDataSetChanged(); // TODO: selection color/mark is gone after this, // dont know how to return it back. //buddyListView.setSelection(buddyListSelectedIdx); //buddyListView.performItemClick(buddyListView, buddyListSelectedIdx, // buddyListView.getItemIdAtPosition(buddyListSelectedIdx)); /* Return back Call activity */ notifyCallState(currentCall); } } else if (m.what == MSG_TYPE.REG_STATE) { String msg_str = (String) m.obj; lastRegStatus = msg_str; } else if (m.what == MSG_TYPE.INCOMING_CALL) { /* Incoming call */ final MyCall call = (MyCall) m.obj; CallOpParam prm = new CallOpParam(); /* Only one call at anytime */ if (currentCall != null) { prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE); try { call.hangup(prm); } catch (Exception e) {} return true; } /* Answer with ringing */ prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING); try { call.answer(prm); } catch (Exception e) {} currentCall = call; showCallActivity(); } else { /* Message not handled */ return false; } return true; } private void dlgAccountSetting() { LayoutInflater li = LayoutInflater.from(this); View view = li.inflate(R.layout.dlg_account_config, null); if (!lastRegStatus.isEmpty()) { TextView tvInfo = (TextView)view.findViewById(R.id.textViewInfo); tvInfo.setText("Last status: " + lastRegStatus); } AlertDialog.Builder adb = new AlertDialog.Builder(this); adb.setView(view); adb.setTitle("Account Settings"); final EditText etId = (EditText)view.findViewById(R.id.editTextId); final EditText etReg = (EditText)view.findViewById(R.id.editTextRegistrar); final EditText etProxy = (EditText)view.findViewById(R.id.editTextProxy); final EditText etUser = (EditText)view.findViewById(R.id.editTextUsername); final EditText etPass = (EditText)view.findViewById(R.id.editTextPassword); etId. setText(accCfg.getIdUri()); etReg. setText(accCfg.getRegConfig().getRegistrarUri()); StringVector proxies = accCfg.getSipConfig().getProxies(); if (proxies.size() > 0) etProxy.setText(proxies.get(0)); else etProxy.setText(""); AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds(); if (creds.size() > 0) { etUser. setText(creds.get(0).getUsername()); etPass. setText(creds.get(0).getData()); } else { etUser. setText(""); etPass. setText(""); } adb.setCancelable(false); adb.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int id) { String acc_id = etId.getText().toString(); String registrar = etReg.getText().toString(); String proxy = etProxy.getText().toString(); String username = etUser.getText().toString(); String password = etPass.getText().toString(); accCfg.setIdUri(acc_id); accCfg.getRegConfig().setRegistrarUri(registrar); AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds(); creds.clear(); if (!username.isEmpty()) { creds.add(new AuthCredInfo("Digest", "*", username, 0, password)); } StringVector proxies = accCfg.getSipConfig().getProxies(); proxies.clear(); if (!proxy.isEmpty()) { proxies.add(proxy); } /* Finally */ lastRegStatus = ""; try { account.modify(accCfg); } catch (Exception e) {} } }); adb.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int id) { dialog.cancel(); } }); AlertDialog ad = adb.create(); ad.show(); } public void makeCall(View view) { if (buddyListSelectedIdx == -1) return; /* Only one call at anytime */ if (currentCall != null) { return; } HashMap item = (HashMap) buddyListView.getItemAtPosition(buddyListSelectedIdx); String buddy_uri = item.get("uri"); MyCall call = new MyCall(account, -1); CallOpParam prm = new CallOpParam(); CallSetting opt = prm.getOpt(); opt.setAudioCount(1); opt.setVideoCount(0); try { call.makeCall(buddy_uri, prm); } catch (Exception e) { currentCall = null; return; } currentCall = call; showCallActivity(); } private void dlgAddEditBuddy(BuddyConfig initial) { final BuddyConfig cfg = new BuddyConfig(); final BuddyConfig old_cfg = initial; final boolean is_add = initial == null; LayoutInflater li = LayoutInflater.from(this); View view = li.inflate(R.layout.dlg_add_buddy, null); AlertDialog.Builder adb = new AlertDialog.Builder(this); adb.setView(view); final EditText etUri = (EditText)view.findViewById(R.id.editTextUri); final CheckBox cbSubs = (CheckBox)view.findViewById(R.id.checkBoxSubscribe); if (is_add) { adb.setTitle("Add Buddy"); } else { adb.setTitle("Edit Buddy"); etUri. setText(initial.getUri()); cbSubs.setChecked(initial.getSubscribe()); } adb.setCancelable(false); adb.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int id) { cfg.setUri(etUri.getText().toString()); cfg.setSubscribe(cbSubs.isChecked()); if (is_add) { account.addBuddy(cfg); buddyList.add(putData(cfg.getUri(), "")); buddyListAdapter.notifyDataSetChanged(); buddyListSelectedIdx = -1; } else { if (!old_cfg.getUri().equals(cfg.getUri())) { account.delBuddy(buddyListSelectedIdx); account.addBuddy(cfg); buddyList.remove(buddyListSelectedIdx); buddyList.add(putData(cfg.getUri(), "")); buddyListAdapter.notifyDataSetChanged(); buddyListSelectedIdx = -1; } else if (old_cfg.getSubscribe() != cfg.getSubscribe()) { MyBuddy bud = account.buddyList.get(buddyListSelectedIdx); try { bud.subscribePresence(cfg.getSubscribe()); } catch (Exception e) {} } } } }); adb.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int id) { dialog.cancel(); } }); AlertDialog ad = adb.create(); ad.show(); } public void addBuddy(View view) { dlgAddEditBuddy(null); } public void editBuddy(View view) { if (buddyListSelectedIdx == -1) return; BuddyConfig old_cfg = account.buddyList.get(buddyListSelectedIdx).cfg; dlgAddEditBuddy(old_cfg); } public void delBuddy(View view) { if (buddyListSelectedIdx == -1) return; final HashMap item = (HashMap) buddyListView.getItemAtPosition(buddyListSelectedIdx); String buddy_uri = item.get("uri"); DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: account.delBuddy(buddyListSelectedIdx); buddyList.remove(item); buddyListAdapter.notifyDataSetChanged(); buddyListSelectedIdx = -1; break; case DialogInterface.BUTTON_NEGATIVE: break; } } }; AlertDialog.Builder adb = new AlertDialog.Builder(this); adb.setTitle(buddy_uri); adb.setMessage("\nDelete this buddy?\n"); adb.setPositiveButton("Yes", ocl); adb.setNegativeButton("No", ocl); adb.show(); } /* * === MyAppObserver === * * As we cannot do UI from worker thread, the callbacks mostly just send * a message to UI/main thread. */ public void notifyIncomingCall(MyCall call) { Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call); m.sendToTarget(); } public void notifyRegState(pjsip_status_code code, String reason, int expiration) { String msg_str = ""; if (expiration == 0) msg_str += "Unregistration"; else msg_str += "Registration"; if (code.swigValue()/100 == 2) msg_str += " successful"; else msg_str += " failed: " + reason; Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str); m.sendToTarget(); } public void notifyCallState(MyCall call) { if (currentCall == null || call.getId() != currentCall.getId()) return; CallInfo ci; try { ci = call.getInfo(); } catch (Exception e) { ci = null; } Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci); m.sendToTarget(); } public void notifyBuddyState(MyBuddy buddy) { Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy); m.sendToTarget(); } /* === end of MyAppObserver ==== */ }