diff --git a/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java b/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java deleted file mode 100644 index d65b0d877ded397f4b558ab11e5192baaa612786..0000000000000000000000000000000000000000 --- a/src/com/savoirfairelinux/sflphone/client/BubblesViewActivity.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.savoirfairelinux.sflphone.client; - -import android.app.Activity; -import android.graphics.PointF; -import android.os.Bundle; -import android.util.DisplayMetrics; -import android.util.Log; - -import com.savoirfairelinux.sflphone.R; -import com.savoirfairelinux.sflphone.model.Bubble; -import com.savoirfairelinux.sflphone.model.BubbleModel; -import com.savoirfairelinux.sflphone.model.BubblesView; - -public class BubblesViewActivity extends Activity { - private static final String TAG = BubblesViewActivity.class.getSimpleName(); - - BubblesView view; - - PointF screenCenter; - int radiusCalls; - double angle_part; - - BubbleModel model; - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.bubbleview_layout); - - model = new BubbleModel(); - DisplayMetrics metrics = getResources().getDisplayMetrics(); - screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3); - radiusCalls = metrics.widthPixels / 2 - 150; - // model.listBubbles.add(new Bubble(this, metrics.widthPixels / 2, metrics.heightPixels / 4, 150, R.drawable.me)); - // model.listBubbles.add(new Bubble(this, metrics.widthPixels / 2, metrics.heightPixels / 4 * 3, 150, R.drawable.callee)); - - view = (BubblesView) findViewById(R.id.main_view); - view.setModel(model); - /* - ((Button) findViewById(R.id.add_bubble)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - addBubble(); - } - }); - - ((Button) findViewById(R.id.remove_bubble)).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - removeBubble(); - } - }); - */ - } - - public void addBubble() { - /* - * Bubble.Builder builder = new Bubble.Builder(getContext()); builder.setRadiusPixels(200).setX(200).setY(300); - */ - DisplayMetrics metrics = getResources().getDisplayMetrics(); - Bubble b = new Bubble(this, metrics.widthPixels / 3, metrics.heightPixels / 4 * 3, 150, -1); - model.listBubbles.add(b); - - angle_part = 2*Math.PI / model.listBubbles.size(); - - double dX = 0; - double dY = 0; - for (int i = 0; i < model.listBubbles.size(); ++i) { - dX = Math.cos(angle_part * i) * radiusCalls; - dY = Math.sin(angle_part * i) * radiusCalls; - Log.i(TAG, "dX " + dX + " dY " + dY); - model.listBubbles.get(i).setAttractor(new PointF((int) dX + screenCenter.x, (int) dY + screenCenter.y)); - } - - // listBubbles.get(listBubbles.size() - 1).setRegion(width, height); - } - - public void removeBubble() { - - if (model.listBubbles.isEmpty()) { - return; - } - /* - * Bubble.Builder builder = new Bubble.Builder(getContext()); builder.setRadiusPixels(200).setX(200).setY(300); - */ - // DisplayMetrics metrics = getResources().getDisplayMetrics(); - // Bubble b = new Bubble(this, metrics.widthPixels / 3, metrics.heightPixels / 4 * 3, 150, -1); - synchronized (model) { - model.listBubbles.remove(model.listBubbles.size() - 1); - } - - if (model.listBubbles.isEmpty()) { - return; - } - - angle_part = 2*Math.PI / model.listBubbles.size(); - - Log.i(TAG, "Angle:" + angle_part); - double dX = 0; - double dY = 0; - for (int i = 0; i < model.listBubbles.size(); ++i) { - dX = Math.cos(angle_part * i) * radiusCalls; - dY = Math.sin(angle_part * i) * radiusCalls; - Log.i(TAG, "dX " + dX + " dY " + dY); - model.listBubbles.get(i).setAttractor(new PointF((int) dX + screenCenter.x, (int) dY + screenCenter.y)); - } - - // listBubbles.get(listBubbles.size() - 1).setRegion(width, height); - } -} \ No newline at end of file diff --git a/src/com/savoirfairelinux/sflphone/client/CallActivity.java b/src/com/savoirfairelinux/sflphone/client/CallActivity.java index df8c27fd8a33badc4505863eb2377119af73f84d..fd2f03821782d2a4409866d7a467d452744c88a1 100644 --- a/src/com/savoirfairelinux/sflphone/client/CallActivity.java +++ b/src/com/savoirfairelinux/sflphone/client/CallActivity.java @@ -42,7 +42,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.drm.DrmStore.Action; import android.graphics.Bitmap; import android.graphics.PointF; import android.os.Bundle; @@ -60,17 +59,16 @@ import com.savoirfairelinux.sflphone.model.Bubble; import com.savoirfairelinux.sflphone.model.BubbleModel; import com.savoirfairelinux.sflphone.model.BubblesView; import com.savoirfairelinux.sflphone.model.CallContact; -import com.savoirfairelinux.sflphone.model.CallContact.Phone; import com.savoirfairelinux.sflphone.model.SipCall; import com.savoirfairelinux.sflphone.service.ISipClient; import com.savoirfairelinux.sflphone.service.ISipService; import com.savoirfairelinux.sflphone.service.SipService; -public class CallActivity extends Activity //implements IncomingCallFragment.ICallActionListener, OngoingCallFragment.ICallActionListener //OnClickListener +public class CallActivity extends Activity { static final String TAG = "CallActivity"; private ISipService service; - private String pendingAction; + private String pendingAction = null; private SipCall mCall; private BubblesView view; @@ -78,7 +76,7 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa private PointF screenCenter; private DisplayMetrics metrics; - private HashMap<Bubble, CallContact> contacts = new HashMap<Bubble, CallContact>(); + private HashMap<CallContact, Bubble> contacts = new HashMap<CallContact, Bubble>(); private ExecutorService infos_fetcher = Executors.newCachedThreadPool(); @@ -137,7 +135,7 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa super.onCreate(savedInstanceState); setContentView(R.layout.bubbleview_layout); - model = new BubbleModel(); + model = new BubbleModel(getResources().getDisplayMetrics().density); metrics = getResources().getDisplayMetrics(); screenCenter = new PointF(metrics.widthPixels / 2, metrics.heightPixels / 3); //radiusCalls = metrics.widthPixels / 2 - 150; @@ -155,30 +153,30 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa // mCall = new SipCall(info); // Intent intent = new Intent(this, SipService.class); - + //setCallStateDisplay(mCall.getCallStateString()); pendingAction = b.getString("action"); - if(pendingAction.equals("call")) { + if(pendingAction != null && pendingAction.equals("call")) { CallContact contact = b.getParcelable("CallContact"); - + Log.i(TAG,"Calling "+ contact.getmDisplayName()); callContact(contact); -// SipCall.CallInfo info = new SipCall.CallInfo(); -// Random random = new Random(); -// String callID = Integer.toString(random.nextInt()); -// Phone phone = contact.getSipPhone(); - -// info.mCallID = callID; -// info.mAccountID = ""+contact.getId(); -// info.mDisplayName = contact.getmDisplayName(); -// info.mPhone = phone==null?null:phone.toString(); -// info.mEmail = contact.getmEmail(); -// info.mCallType = SipCall.CALL_TYPE_OUTGOING; - -// mCall = CallListReceiver.getCallInstance(info); - - + // SipCall.CallInfo info = new SipCall.CallInfo(); + // Random random = new Random(); + // String callID = Integer.toString(random.nextInt()); + // Phone phone = contact.getSipPhone(); + + // info.mCallID = callID; + // info.mAccountID = ""+contact.getId(); + // info.mDisplayName = contact.getmDisplayName(); + // info.mPhone = phone==null?null:phone.toString(); + // info.mEmail = contact.getmEmail(); + // info.mCallType = SipCall.CALL_TYPE_OUTGOING; + + // mCall = CallListReceiver.getCallInstance(info); + + //mCallbacks.onCallSelected(call); /* try { @@ -187,9 +185,9 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa Log.e(TAG, "Cannot call service method", e); }*/ bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - - } else if(pendingAction.equals("incoming")) { + } else if(pendingAction.equals("incoming")) { + callIncoming(); } /* @@ -219,8 +217,9 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa } })); + contact_bubble.contact = contact; model.listBubbles.add(contact_bubble); - contacts.put(contact_bubble, contact); + contacts.put(contact, contact_bubble); } private void callIncoming() { @@ -259,22 +258,24 @@ public class CallActivity extends Activity //implements IncomingCallFragment.ICa service = ISipService.Stub.asInterface(binder); try { service.registerClient(callback); - if(pendingAction.contentEquals("call")){ - - Log.i(TAG, "Placing call"); - Random random = new Random(); - String callID = Integer.toString(random.nextInt()); - SipCall.CallInfo info = new SipCall.CallInfo(); - info.mCallID = callID; - info.mAccountID = service.getAccountList().get(1).toString(); - info.mDisplayName = "Cool Guy!"; - info.mPhone = contacts.get(contacts.keySet().iterator().next()).getPhones().get(0).getNumber(); - info.mEmail = "coolGuy@coolGuy.com"; - info.mCallType = SipCall.CALL_TYPE_OUTGOING; - - mCall = CallListReceiver.getCallInstance(info); - - service.placeCall(info.mAccountID, info.mCallID, info.mPhone); + if(pendingAction != null && pendingAction.contentEquals("call")){ + + Log.i(TAG, "Placing call"); + CallContact contact = model.listBubbles.get(0).contact; + + String callID = Integer.toString(new Random().nextInt()); + SipCall.CallInfo info = new SipCall.CallInfo(); + info.mCallID = callID; + info.mAccountID = service.getAccountList().get(0).toString(); + info.mDisplayName = contact.getmDisplayName(); + info.mPhone = contact.getSipPhone().getNumber(); + info.mEmail = contact.getmEmail(); + info.mCallType = SipCall.CALL_TYPE_OUTGOING; + + mCall = CallListReceiver.getCallInstance(info); + + service.placeCall(info.mAccountID, info.mCallID, info.mPhone); + pendingAction = null; } } catch (RemoteException e) { Log.e(TAG, e.toString()); diff --git a/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java b/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java index fcf4eda5865a6e845186df25ce5f0d2e4e9b8b2f..a8d28abbca54c491c1bbe0daf6530d8d55395fc7 100644 --- a/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java +++ b/src/com/savoirfairelinux/sflphone/fragments/CallElementListFragment.java @@ -72,7 +72,6 @@ import android.widget.ToggleButton; import com.savoirfairelinux.sflphone.R; import com.savoirfairelinux.sflphone.account.AccountSelectionSpinner; import com.savoirfairelinux.sflphone.adapters.CallElementAdapter; -import com.savoirfairelinux.sflphone.client.BubblesViewActivity; import com.savoirfairelinux.sflphone.client.CallActivity; import com.savoirfairelinux.sflphone.client.SFLPhoneHomeActivity; import com.savoirfairelinux.sflphone.client.SFLPhonePreferenceActivity; @@ -85,393 +84,393 @@ import com.savoirfairelinux.sflphone.service.ISipService; * Main list of Call Elements. We don't manage contacts ourself so they are */ public class CallElementListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { - private static final String TAG = CallElementListFragment.class.getSimpleName(); - private CallElementAdapter mAdapter; - private String mCurFilter; - private SFLPhoneHomeActivity sflphoneHome; - private ISipService service; - ImageButton buttonCall; - Button attendedTransfer, conference; - EditText phoneField; - private AccountSelectionSpinner mAccountSelectionSpinner; - // private AccountListReceiver mAccountList; - private boolean isReady = false; - - static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY }; - static final String[] CONTACTS_PHONES_PROJECTION = new String[] { Phone.NUMBER, Phone.TYPE }; - static final String[] CONTACTS_SIP_PROJECTION = new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE }; - - private Callbacks mCallbacks = sDummyCallbacks; - private ToggleButton switchHold; - /** - * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity. - */ - private static Callbacks sDummyCallbacks = new Callbacks() { - @Override - public void onCallSelected(SipCall c) { - } - - @Override - public ISipService getService() { - // TODO Auto-generated method stub - return null; - } - }; - - /** - * The Activity calling this fragment has to implement this interface - * - */ - public interface Callbacks { - public void onCallSelected(SipCall c); - - public ISipService getService(); - - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - sflphoneHome = (SFLPhoneHomeActivity) activity; - service = ((SFLphoneApplication) sflphoneHome.getApplication()).getSipService(); - if (!(activity instanceof Callbacks)) { - throw new IllegalStateException("Activity must implement fragment's callbacks."); - } - - mCallbacks = (Callbacks) activity; - } - - @Override - public void onDetach() { - super.onDetach(); - mCallbacks = sDummyCallbacks; - } - - public String getSelectedAccount() { - return mAccountSelectionSpinner.getAccount(); - } - - /** - * Runnable that fill information in a contact card asynchroniously. - */ - /* - * public static class InfosLoader implements Runnable { private View view; private long cid; private ContentResolver cr; - * - * public InfosLoader(Context context, View element, long contact_id) { cid = contact_id; cr = context.getContentResolver(); view = element; } - * - * public static Bitmap loadContactPhoto(ContentResolver cr, long id) { Uri uri = - * ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id); InputStream input = - * ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); if (input == null) { return null; } return BitmapFactory.decodeStream(input); } - * - * @Override public void run() { final Bitmap photo_bmp = loadContactPhoto(cr, cid); - * - * Cursor phones = cr.query(CommonDataKinds.Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] - * { Long.toString(cid) }, null); - * - * final List<String> numbers = new ArrayList<String>(); while (phones.moveToNext()) { String number = - * phones.getString(phones.getColumnIndex(CommonDataKinds.Phone.NUMBER)); // int type = - * phones.getInt(phones.getColumnIndex(CommonDataKinds.Phone.TYPE)); numbers.add(number); } phones.close(); // TODO: same for SIP adresses. - * - * final Bitmap bmp = photo_bmp; view.post(new Runnable() { - * - * @Override public void run() { } }); } } - */ - - public CallElementListFragment() { - super(); - } - - public void addCall(SipCall c) { - Log.i(TAG, "Adding call " + c.mCallInfo.mDisplayName); - mAdapter.add(c); - } - - // public void removeCall(SipCall c) { - // Log.i(TAG, "Removing call " + c.mCallInfo.mDisplayName); - // mAdapter.remove(c); - // } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - // setEmptyText("No phone numbers"); - - // We have a menu item to show in action bar. - setHasOptionsMenu(true); - - // Create an empty adapter we will use to display the loaded data. - ArrayList<SipCall> calls = new ArrayList<SipCall>(); - mAdapter = new CallElementAdapter(getActivity(), calls); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - // setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - // getLoaderManager().initLoader(0, null, this) - - final Context context = getActivity(); - ListView lv = getListView(); - lv.setOnItemLongClickListener(new OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) { - Log.i(TAG, "On Long Click"); - final CharSequence[] items = { "Hang up Call", "Send Message", "Add to Conference" }; - final SipCall call = mAdapter.getItem(pos); - // // FIXME - // service = sflphoneApplication.getSipService(); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle("Action to perform with " + call.mCallInfo.mDisplayName).setCancelable(true) - .setItems(items, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int item) { - Log.i(TAG, "Selected " + items[item]); - switch (item) { - case 0: - call.notifyServiceHangup(service); - break; - case 1: - call.sendTextMessage(); - // Need to hangup this call immediately since no way to do it after this action - call.notifyServiceHangup(service); - break; - case 2: - call.addToConference(); - // Need to hangup this call immediately since no way to do it after this action - call.notifyServiceHangup(service); - break; - default: - break; - } - } - }); - AlertDialog alert = builder.create(); - alert.show(); - - return true; - } - }); - - lv.setOnItemClickListener(new OnItemClickListener() { - - @Override - public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) { - mCallbacks.onCallSelected(mAdapter.getItem(pos)); - - } - - }); - } - - private void launchCallActivity(SipCall call) { - Log.i(TAG, "Launch Call Activity"); - Bundle bundle = new Bundle(); - bundle.putParcelable("CallInfo", call.mCallInfo); - Intent intent = new Intent().setClass(getActivity(), CallActivity.class); - intent.putExtras(bundle); - getActivity().startActivity(intent); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.call_element_menu, menu); - - } - - private static final int REQUEST_CODE_PREFERENCES = 1; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Log.i(TAG, "onOptionsItemSelected " + item.getItemId()); - switch (item.getItemId()) { - case R.id.menu_settings: - Intent launchPreferencesIntent = new Intent().setClass(getActivity(), SFLPhonePreferenceActivity.class); - startActivityForResult(launchPreferencesIntent, REQUEST_CODE_PREFERENCES); - break; - } - - return super.onOptionsItemSelected(item); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Log.i(TAG, "onCreateView"); - View inflatedView = inflater.inflate(R.layout.frag_call_element, container, false); - - mAccountSelectionSpinner = (AccountSelectionSpinner) inflatedView.findViewById(R.id.account_selection_button); - phoneField = (EditText) inflatedView.findViewById(R.id.phoneNumberTextEntry); - buttonCall = (ImageButton) inflatedView.findViewById(R.id.buttonCall); - - buttonCall.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - processingNewCallAction(); - } - }); - - attendedTransfer = (Button) inflatedView.findViewById(R.id.button_attended); - attendedTransfer.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (mAdapter.getCount() == 2) { - try { - service.attendedTransfer(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId()); - mAdapter.clear(); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Toast.makeText(getActivity(), "You need two calls one on Hold the other current to bind them", Toast.LENGTH_LONG).show(); - } - - } - }); - - conference = (Button) inflatedView.findViewById(R.id.button_conf); - conference.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (mAdapter.getCount() == 2) { - try { - service.joinParticipant(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId()); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - } else { - Toast.makeText(getActivity(), "You need two calls one on Hold the other current to create a conference", Toast.LENGTH_LONG) - .show(); - } - } - }); - - switchHold = (ToggleButton) inflatedView.findViewById(R.id.switch_hold); - switchHold.setOnCheckedChangeListener(new OnCheckedChangeListener() { - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - try { - ArrayList<String> confList = (ArrayList<String>) service.getConferenceList(); - if (!confList.isEmpty()) { - if (isChecked) { - service.holdConference(confList.get(0)); - } else { - service.unholdConference(confList.get(0)); - } - } - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - - } - }); - - isReady = true; - if (mCallbacks.getService() != null) { - - onServiceSipBinded(mCallbacks.getService()); - } - return inflatedView; - } - - public void processingNewCallAction() { - // String accountID = mAccountList.currentAccountID; - String accountID = mAccountSelectionSpinner.getAccount(); - - String to = phoneField.getText().toString(); - - Random random = new Random(); - String callID = Integer.toString(random.nextInt()); - SipCall.CallInfo info = new SipCall.CallInfo(); - - info.mCallID = callID; - info.mAccountID = accountID; - info.mDisplayName = "Cool Guy!"; - info.mPhone = to; - info.mEmail = "coolGuy@coolGuy.com"; - info.mCallType = SipCall.CALL_TYPE_OUTGOING; - - SipCall call = CallListReceiver.getCallInstance(info); - mCallbacks.onCallSelected(call); - - try { - service.placeCall(info.mAccountID, info.mCallID, info.mPhone); - } catch (RemoteException e) { - Log.e(TAG, "Cannot call service method", e); - } - addCall(call); - } - - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - - Log.i(TAG, "onCreateLoader"); - Uri baseUri; - - if (mCurFilter != null) { - baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); - } else { - baseUri = Contacts.CONTENT_URI; - } - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME - + " != '' ))"; - // String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; - - return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - Log.i(TAG, "onLoadFinished"); - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - // mAdapter.swapCursor(data); - - // The list should now be shown. - /* - * if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } - */ - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - // mAdapter.swapCursor(null); - } - - /** - * Called by activity to pass a reference to sipservice to Fragment. - * - * @param isip - */ - public void onServiceSipBinded(ISipService isip) { - - if (isReady) { - service = isip; - ArrayList<String> accountList; - try { - accountList = (ArrayList<String>) mCallbacks.getService().getAccountList(); - mAccountSelectionSpinner.populate(mCallbacks.getService(), accountList); - } catch (RemoteException e) { - Log.i(TAG, e.toString()); - } - } - - } - - public void updateCall(String iD, String newState) { - mAdapter.update(iD, newState); - - } + private static final String TAG = CallElementListFragment.class.getSimpleName(); + private CallElementAdapter mAdapter; + private String mCurFilter; + private SFLPhoneHomeActivity sflphoneHome; + private ISipService service; + ImageButton buttonCall; + Button attendedTransfer, conference; + EditText phoneField; + private AccountSelectionSpinner mAccountSelectionSpinner; + // private AccountListReceiver mAccountList; + private boolean isReady = false; + + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY }; + static final String[] CONTACTS_PHONES_PROJECTION = new String[] { Phone.NUMBER, Phone.TYPE }; + static final String[] CONTACTS_SIP_PROJECTION = new String[] { SipAddress.SIP_ADDRESS, SipAddress.TYPE }; + + private Callbacks mCallbacks = sDummyCallbacks; + private ToggleButton switchHold; + /** + * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity. + */ + private static Callbacks sDummyCallbacks = new Callbacks() { + @Override + public void onCallSelected(SipCall c) { + } + + @Override + public ISipService getService() { + // TODO Auto-generated method stub + return null; + } + }; + + /** + * The Activity calling this fragment has to implement this interface + * + */ + public interface Callbacks { + public void onCallSelected(SipCall c); + + public ISipService getService(); + + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + sflphoneHome = (SFLPhoneHomeActivity) activity; + service = ((SFLphoneApplication) sflphoneHome.getApplication()).getSipService(); + if (!(activity instanceof Callbacks)) { + throw new IllegalStateException("Activity must implement fragment's callbacks."); + } + + mCallbacks = (Callbacks) activity; + } + + @Override + public void onDetach() { + super.onDetach(); + mCallbacks = sDummyCallbacks; + } + + public String getSelectedAccount() { + return mAccountSelectionSpinner.getAccount(); + } + + /** + * Runnable that fill information in a contact card asynchroniously. + */ + /* + * public static class InfosLoader implements Runnable { private View view; private long cid; private ContentResolver cr; + * + * public InfosLoader(Context context, View element, long contact_id) { cid = contact_id; cr = context.getContentResolver(); view = element; } + * + * public static Bitmap loadContactPhoto(ContentResolver cr, long id) { Uri uri = + * ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id); InputStream input = + * ContactsContract.Contacts.openContactPhotoInputStream(cr, uri); if (input == null) { return null; } return BitmapFactory.decodeStream(input); } + * + * @Override public void run() { final Bitmap photo_bmp = loadContactPhoto(cr, cid); + * + * Cursor phones = cr.query(CommonDataKinds.Phone.CONTENT_URI, CONTACTS_PHONES_PROJECTION, CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] + * { Long.toString(cid) }, null); + * + * final List<String> numbers = new ArrayList<String>(); while (phones.moveToNext()) { String number = + * phones.getString(phones.getColumnIndex(CommonDataKinds.Phone.NUMBER)); // int type = + * phones.getInt(phones.getColumnIndex(CommonDataKinds.Phone.TYPE)); numbers.add(number); } phones.close(); // TODO: same for SIP adresses. + * + * final Bitmap bmp = photo_bmp; view.post(new Runnable() { + * + * @Override public void run() { } }); } } + */ + + public CallElementListFragment() { + super(); + } + + public void addCall(SipCall c) { + Log.i(TAG, "Adding call " + c.mCallInfo.mDisplayName); + mAdapter.add(c); + } + + // public void removeCall(SipCall c) { + // Log.i(TAG, "Removing call " + c.mCallInfo.mDisplayName); + // mAdapter.remove(c); + // } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + // setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + ArrayList<SipCall> calls = new ArrayList<SipCall>(); + mAdapter = new CallElementAdapter(getActivity(), calls); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + // setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + // getLoaderManager().initLoader(0, null, this) + + final Context context = getActivity(); + ListView lv = getListView(); + lv.setOnItemLongClickListener(new OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) { + Log.i(TAG, "On Long Click"); + final CharSequence[] items = { "Hang up Call", "Send Message", "Add to Conference" }; + final SipCall call = mAdapter.getItem(pos); + // // FIXME + // service = sflphoneApplication.getSipService(); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Action to perform with " + call.mCallInfo.mDisplayName).setCancelable(true) + .setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + Log.i(TAG, "Selected " + items[item]); + switch (item) { + case 0: + call.notifyServiceHangup(service); + break; + case 1: + call.sendTextMessage(); + // Need to hangup this call immediately since no way to do it after this action + call.notifyServiceHangup(service); + break; + case 2: + call.addToConference(); + // Need to hangup this call immediately since no way to do it after this action + call.notifyServiceHangup(service); + break; + default: + break; + } + } + }); + AlertDialog alert = builder.create(); + alert.show(); + + return true; + } + }); + + lv.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) { + mCallbacks.onCallSelected(mAdapter.getItem(pos)); + + } + + }); + } + + private void launchCallActivity(SipCall call) { + Log.i(TAG, "Launch Call Activity"); + Bundle bundle = new Bundle(); + bundle.putParcelable("CallInfo", call.mCallInfo); + Intent intent = new Intent().setClass(getActivity(), CallActivity.class); + intent.putExtras(bundle); + getActivity().startActivity(intent); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.call_element_menu, menu); + + } + + private static final int REQUEST_CODE_PREFERENCES = 1; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Log.i(TAG, "onOptionsItemSelected " + item.getItemId()); + switch (item.getItemId()) { + case R.id.menu_settings: + Intent launchPreferencesIntent = new Intent().setClass(getActivity(), SFLPhonePreferenceActivity.class); + startActivityForResult(launchPreferencesIntent, REQUEST_CODE_PREFERENCES); + break; + } + + return super.onOptionsItemSelected(item); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Log.i(TAG, "onCreateView"); + View inflatedView = inflater.inflate(R.layout.frag_call_element, container, false); + + mAccountSelectionSpinner = (AccountSelectionSpinner) inflatedView.findViewById(R.id.account_selection_button); + phoneField = (EditText) inflatedView.findViewById(R.id.phoneNumberTextEntry); + buttonCall = (ImageButton) inflatedView.findViewById(R.id.buttonCall); + + buttonCall.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + processingNewCallAction(); + } + }); + + attendedTransfer = (Button) inflatedView.findViewById(R.id.button_attended); + attendedTransfer.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (mAdapter.getCount() == 2) { + try { + service.attendedTransfer(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId()); + mAdapter.clear(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Toast.makeText(getActivity(), "You need two calls one on Hold the other current to bind them", Toast.LENGTH_LONG).show(); + } + + } + }); + + conference = (Button) inflatedView.findViewById(R.id.button_conf); + conference.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + if (mAdapter.getCount() == 2) { + try { + service.joinParticipant(mAdapter.getItem(0).getCallId(), mAdapter.getItem(1).getCallId()); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Toast.makeText(getActivity(), "You need two calls one on Hold the other current to create a conference", Toast.LENGTH_LONG) + .show(); + } + } + }); + + switchHold = (ToggleButton) inflatedView.findViewById(R.id.switch_hold); + switchHold.setOnCheckedChangeListener(new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + try { + ArrayList<String> confList = (ArrayList<String>) service.getConferenceList(); + if (!confList.isEmpty()) { + if (isChecked) { + service.holdConference(confList.get(0)); + } else { + service.unholdConference(confList.get(0)); + } + } + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + + } + }); + + isReady = true; + if (mCallbacks.getService() != null) { + + onServiceSipBinded(mCallbacks.getService()); + } + return inflatedView; + } + + public void processingNewCallAction() { + // String accountID = mAccountList.currentAccountID; + String accountID = mAccountSelectionSpinner.getAccount(); + + String to = phoneField.getText().toString(); + + Random random = new Random(); + String callID = Integer.toString(random.nextInt()); + SipCall.CallInfo info = new SipCall.CallInfo(); + + info.mCallID = callID; + info.mAccountID = accountID; + info.mDisplayName = "Cool Guy!"; + info.mPhone = to; + info.mEmail = "coolGuy@coolGuy.com"; + info.mCallType = SipCall.CALL_TYPE_OUTGOING; + + SipCall call = CallListReceiver.getCallInstance(info); + mCallbacks.onCallSelected(call); + + try { + service.placeCall(info.mAccountID, info.mCallID, info.mPhone); + } catch (RemoteException e) { + Log.e(TAG, "Cannot call service method", e); + } + addCall(call); + } + + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + + Log.i(TAG, "onCreateLoader"); + Uri baseUri; + + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + + " != '' ))"; + // String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; + + return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + Log.i(TAG, "onLoadFinished"); + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + // mAdapter.swapCursor(data); + + // The list should now be shown. + /* + * if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } + */ + } + + @Override + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + // mAdapter.swapCursor(null); + } + + /** + * Called by activity to pass a reference to sipservice to Fragment. + * + * @param isip + */ + public void onServiceSipBinded(ISipService isip) { + + if (isReady) { + service = isip; + ArrayList<String> accountList; + try { + accountList = (ArrayList<String>) mCallbacks.getService().getAccountList(); + mAccountSelectionSpinner.populate(mCallbacks.getService(), accountList); + } catch (RemoteException e) { + Log.i(TAG, e.toString()); + } + } + + } + + public void updateCall(String iD, String newState) { + mAdapter.update(iD, newState); + + } } diff --git a/src/com/savoirfairelinux/sflphone/model/Bubble.java b/src/com/savoirfairelinux/sflphone/model/Bubble.java index 41dc4bc0e07b89a54caa3fc25f910e91e9e52e8c..0dbd013a909f0374e788b5c71cee1d0585fff6fc 100644 --- a/src/com/savoirfairelinux/sflphone/model/Bubble.java +++ b/src/com/savoirfairelinux/sflphone/model/Bubble.java @@ -15,12 +15,15 @@ import com.savoirfairelinux.sflphone.R; public class Bubble { + public CallContact contact; // A Bitmap object that is going to be passed to the BitmapShader private Bitmap internalBMP, externalBMP; private PointF pos = new PointF(); private RectF bounds; - private float radius; + public float target_scale = 1.f; + private final float radius; + private float scale = 1.f; public PointF speed = new PointF(0, 0); public PointF last_speed = new PointF(); public PointF attractor = null; @@ -85,6 +88,17 @@ public class Bubble return bounds; } + public void set(float x, float y, float s) { + scale = s; + pos.x = x; + pos.y = y; + float rad = scale*radius; + bounds.left = pos.x - rad; + bounds.right = pos.x + rad; + bounds.top = pos.y - rad; + bounds.bottom = pos.y + rad; + } + public float getPosX() { return pos.x; } @@ -94,16 +108,7 @@ public class Bubble } public void setPos(float x, float y) { - pos.x = x; - pos.y = y; - bounds.left = pos.x - radius; - bounds.right = pos.x + radius; - bounds.top = pos.y - radius; - bounds.bottom = pos.y + radius; - } - - public float getRadius() { - return radius; + set(x, y, scale); } public PointF getPos() @@ -111,6 +116,18 @@ public class Bubble return pos; } + public float getScale() { + return scale; + } + + public void setScale(float s) { + set(pos.x, pos.y, s); + } + + public float getRadius() { + return radius; + } + /** * Point intersection test. */ diff --git a/src/com/savoirfairelinux/sflphone/model/BubbleModel.java b/src/com/savoirfairelinux/sflphone/model/BubbleModel.java index 5e785ae6c6c4afd88f408eaa807303d7dde167e2..460d9e97caace322ad2d9affa1fffb09b1fa4a80 100644 --- a/src/com/savoirfairelinux/sflphone/model/BubbleModel.java +++ b/src/com/savoirfairelinux/sflphone/model/BubbleModel.java @@ -3,7 +3,6 @@ package com.savoirfairelinux.sflphone.model; import java.util.ArrayList; import android.graphics.PointF; -import android.util.Log; public class BubbleModel { @@ -14,18 +13,35 @@ public class BubbleModel public ArrayList<Bubble> listBubbles = new ArrayList<Bubble>(); public ArrayList<Attractor> attractors = new ArrayList<Attractor>(); - private static final float ATTRACTOR_DIST_SUCK = 20.f; - - private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .25; + private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .3; private static final double BUBBLE_RETURN_TIME_LAMBDA = Math.log(2)/BUBBLE_RETURN_TIME_HALF_LIFE; - /*private static final float FRICTION_VISCOUS = .5f; // Viscous friction factor + private static final double FRICTION_VISCOUS = Math.log(2)/.2f; // Viscous friction factor + + private static final float BUBBLE_MAX_SPEED = 2500.f; // px.s-1 : Max target speed in px/sec + private static final float ATTRACTOR_SMOOTH_DIST = 50.f; // px : Size of the "gravity hole" around the attractor + private static final float ATTRACTOR_STALL_DIST = 15.f; // px : Size of the "gravity hole" flat bottom + private static final float ATTRACTOR_DIST_SUCK = 20.f; // px + + private static final float BORDER_REPULSION = 60000; // px.s^-2 + + private final float border_repulsion; + private final float bubble_max_speed; + private final float attractor_smooth_dist; + private final float attractor_stall_dist; + private final float attractor_dist_suck; + + private float density = 1.f; + + public BubbleModel(float screen_density) { + this.density = screen_density; + attractor_dist_suck = ATTRACTOR_DIST_SUCK*density; + bubble_max_speed = BUBBLE_MAX_SPEED*density; + attractor_smooth_dist = ATTRACTOR_SMOOTH_DIST*density; + attractor_stall_dist = ATTRACTOR_STALL_DIST*density; + border_repulsion = BORDER_REPULSION*density; + } - private static final float BUBBLE_MAX_SPEED = 2500.f; // Max target speed in px/sec - private static final float ATTRACTOR_SMOOTH_DIST = 100.f;// Size of the "gravity hole" around the attractor - private static final float ATTRACTOR_STALL_DIST = 15.f; // Size of the "gravity hole" flat bottom - private static final float ATTRACTOR_ACCEL = 10.f; // Acceleration factor towards target speed - */ public void update() { long now = System.nanoTime(); @@ -65,76 +81,66 @@ public class BubbleModel } } - double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA*dt); - double dx = (attractor_pos.x - bx) * edt; - double dy = (attractor_pos.y - by) * edt; -// Log.w(TAG, "update dx="+dt+" dy="+dy); - b.setPos((float)(bx+dx), (float)(by+dy)); - - if(attractor != null && attractor_dist < ATTRACTOR_DIST_SUCK*ATTRACTOR_DIST_SUCK) { - attractor.callback.onBubbleSucked(b); - listBubbles.remove(b); - n--; - } - - /* float bx=b.getPosX(), by=b.getPosY(); - /// Apply viscous friction - float friction_coef = 1.f-FRICTION_VISCOUS*dt; - float tdx = b.attractor.x - bx, tdy = b.attractor.y - by; - float dist = (float) Math.sqrt(tdx*tdx + tdy*tdy); - float speed = (float)Math.sqrt(b.speed.x*b.speed.x + b.speed.y*b.speed.y); - + //float friction_coef = 1.f-FRICTION_VISCOUS*dt; + double friction_coef = 1+Math.expm1(-FRICTION_VISCOUS*ddt); b.speed.x *= friction_coef; b.speed.y *= friction_coef; + //if(attractor != null) { + float target_speed; + float tdx = attractor_pos.x - bx, tdy = attractor_pos.y - by; + float dist = Math.max(1.f, (float) Math.sqrt(tdx*tdx + tdy*tdy)); + if(dist > attractor_smooth_dist) + target_speed = bubble_max_speed; + else if(dist < attractor_stall_dist) + target_speed = 0; + else { + float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist); + target_speed = bubble_max_speed*a; + } + if(attractor != null) { + if(dist > attractor_smooth_dist) + b.target_scale = 1.f; + else if(dist < attractor_stall_dist) + b.target_scale = .2f; + else { + float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist); + b.target_scale = a*.8f+.2f; + } - if(speed > 10.f || dist > ATTRACTOR_STALL_DIST) { - dist = Math.max(1.f, dist); // Avoid division by 0 - - b.speed.x *= friction_coef; - b.speed.y *= friction_coef; - - // Target speed (defines the "gravity hole") - float target_speed; - if(dist > ATTRACTOR_SMOOTH_DIST) - target_speed = BUBBLE_MAX_SPEED; - else if(dist < ATTRACTOR_STALL_DIST) - target_speed = 0; - else - target_speed = BUBBLE_MAX_SPEED/(ATTRACTOR_SMOOTH_DIST-ATTRACTOR_STALL_DIST)*(dist-ATTRACTOR_STALL_DIST); - - float target_speed_x = target_speed*tdx/dist; - float target_speed_y = target_speed*tdy/dist; - - // Acceleration - float ax = (target_speed_x-b.speed.x) * ATTRACTOR_ACCEL;// + 2*(b.last_speed.x-b.speed.x)*(1-FRICTION_VISCOUS)/FRICTION_VISCOUS*60.f; - float ay = (target_speed_y-b.speed.y) * ATTRACTOR_ACCEL;// + 2*(b.last_speed.y-b.speed.y)*(1-FRICTION_VISCOUS)/FRICTION_VISCOUS*60.f; - - // Speed update - - b.speed.x += ax*dt; - b.speed.y += ay*dt; - b.last_speed.set(b.speed); - Log.w(TAG, "dist " + dist + " speed " + Math.sqrt(b.speed.x*b.speed.x + b.speed.y*b.speed.y) + " target speed "+target_speed); - - // Position update - float dx = b.speed.x * dt; - float dy = b.speed.y * dt; - b.setPos(bx+dx, by+dy); + } + // border repulsion + if(bx < 0 && b.speed.x < 0) { + b.speed.x += dt * border_repulsion; + } else if(bx > width && b.speed.x > 0) { + b.speed.x -= dt * border_repulsion; + } + if(by < 0 && b.speed.y < 0) { + b.speed.y += dt * border_repulsion; + } else if(by > height && b.speed.y > 0) { + b.speed.y -= dt * border_repulsion; } - // Prevent speed higher than BUBBLE_MAX_SPEED - float ds = (target_speed-speed)*dt; - // Set motion direction and speed - float nsr = (speed>BUBBLE_MAX_SPEED ? BUBBLE_MAX_SPEED : speed+ds)/(dist < 1.f ? 1.f : dist); - b.speed.x = tdx * nsr; - b.speed.y = tdy * nsr;*/ + b.speed.x += dt * target_speed * tdx/dist; + b.speed.y += dt * target_speed * tdy/dist; + double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA*ddt); + double dx = (attractor_pos.x - bx) * edt + Math.min(bubble_max_speed, b.speed.x) * dt; + double dy = (attractor_pos.y - by) * edt + Math.min(bubble_max_speed, b.speed.y) * dt; + // Log.w(TAG, "update dx="+dt+" dy="+dy); + b.setPos((float)(bx+dx), (float)(by+dy)); + if(attractor != null && attractor_dist < attractor_dist_suck*attractor_dist_suck) { + attractor.callback.onBubbleSucked(b); + listBubbles.remove(b); + n--; + } } + b.setScale(b.getScale() + (b.target_scale-b.getScale())*dt*10.f); + } } } diff --git a/src/com/savoirfairelinux/sflphone/model/BubblesView.java b/src/com/savoirfairelinux/sflphone/model/BubblesView.java index e0472ccdfdce65f579119e452e4ef44c080a924c..12432751d753ebf4771a06358fddd9f22f742ba7 100644 --- a/src/com/savoirfairelinux/sflphone/model/BubblesView.java +++ b/src/com/savoirfairelinux/sflphone/model/BubblesView.java @@ -4,6 +4,8 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.RectF; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; @@ -22,11 +24,18 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, private BubbleModel model; private Paint attractor_paint = new Paint(); + private Paint name_paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private float density; + private float textDensity; public BubblesView(Context context, AttributeSet attrs) { super(context, attrs); + density = getResources().getDisplayMetrics().density; + textDensity = getResources().getDisplayMetrics().scaledDensity; + SurfaceHolder holder = getHolder(); holder.addCallback(this); @@ -38,6 +47,9 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, attractor_paint.setColor(Color.RED); //attractor_paint.set + name_paint.setTextSize(20*textDensity); + name_paint.setColor(0xFF303030); + name_paint.setTextAlign(Align.CENTER); } private void createThread() @@ -127,13 +139,15 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, if (b.intersects(event.getX(), event.getY())) { b.dragged = true; b.last_drag = System.nanoTime(); + b.setPos(event.getX(), event.getY()); + b.target_scale = .8f; } } } else if (action == MotionEvent.ACTION_MOVE) { + long now = System.nanoTime(); for (Bubble b : model.listBubbles) { if (b.dragged) { float x = event.getX(), y = event.getY(); - long now = System.nanoTime(); float dt = (float) ((now-b.last_drag)/1000000000.); float dx = x - b.getPosX(), dy = y - b.getPosY(); b.last_drag = now; @@ -156,6 +170,7 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, for (Bubble b : model.listBubbles) { if (b.dragged) { b.dragged = false; + b.target_scale = 1.f; } } } @@ -236,7 +251,17 @@ public class BubblesView extends SurfaceView implements SurfaceHolder.Callback, for (int i = 0; i < model.listBubbles.size(); i++) { Bubble b = model.listBubbles.get(i); - canvas.drawBitmap(b.getBitmap(), null, b.getBounds(), null); + RectF bounds = new RectF(b.getBounds()); + /*if(b.dragged) { + float width = bounds.left - bounds.right; + float red = width/4; + bounds.left += red; + bounds.right -= red; + bounds.top += red; + bounds.bottom -= red; + }*/ + canvas.drawBitmap(b.getBitmap(), null, bounds, null); + canvas.drawText(b.contact.getmDisplayName(), b.getPosX(), b.getPosY()-50*density, name_paint); } } }