In-app contact editor finished (misses add field icon)

This commit is contained in:
Sylvain Berfini 2012-11-07 16:57:55 +01:00
parent 427c64971d
commit f21c6ecb74
11 changed files with 261 additions and 123 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:gravity="center_vertical"
android:padding="10dp">
<ImageView
android:contentDescription="@string/content_description_delete"
android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/sym_action_add"
android:paddingRight="5dp"
android:gravity="left" />
<EditText
android:id="@+id/numoraddr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:background="@drawable/chat_fast_address_background"
android:inputType="textEmailAddress"/>
</TableRow>

View file

@ -2,7 +2,7 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/delete"
android:text="@string/delete_contact"
android:background="@drawable/alert"
android:textColor="@android:color/white"
android:gravity="center"

View file

@ -42,45 +42,52 @@
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<org.linphone.ui.AvatarWithShadow
android:id="@+id/contactPicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
linphone:picture="@drawable/unknown_small" />
<EditText
android:id="@+id/contactName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:background="@drawable/chat_fast_address_background"
android:gravity="left"
android:paddingRight="5dp"
android:inputType="textPersonName|textCapWords"/>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout
android:id="@+id/controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*"
android:paddingTop="20dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<org.linphone.ui.AvatarWithShadow
android:id="@+id/contactPicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
linphone:picture="@drawable/unknown_small" />
<EditText
android:id="@+id/contactName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:background="@drawable/chat_fast_address_background"
android:gravity="left"
android:paddingRight="5dp"
android:inputType="textPersonName|textCapWords"/>
</LinearLayout>
<TableLayout
android:id="@+id/controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*"
android:paddingTop="20dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"/>
</LinearLayout>
</ScrollView>

View file

@ -341,4 +341,8 @@
<string name="setup_ec_calibration">Calibration de l\'annulateur d\'écho en cours</string>
<string name="delete_contact">Supprimer contact</string>
<string name="sip_address">Adresse SIP</string>
<string name="phone_number">Numéro de téléphone</string>
</resources>

View file

@ -6,6 +6,7 @@
<string name="picture_name_format">linphone-mms-%s.jpg</string>
<string name="app_name">Linphone</string>
<string name="addressbook_label">Linphone</string>
<string name="notification_title">Linphone</string>
<string name="wait_dialog_text">Starting up</string>
<string name="notification_registered">Registered to %s </string>

View file

@ -388,5 +388,7 @@
<string name="linphone_friend_new_request_desc">wants to share it\'s presence status with you and be aware of yours.</string>
<string name="setup_ec_calibration">Echo canceller calibration in progress</string>
<string name="delete_contact">Delete contact</string>
<string name="sip_address">SIP address</string>
<string name="phone_number">Phone number</string>
</resources>

View file

