mirror of
https://github.com/nextcloud/android.git
synced 2024-11-30 06:51:18 +03:00
Merge pull request #1043 from nextcloud/fixingContactBackup
Fixing contact backup
This commit is contained in:
commit
037236f095
4 changed files with 278 additions and 107 deletions
|
@ -21,14 +21,22 @@
|
|||
|
||||
package com.owncloud.android.services;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.evernote.android.job.Job;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import ezvcard.Ezvcard;
|
||||
import ezvcard.VCard;
|
||||
|
@ -62,9 +70,34 @@ public class ContactsImportJob extends Job {
|
|||
try {
|
||||
ContactOperations operations = new ContactOperations(getContext(), accountName, accountType);
|
||||
vCards.addAll(Ezvcard.parse(file).all());
|
||||
Collections.sort(vCards, new ContactListFragment.VCardComparator());
|
||||
Cursor cursor = getContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null,
|
||||
null, null, null);
|
||||
|
||||
for (int i = 0; i < intArray.length; i++ ){
|
||||
operations.insertContact(vCards.get(intArray[i]));
|
||||
TreeMap<VCard, Long> ownContactList = new TreeMap<>(new ContactListFragment.VCardComparator());
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
VCard vCard = getContactFromCursor(cursor);
|
||||
if (vCard != null) {
|
||||
ownContactList.put(vCard, cursor.getLong(cursor.getColumnIndex("NAME_RAW_CONTACT_ID")));
|
||||
}
|
||||
cursor.moveToNext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < intArray.length; i++) {
|
||||
VCard vCard = vCards.get(intArray[i]);
|
||||
if (ContactListFragment.getDisplayName(vCard).length() != 0) {
|
||||
if (!ownContactList.containsKey(vCard)) {
|
||||
operations.insertContact(vCard);
|
||||
} else {
|
||||
operations.updateContact(vCard, ownContactList.get(vCard));
|
||||
}
|
||||
} else {
|
||||
operations.insertContact(vCard); //Insert All the contacts without name
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG, e.getMessage());
|
||||
|
@ -72,4 +105,24 @@ public class ContactsImportJob extends Job {
|
|||
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
|
||||
private VCard getContactFromCursor(Cursor cursor) {
|
||||
String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
|
||||
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
|
||||
VCard vCard = null;
|
||||
try {
|
||||
InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
|
||||
ArrayList<VCard> vCardList = new ArrayList<>();
|
||||
vCardList.addAll(Ezvcard.parse(inputStream).all());
|
||||
if (vCardList.size() > 0) {
|
||||
vCard = vCardList.get(0);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log_OC.d(TAG, e.getMessage());
|
||||
}
|
||||
return vCard;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.content.IntentFilter;
|
|||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.ContactsContract;
|
||||
|
@ -53,6 +54,9 @@ import android.widget.CheckedTextView;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.evernote.android.job.JobRequest;
|
||||
import com.evernote.android.job.util.support.PersistableBundleCompat;
|
||||
|
@ -86,7 +90,8 @@ import butterknife.BindView;
|
|||
import butterknife.ButterKnife;
|
||||
import ezvcard.Ezvcard;
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.property.StructuredName;
|
||||
|
||||
import static com.owncloud.android.ui.fragment.contactsbackup.ContactListFragment.getDisplayName;
|
||||
|
||||
/**
|
||||
* This fragment shows all contacts from a file and allows to import them.
|
||||
|
@ -108,8 +113,26 @@ public class ContactListFragment extends FileFragment {
|
|||
@BindView(R.id.contactlist_restore_selected)
|
||||
public Button restoreContacts;
|
||||
|
||||
@BindView(R.id.empty_list_view_text)
|
||||
public TextView emptyContentMessage;
|
||||
|
||||
@BindView(R.id.empty_list_view_headline)
|
||||
public TextView emptyContentHeadline;
|
||||
|
||||
@BindView(R.id.empty_list_icon)
|
||||
public ImageView emptyContentIcon;
|
||||
|
||||
@BindView(R.id.empty_list_progress)
|
||||
public ProgressBar emptyContentProgressBar;
|
||||
|
||||
@BindView(R.id.empty_list_container)
|
||||
public RelativeLayout emptyListContainer;
|
||||
|
||||
|
||||
private ContactListAdapter contactListAdapter;
|
||||
private Account account;
|
||||
private ArrayList<VCard> vCards = new ArrayList<>();
|
||||
private OCFile ocFile;
|
||||
|
||||
public static ContactListFragment newInstance(OCFile file, Account account) {
|
||||
ContactListFragment frag = new ContactListFragment();
|
||||
|
@ -147,47 +170,41 @@ public class ContactListFragment extends FileFragment {
|
|||
}
|
||||
contactsPreferenceActivity.setDrawerIndicatorEnabled(false);
|
||||
|
||||
ArrayList<VCard> vCards = new ArrayList<>();
|
||||
recyclerView = (RecyclerView) view.findViewById(R.id.contactlist_recyclerview);
|
||||
|
||||
try {
|
||||
OCFile ocFile = getArguments().getParcelable(FILE_NAME);
|
||||
setFile(ocFile);
|
||||
account = getArguments().getParcelable(ACCOUNT);
|
||||
|
||||
if (!ocFile.isDown()) {
|
||||
Intent i = new Intent(getContext(), FileDownloader.class);
|
||||
i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
|
||||
i.putExtra(FileDownloader.EXTRA_FILE, ocFile);
|
||||
getContext().startService(i);
|
||||
|
||||
// Listen for download messages
|
||||
IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.getDownloadAddedMessage());
|
||||
downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
|
||||
DownloadFinishReceiver mDownloadFinishReceiver = new DownloadFinishReceiver();
|
||||
getContext().registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
|
||||
} else {
|
||||
File file = new File(ocFile.getStoragePath());
|
||||
|
||||
Collections.sort(vCards, new Comparator<VCard>() {
|
||||
@Override
|
||||
public int compare(VCard o1, VCard o2) {
|
||||
if (o1.getFormattedName() != null && o2.getFormattedName() != null) {
|
||||
return o1.getFormattedName().getValue().compareTo(o2.getFormattedName().getValue());
|
||||
} else {
|
||||
if (o1.getFormattedName() == null) {
|
||||
return 1; // Send the contact to the end of the list
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
vCards.addAll(Ezvcard.parse(file).all());
|
||||
if (savedInstanceState == null) {
|
||||
contactListAdapter = new ContactListAdapter(getContext(), vCards);
|
||||
} else {
|
||||
Set<Integer> checkedItems = new HashSet<>();
|
||||
int[] itemsArray = savedInstanceState.getIntArray(CHECKED_ITEMS_ARRAY_KEY);
|
||||
for (int i = 0; i < itemsArray.length; i++) {
|
||||
checkedItems.add(itemsArray[i]);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error processing contacts file!", e);
|
||||
if (checkedItems.size() > 0) {
|
||||
onMessageEvent(new VCardToggleEvent(true));
|
||||
}
|
||||
contactListAdapter = new ContactListAdapter(getContext(), vCards, checkedItems);
|
||||
}
|
||||
recyclerView.setAdapter(contactListAdapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
ocFile = getArguments().getParcelable(FILE_NAME);
|
||||
setFile(ocFile);
|
||||
account = getArguments().getParcelable(ACCOUNT);
|
||||
|
||||
if (!ocFile.isDown()) {
|
||||
Intent i = new Intent(getContext(), FileDownloader.class);
|
||||
i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
|
||||
i.putExtra(FileDownloader.EXTRA_FILE, ocFile);
|
||||
getContext().startService(i);
|
||||
|
||||
// Listen for download messages
|
||||
IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.getDownloadAddedMessage());
|
||||
downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
|
||||
DownloadFinishReceiver mDownloadFinishReceiver = new DownloadFinishReceiver();
|
||||
getContext().registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
|
||||
} else {
|
||||
loadContactsTask.execute();
|
||||
}
|
||||
|
||||
restoreContacts.setOnClickListener(new View.OnClickListener() {
|
||||
|
@ -200,26 +217,6 @@ public class ContactListFragment extends FileFragment {
|
|||
}
|
||||
});
|
||||
|
||||
recyclerView = (RecyclerView) view.findViewById(R.id.contactlist_recyclerview);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
contactListAdapter = new ContactListAdapter(getContext(), vCards);
|
||||
} else {
|
||||
Set<Integer> checkedItems = new HashSet<>();
|
||||
int[] itemsArray = savedInstanceState.getIntArray(CHECKED_ITEMS_ARRAY_KEY);
|
||||
if (itemsArray != null) {
|
||||
for (int item : itemsArray) {
|
||||
checkedItems.add(item);
|
||||
}
|
||||
}
|
||||
if (checkedItems.size() > 0) {
|
||||
onMessageEvent(new VCardToggleEvent(true));
|
||||
}
|
||||
contactListAdapter = new ContactListAdapter(getContext(), vCards, checkedItems);
|
||||
}
|
||||
recyclerView.setAdapter(contactListAdapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
@ -260,6 +257,9 @@ public class ContactListFragment extends FileFragment {
|
|||
@Override
|
||||
public void onStop() {
|
||||
EventBus.getDefault().unregister(this);
|
||||
if (loadContactsTask != null) {
|
||||
loadContactsTask.cancel(true);
|
||||
}
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
|
@ -286,6 +286,14 @@ public class ContactListFragment extends FileFragment {
|
|||
return retval;
|
||||
}
|
||||
|
||||
private void setLoadingMessage() {
|
||||
emptyContentHeadline.setText(R.string.file_list_loading);
|
||||
emptyContentMessage.setText("");
|
||||
|
||||
emptyContentIcon.setVisibility(View.GONE);
|
||||
emptyContentProgressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setSelectAllMenuItem(MenuItem selectAll, boolean checked) {
|
||||
selectAll.setChecked(checked);
|
||||
if (checked) {
|
||||
|
@ -476,22 +484,72 @@ public class ContactListFragment extends FileFragment {
|
|||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equalsIgnoreCase(FileDownloader.getDownloadFinishMessage())){
|
||||
if (intent.getAction().equalsIgnoreCase(FileDownloader.getDownloadFinishMessage())) {
|
||||
String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
|
||||
|
||||
FileDataStorageManager storageManager = new FileDataStorageManager(account,
|
||||
getContext().getContentResolver());
|
||||
OCFile ocFile = storageManager.getFileByPath(downloadedRemotePath);
|
||||
File file = new File(ocFile.getStoragePath());
|
||||
|
||||
try {
|
||||
contactListAdapter.replaceVCards(Ezvcard.parse(file).all());
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "IO Exception: " + file.getAbsolutePath());
|
||||
}
|
||||
ocFile = storageManager.getFileByPath(downloadedRemotePath);
|
||||
loadContactsTask.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class VCardComparator implements Comparator<VCard> {
|
||||
@Override
|
||||
public int compare(VCard o1, VCard o2) {
|
||||
String contac1 = getDisplayName(o1);
|
||||
String contac2 = getDisplayName(o2);
|
||||
|
||||
return contac1.compareToIgnoreCase(contac2);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private AsyncTask loadContactsTask = new AsyncTask() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
setLoadingMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground(Object[] params) {
|
||||
if (!isCancelled()) {
|
||||
File file = new File(ocFile.getStoragePath());
|
||||
try {
|
||||
vCards.addAll(Ezvcard.parse(file).all());
|
||||
Collections.sort(vCards, new VCardComparator());
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "IO Exception: " + file.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object o) {
|
||||
if (!isCancelled()) {
|
||||
emptyListContainer.setVisibility(View.GONE);
|
||||
contactListAdapter.replaceVCards(vCards);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static String getDisplayName(VCard vCard) {
|
||||
if (vCard.getFormattedName() != null) {
|
||||
return vCard.getFormattedName().getValue();
|
||||
} else if (vCard.getTelephoneNumbers() != null && vCard.getTelephoneNumbers().size() > 0) {
|
||||
return vCard.getTelephoneNumbers().get(0).getText();
|
||||
} else if (vCard.getEmails() != null && vCard.getEmails().size() > 0) {
|
||||
return vCard.getEmails().get(0).getValue();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.ContactItemViewHolder> {
|
||||
|
@ -561,15 +619,8 @@ class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.Contac
|
|||
} else {
|
||||
holder.getName().setChecked(false);
|
||||
}
|
||||
// name
|
||||
StructuredName name = vcard.getStructuredName();
|
||||
if (name != null) {
|
||||
String first = (name.getGiven() == null) ? "" : name.getGiven() + " ";
|
||||
String last = (name.getFamily() == null) ? "" : name.getFamily();
|
||||
holder.getName().setText(String.format("%s%s", first, last));
|
||||
} else {
|
||||
holder.getName().setText("");
|
||||
}
|
||||
|
||||
holder.getName().setText(getDisplayName(vcard));
|
||||
|
||||
// photo
|
||||
if (vcard.getPhotos().size() > 0) {
|
||||
|
@ -641,4 +692,5 @@ class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.Contac
|
|||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -141,6 +141,59 @@ public class ContactOperations {
|
|||
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
|
||||
}
|
||||
|
||||
public void updateContact(VCard vcard, Long key) throws RemoteException, OperationApplicationException {
|
||||
|
||||
List<NonEmptyContentValues> contentValues = new ArrayList<NonEmptyContentValues>();
|
||||
convertName(contentValues, vcard);
|
||||
convertNickname(contentValues, vcard);
|
||||
convertPhones(contentValues, vcard);
|
||||
convertEmails(contentValues, vcard);
|
||||
convertAddresses(contentValues, vcard);
|
||||
convertIms(contentValues, vcard);
|
||||
|
||||
// handle Android Custom fields..This is only valid for Android generated Vcards. As the Android would
|
||||
// generate NickName, ContactEvents other than Birthday and RelationShip with this "X-ANDROID-CUSTOM" name
|
||||
convertCustomFields(contentValues, vcard);
|
||||
|
||||
// handle Iphone kinda of group properties. which are grouped together.
|
||||
convertGroupedProperties(contentValues, vcard);
|
||||
|
||||
convertBirthdays(contentValues, vcard);
|
||||
|
||||
convertWebsites(contentValues, vcard);
|
||||
convertNotes(contentValues, vcard);
|
||||
convertPhotos(contentValues, vcard);
|
||||
convertOrganization(contentValues, vcard);
|
||||
|
||||
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(contentValues.size());
|
||||
ContentValues cv = account.getContentValues();
|
||||
//ContactsContract.RawContact.CONTENT_URI needed to add account, backReference is also not needed
|
||||
long contactID = key;
|
||||
ContentProviderOperation operation;
|
||||
|
||||
for (NonEmptyContentValues values : contentValues) {
|
||||
cv = values.getContentValues();
|
||||
if (cv.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String mimeType = cv.getAsString("mimetype");
|
||||
cv.remove("mimetype");
|
||||
//@formatter:off
|
||||
operation =
|
||||
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
|
||||
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ? ", new String[]{"" + contactID, "" + mimeType})
|
||||
.withValues(cv)
|
||||
.build();
|
||||
//@formatter:on
|
||||
operations.add(operation);
|
||||
}
|
||||
|
||||
// Executing all the insert operations as a single database transaction
|
||||
context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
|
||||
|
||||
}
|
||||
|
||||
private void convertName(List<NonEmptyContentValues> contentValues, VCard vcard) {
|
||||
NonEmptyContentValues values = new NonEmptyContentValues(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2017 Tobias Kaminsky
|
||||
|
@ -18,39 +17,53 @@
|
|||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/contactlist_recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:choiceMode="multipleChoice"/>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contactlist_restore_selected_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/contactlist_recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:src="@drawable/uploader_list_separator"/>
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:choiceMode="multipleChoice" />
|
||||
|
||||
<android.support.v7.widget.AppCompatButton
|
||||
android:id="@+id/contactlist_restore_selected"
|
||||
style="@style/Button.Borderless"
|
||||
<LinearLayout
|
||||
android:id="@+id/contactlist_restore_selected_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/contaclist_restore_selected"/>
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:src="@drawable/uploader_list_separator" />
|
||||
|
||||
<android.support.v7.widget.AppCompatButton
|
||||
android:id="@+id/contactlist_restore_selected"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/contaclist_restore_selected" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<RelativeLayout
|
||||
android:id="@+id/empty_list_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<include layout="@layout/empty_list" />
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
Loading…
Reference in a new issue