@ -88,6 +88,9 @@ public class ChatStorage {
}
public int saveMessage(String from, String to, Bitmap image) {
if (image == null)
return -1;
ContentValues values = new ContentValues();
if (from.equals("")) {
values.put("localContact", from);

View file

@ -8,6 +8,7 @@ import org.linphone.compatibility.Compatibility;
import org.linphone.ui.AvatarWithShadow;
import android.content.ContentProviderOperation;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.ContactsContract;
@ -29,10 +30,13 @@ public class EditContactFragment extends Fragment {
private TextView ok;
private EditText displayName;
private LayoutInflater inflater;
private View deleteContact;
private boolean isNewContact = true;
private int contactID;
private List<NewOrUpdatedNumberOrAddress> numbersAndAddresses;
private ArrayList<ContentProviderOperation> ops;
private int firstSipAddressIndex = -1;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.inflater = inflater;
@ -67,7 +71,17 @@ public class EditContactFragment extends Fragment {
for (NewOrUpdatedNumberOrAddress numberOrAddress : numbersAndAddresses) {
numberOrAddress.save();
}
try {
getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
if (isNewContact) {
LinphoneActivity.instance().prepareContactsInBackground();
}
} catch (Exception e) {
e.printStackTrace();
}
getFragmentManager().popBackStackImmediate();
}
});
@ -106,17 +120,23 @@ public class EditContactFragment extends Fragment {
initNumbersFields((TableLayout) view.findViewById(R.id.controls), contact);
ops = new ArrayList<ContentProviderOperation>();
return view;
}
private void initNumbersFields(TableLayout controls, final Contact contact) {
private void initNumbersFields(final TableLayout controls, final Contact contact) {
controls.removeAllViews();
numbersAndAddresses = new ArrayList<NewOrUpdatedNumberOrAddress>();
if (contact != null) {
for (String numberOrAddress : contact.getNumerosOrAddresses()) {
boolean isSip = numberOrAddress.startsWith("sip:");
final boolean isSip = numberOrAddress.startsWith("sip:");
if (isSip) {
if (firstSipAddressIndex == -1) {
firstSipAddressIndex = controls.getChildCount();
}
numberOrAddress = numberOrAddress.replace("sip:", "");
}
@ -149,31 +169,98 @@ public class EditContactFragment extends Fragment {
nounoa.delete();
numbersAndAddresses.remove(nounoa);
view.setVisibility(View.GONE);
if (isSip) // Add back the add SIP row
addEmptyRowToAllowNewNumberOrAddress(controls, true);
}
});
controls.addView(view);
}
if (!isNewContact) {
View deleteContact = inflater.inflate(R.layout.contact_delete_button, null);
deleteContact.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
deleteExistingContact();
LinphoneActivity.instance().removeContactFromLists(contact);
LinphoneActivity.instance().displayContacts(false);
}
});
controls.addView(deleteContact);
}
if (!isNewContact) {
deleteContact = inflater.inflate(R.layout.contact_delete_button, null);
deleteContact.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
deleteExistingContact();
LinphoneActivity.instance().removeContactFromLists(contact);
LinphoneActivity.instance().displayContacts(false);
}
});
controls.addView(deleteContact, controls.getChildCount());
}
// Add one for phone numbers, one for SIP address
addEmptyRowToAllowNewNumberOrAddress(controls, false);
if (firstSipAddressIndex == -1) { // Only add new SIP address field if there is no SIP address yet
firstSipAddressIndex = controls.getChildCount() - 2; // Update the value to alwas display phone numbers before SIP accounts
addEmptyRowToAllowNewNumberOrAddress(controls, true);
}
}
private void addEmptyRowToAllowNewNumberOrAddress(final TableLayout controls, final boolean isSip) {
final View view = inflater.inflate(R.layout.contact_add_row, null);
final NewOrUpdatedNumberOrAddress nounoa = new NewOrUpdatedNumberOrAddress(isSip);
final EditText noa = (EditText) view.findViewById(R.id.numoraddr);
noa.setHint(isSip ? getString(R.string.sip_address) : getString(R.string.phone_number));
noa.requestFocus();
noa.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
nounoa.setNewNumberOrAddress(noa.getText().toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
final ImageView add = (ImageView) view.findViewById(R.id.add);
add.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Add a line, and change add button for a delete button
numbersAndAddresses.add(nounoa);
add.setImageResource(R.drawable.list_delete);
add.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
numbersAndAddresses.remove(nounoa);
view.setVisibility(View.GONE);
if (isSip) // Add back the add SIP row
addEmptyRowToAllowNewNumberOrAddress(controls, true);
}
});
if (!isSip) { // Only 1 SIP address / contact
firstSipAddressIndex++;
addEmptyRowToAllowNewNumberOrAddress(controls, false);
}
}
});
if (isSip) {
controls.addView(view, controls.getChildCount());
// Move to the bottom the remove contact button
controls.removeView(deleteContact);
controls.addView(deleteContact, controls.getChildCount());
} else {
if (firstSipAddressIndex != -1) {
controls.addView(view, firstSipAddressIndex);
} else {
controls.addView(view);
}
}
}
private void createNewContact() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
contactID = ops.size();
contactID = 0;
ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, null)
@ -187,17 +274,9 @@ public class EditContactFragment extends Fragment {
.build()
);
}
try {
getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateExistingContact() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (displayName.getText().length() > 0) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'" ;
String[] args = new String[] { String.valueOf(contactID) };
@ -209,17 +288,9 @@ public class EditContactFragment extends Fragment {
.build()
);
}
try {
getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
}
private void deleteExistingContact() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (displayName.getText().length() > 0) {
String select = ContactsContract.Data.CONTACT_ID + "=?";
String[] args = new String[] { String.valueOf(contactID) };
@ -237,11 +308,28 @@ public class EditContactFragment extends Fragment {
}
}
private String findRawContactID(String contactID) {
Cursor c = getActivity().getContentResolver().query(RawContacts.CONTENT_URI,
new String[]{RawContacts._ID},
RawContacts.CONTACT_ID + "=?",
new String[]{contactID}, null);
if (c != null && c.moveToFirst()) {
return c.getString(c.getColumnIndex(RawContacts._ID));
}
return null;
}
class NewOrUpdatedNumberOrAddress {
private String oldNumberOrAddress;
private String newNumberOrAddress;
private boolean isSipAddress;
public NewOrUpdatedNumberOrAddress() {
oldNumberOrAddress = null;
newNumberOrAddress = null;
isSipAddress = false;
}
public NewOrUpdatedNumberOrAddress(boolean isSip) {
oldNumberOrAddress = null;
newNumberOrAddress = null;
@ -262,26 +350,16 @@ public class EditContactFragment extends Fragment {
if (newNumberOrAddress == null || newNumberOrAddress.equals(oldNumberOrAddress))
return;
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (oldNumberOrAddress == null) {
// New number to add
addNewNumber(ops);
addNewNumber();
} else {
// Old number to update
updateNumber(ops);
updateNumber();
}
try {
getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
}
public void delete() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
if (isSipAddress) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "
@ -303,39 +381,55 @@ public class EditContactFragment extends Fragment {
.build()
);
}
try {
getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
} catch (Exception e) {
e.printStackTrace();
}
}
private void addNewNumber(ArrayList<ContentProviderOperation> ops) {
if (isSipAddress) {
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, contactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.SipAddress.TYPE, ContactsContract.CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.SipAddress.LABEL, "Linphone")
.build()
);
private void addNewNumber() {
if (isNewContact) {
if (isSipAddress) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.SipAddress.TYPE, ContactsContract.CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.SipAddress.LABEL, getString(R.string.addressbook_label))
.build()
);
} else {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Phone.LABEL, getString(R.string.addressbook_label))
.build()
);
}
} else {
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, contactID)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Phone.LABEL, "Linphone")
.build()
);
String rawContactId = findRawContactID(String.valueOf(contactID));
if (isSipAddress) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.SipAddress.TYPE, ContactsContract.CommonDataKinds.SipAddress.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.SipAddress.LABEL, getString(R.string.addressbook_label))
.build()
);
} else {
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(ContactsContract.Data.RAW_CONTACT_ID, rawContactId)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, newNumberOrAddress)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM)
.withValue(ContactsContract.CommonDataKinds.Phone.LABEL, getString(R.string.addressbook_label))
.build()
);
}
}
}
private void updateNumber(ArrayList<ContentProviderOperation> ops) {
private void updateNumber() {
if (isSipAddress) {
String select = ContactsContract.Data.CONTACT_ID + "=? AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' AND "

View file

@ -1087,7 +1087,7 @@ public class LinphoneActivity extends FragmentActivity implements
}
}
private synchronized void prepareContactsInBackground() {
public synchronized void prepareContactsInBackground() {
if (contactCursor != null) {
contactCursor.close();
}

View file

@ -118,12 +118,23 @@ public class ApiFivePlus {
return intent;
}
@SuppressWarnings("resource")
public static List<String> extractContactNumbersAndAddresses(String id, ContentResolver cr) {
List<String> list = new ArrayList<String>();
Uri uri = Data.CONTENT_URI;
String[] projection = {ContactsContract.CommonDataKinds.Im.DATA};
// Phone Numbers
Cursor c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null);
if (c != null) {
while (c.moveToNext()) {
String number = c.getString(c.getColumnIndex(Phone.NUMBER));
list.add(number);
}
c.close();
}
// SIP addresses
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
String selection = new StringBuilder()
@ -135,7 +146,7 @@ public class ApiFivePlus {
.append("'")
.toString();
projection = new String[] {ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS};
Cursor c = cr.query(uri, projection, selection, new String[]{id}, null);
c = cr.query(uri, projection, selection, new String[]{id}, null);
if (c != null) {
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS);
while (c.moveToNext()) {
@ -152,7 +163,7 @@ public class ApiFivePlus {
.append(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL)
.append(") = 'sip'")
.toString();
Cursor c = cr.query(uri, projection, selection, new String[]{id}, null);
c = cr.query(uri, projection, selection, new String[]{id}, null);
if (c != null) {
int nbId = c.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA);
while (c.moveToNext()) {
@ -161,16 +172,6 @@ public class ApiFivePlus {
c.close();
}
}
// Phone Numbers
Cursor c = cr.query(Phone.CONTENT_URI, new String[] { Phone.NUMBER }, Phone.CONTACT_ID + " = " + id, null, null);
if (c != null) {
while (c.moveToNext()) {
String number = c.getString(c.getColumnIndex(Phone.NUMBER));
list.add(number);
}
c.close();
}
return list;
}