Fixed edit contact groups list after labels changes.
MAILAND-1525
This commit is contained in:
parent
7c70fdcf77
commit
b1af58877a
|
@ -348,7 +348,7 @@ internal class ContactDaoTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun saveContactEmail() {
|
||||
fun saveContactEmail() = runBlocking {
|
||||
val inserted = ContactEmail(
|
||||
"z",
|
||||
"z@z.com",
|
||||
|
|
|
@ -177,7 +177,8 @@ class ProtonMailApiManager @Inject constructor(var api: ProtonMailApi) :
|
|||
|
||||
override suspend fun deleteContact(contactIds: IDList): DeleteResponse = api.deleteContact(contactIds)
|
||||
|
||||
override fun labelContacts(labelContactsBody: LabelContactsBody): Completable = api.labelContacts(labelContactsBody)
|
||||
override suspend fun labelContacts(labelContactsBody: LabelContactsBody) =
|
||||
api.labelContacts(labelContactsBody)
|
||||
|
||||
override fun unlabelContactEmailsCompletable(labelContactsBody: LabelContactsBody): Completable =
|
||||
api.unlabelContactEmailsCompletable(labelContactsBody)
|
||||
|
|
|
@ -37,8 +37,7 @@ import io.reactivex.Single
|
|||
import java.io.IOException
|
||||
|
||||
class ContactApi(private val service: ContactService) : BaseApi(), ContactApiSpec {
|
||||
@Throws(IOException::class)
|
||||
override fun labelContacts(labelContactsBody: LabelContactsBody): Completable =
|
||||
override suspend fun labelContacts(labelContactsBody: LabelContactsBody) =
|
||||
service.labelContacts(labelContactsBody)
|
||||
|
||||
@Throws(IOException::class)
|
||||
|
|
|
@ -62,8 +62,7 @@ interface ContactApiSpec {
|
|||
|
||||
suspend fun deleteContact(contactIds: IDList): DeleteResponse
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun labelContacts(labelContactsBody: LabelContactsBody): Completable
|
||||
suspend fun labelContacts(labelContactsBody: LabelContactsBody)
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun unlabelContactEmailsCompletable(labelContactsBody: LabelContactsBody): Completable
|
||||
|
|
|
@ -93,7 +93,7 @@ interface ContactService {
|
|||
|
||||
@PUT("contacts/emails/label")
|
||||
@Headers(CONTENT_TYPE, ACCEPT_HEADER_V1)
|
||||
fun labelContacts(@Body labelContactsBody: LabelContactsBody): Completable
|
||||
suspend fun labelContacts(@Body labelContactsBody: LabelContactsBody)
|
||||
|
||||
@PUT("contacts/emails/unlabel")
|
||||
@Headers(CONTENT_TYPE, ACCEPT_HEADER_V1)
|
||||
|
|
|
@ -31,9 +31,7 @@ import ch.protonmail.android.labels.data.model.LabelResponse
|
|||
import ch.protonmail.android.worker.CreateContactGroupWorker
|
||||
import ch.protonmail.android.worker.RemoveMembersFromContactGroupWorker
|
||||
import com.birbit.android.jobqueue.JobManager
|
||||
import io.reactivex.Completable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.network.domain.ApiResult
|
||||
import timber.log.Timber
|
||||
|
@ -73,29 +71,30 @@ class ContactGroupEditCreateRepository @Inject constructor(
|
|||
fun getContactGroupEmails(id: String): Flow<List<ContactEmail>> =
|
||||
contactRepository.observeAllContactEmailsByContactGroupId(id)
|
||||
|
||||
fun removeMembersFromContactGroup(
|
||||
suspend fun removeMembersFromContactGroup(
|
||||
contactGroupId: String,
|
||||
contactGroupName: String,
|
||||
membersList: List<String>
|
||||
): Completable {
|
||||
) {
|
||||
if (membersList.isEmpty()) {
|
||||
return Completable.complete()
|
||||
Timber.v("No group members to remove")
|
||||
return
|
||||
}
|
||||
val labelContactsBody = LabelContactsBody(contactGroupId, membersList)
|
||||
return apiManager.unlabelContactEmailsCompletable(labelContactsBody)
|
||||
.doOnComplete {
|
||||
runBlocking {
|
||||
val contactEmails =
|
||||
contactRepository.findAllContactEmailsByContactGroupId(contactGroupId)
|
||||
contactEmails.forEach { contactEmail ->
|
||||
val updatedList = contactEmail.labelIds?.toMutableList()
|
||||
if (updatedList != null) {
|
||||
updatedList.remove(contactGroupName)
|
||||
contactRepository.saveContactEmail(contactEmail.copy(labelIds = updatedList))
|
||||
}
|
||||
runCatching {
|
||||
val labelContactsBody = LabelContactsBody(contactGroupId, membersList)
|
||||
apiManager.unlabelContactEmails(labelContactsBody)
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
val contactEmails = contactRepository.findAllContactEmailsByContactGroupId(contactGroupId)
|
||||
contactEmails.forEach { contactEmail ->
|
||||
val updatedList = contactEmail.labelIds?.toMutableList()
|
||||
if (updatedList != null) {
|
||||
updatedList.remove(contactGroupName)
|
||||
contactRepository.saveContactEmail(contactEmail.copy(labelIds = updatedList))
|
||||
}
|
||||
}
|
||||
}.doOnError { throwable ->
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable is IOException) {
|
||||
RemoveMembersFromContactGroupWorker.Enqueuer(workManager).enqueue(
|
||||
contactGroupId,
|
||||
|
@ -104,38 +103,41 @@ class ContactGroupEditCreateRepository @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun setMembersForContactGroup(
|
||||
suspend fun setMembersForContactGroup(
|
||||
contactGroupId: String,
|
||||
contactGroupName: String,
|
||||
membersList: List<String>
|
||||
): Completable {
|
||||
) {
|
||||
if (membersList.isEmpty()) {
|
||||
return Completable.complete()
|
||||
Timber.v("No group members to update")
|
||||
return
|
||||
}
|
||||
val labelContactsBody = LabelContactsBody(contactGroupId, membersList)
|
||||
return apiManager.labelContacts(labelContactsBody)
|
||||
.doOnComplete {
|
||||
runBlocking {
|
||||
val contactEmails =
|
||||
contactRepository.findAllContactEmailsByContactGroupId(contactGroupId)
|
||||
contactEmails.forEach { contactEmail ->
|
||||
val updatedList = contactEmail.labelIds?.toMutableList()
|
||||
if (updatedList != null) {
|
||||
updatedList.add(contactGroupName)
|
||||
contactRepository.saveContactEmail(contactEmail.copy(labelIds = updatedList))
|
||||
}
|
||||
Timber.v("Set contact Members contactGroupId: $contactGroupId, contactGroupName: $contactGroupName")
|
||||
runCatching {
|
||||
val labelContactsBody = LabelContactsBody(contactGroupId, membersList)
|
||||
apiManager.labelContacts(labelContactsBody)
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
val contactEmails = contactRepository.findAllContactEmailsByContactGroupId(contactGroupId)
|
||||
contactEmails.forEach { contactEmail ->
|
||||
val updatedList = contactEmail.labelIds?.toMutableList()
|
||||
if (updatedList != null) {
|
||||
updatedList.add(contactGroupName)
|
||||
contactRepository.saveContactEmail(contactEmail.copy(labelIds = updatedList))
|
||||
}
|
||||
}
|
||||
}
|
||||
.doOnError { throwable ->
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable is IOException) {
|
||||
jobManager.addJobInBackground(
|
||||
SetMembersForContactGroupJob(contactGroupId, contactGroupName, membersList, labelRepository)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun createContactGroup(contactLabel: LabelEntity, userId: UserId): ApiResult<LabelResponse> {
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
package ch.protonmail.android.contacts.groups.jobs
|
||||
|
||||
import ch.protonmail.android.api.models.contacts.send.LabelContactsBody
|
||||
import ch.protonmail.android.api.rx.ThreadSchedulers
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.jobs.Priority
|
||||
import ch.protonmail.android.jobs.ProtonMailBaseJob
|
||||
|
@ -42,10 +41,9 @@ class SetMembersForContactGroupJob(
|
|||
id = contactLabel?.id?.id ?: ""
|
||||
}
|
||||
}
|
||||
val labelContactsBody = LabelContactsBody(id, membersList)
|
||||
getApi().labelContacts(labelContactsBody)
|
||||
.subscribeOn(ThreadSchedulers.io())
|
||||
.observeOn(ThreadSchedulers.io())
|
||||
.blockingAwait()
|
||||
runBlocking {
|
||||
val labelContactsBody = LabelContactsBody(id, membersList)
|
||||
getApi().labelContacts(labelContactsBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ class ContactsRepository @Inject constructor(
|
|||
fun observeFilterContactEmailsByContactGroup(groupLabelId: String, filter: String): Flow<List<ContactEmail>> =
|
||||
contactDao.observeFilterContactEmailsByContactGroup(groupLabelId.take(IMPORTANT_LABEL_CHARACTERS_COUNT), filter)
|
||||
|
||||
fun saveContactEmail(contactEmail: ContactEmail) =
|
||||
suspend fun saveContactEmail(contactEmail: ContactEmail) =
|
||||
contactDao.saveContactEmail(contactEmail)
|
||||
|
||||
private suspend fun getUniqueContactGroupsIds(): Set<String> {
|
||||
|
|
|
@ -153,7 +153,7 @@ interface ContactDao {
|
|||
fun deleteAllContactsEmails(contactEmail: Collection<ContactEmail>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveContactEmail(contactEmail: ContactEmail): Long
|
||||
suspend fun saveContactEmail(contactEmail: ContactEmail): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun saveAllContactsEmails(emailData: Collection<ContactEmail>): List<Long>
|
||||
|
@ -183,7 +183,7 @@ interface ContactDao {
|
|||
"""
|
||||
SELECT * FROM $TABLE_CONTACT_EMAILS
|
||||
WHERE $COLUMN_CONTACT_EMAILS_LABEL_IDS LIKE '%' || :contactGroupId || '%'
|
||||
AND $COLUMN_CONTACT_EMAILS_EMAIL LIKE :filter
|
||||
AND $COLUMN_CONTACT_EMAILS_EMAIL LIKE '%' || :filter || '%'
|
||||
"""
|
||||
)
|
||||
fun observeFilterContactEmailsByContactGroup(contactGroupId: String, filter: String): Flow<List<ContactEmail>>
|
||||
|
|
|
@ -343,15 +343,9 @@ class ConvertLocalContactsJob(
|
|||
contactDao.saveAllContactsEmailsBlocking(contact.emails!!)
|
||||
contactGroupIds.forEach { contactGroupId ->
|
||||
val emailsList = contact.emails!!.map { it.contactEmailId }
|
||||
getApi().labelContacts(LabelContactsBody(contactGroupId, emailsList))
|
||||
.doOnComplete {
|
||||
//val joins = contactDao.fetchJoinsBlocking(contactGroupId) as ArrayList
|
||||
for (contactEmail in emailsList) {
|
||||
// joins.add(ContactEmailContactLabelJoin(contactEmail, contactGroupId))
|
||||
}
|
||||
//contactDao.saveContactEmailContactLabelBlocking(joins)
|
||||
}
|
||||
.blockingAwait()
|
||||
runBlocking {
|
||||
getApi().labelContacts(LabelContactsBody(contactGroupId, emailsList))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ContactEvent.SUCCESS
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
package ch.protonmail.android.jobs;
|
||||
|
||||
import static ch.protonmail.android.api.segments.BaseApiKt.RESPONSE_CODE_ERROR_EMAIL_DUPLICATE_FAILED;
|
||||
import static ch.protonmail.android.api.segments.BaseApiKt.RESPONSE_CODE_ERROR_EMAIL_EXIST;
|
||||
import static ch.protonmail.android.api.segments.BaseApiKt.RESPONSE_CODE_ERROR_INVALID_EMAIL;
|
||||
|
||||
import android.database.sqlite.SQLiteBlobTooBigException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.birbit.android.jobqueue.Params;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ch.protonmail.android.api.models.ContactEncryptedData;
|
||||
import ch.protonmail.android.api.models.CreateContactV2BodyItem;
|
||||
import ch.protonmail.android.api.models.contacts.send.LabelContactsBody;
|
||||
import ch.protonmail.android.api.rx.ThreadSchedulers;
|
||||
import ch.protonmail.android.contacts.details.presentation.model.ContactLabelUiModel;
|
||||
import ch.protonmail.android.contacts.groups.jobs.SetMembersForContactGroupJob;
|
||||
import ch.protonmail.android.core.Constants;
|
||||
import ch.protonmail.android.crypto.CipherText;
|
||||
import ch.protonmail.android.crypto.Crypto;
|
||||
import ch.protonmail.android.crypto.UserCrypto;
|
||||
import ch.protonmail.android.data.local.ContactDao;
|
||||
import ch.protonmail.android.data.local.ContactDatabase;
|
||||
import ch.protonmail.android.data.local.model.ContactData;
|
||||
import ch.protonmail.android.data.local.model.ContactEmail;
|
||||
import ch.protonmail.android.data.local.model.FullContactDetails;
|
||||
import ch.protonmail.android.data.local.model.FullContactDetailsResponse;
|
||||
import ch.protonmail.android.events.ContactEvent;
|
||||
import ch.protonmail.android.labels.data.LabelRepository;
|
||||
import ch.protonmail.android.utils.AppUtil;
|
||||
import ezvcard.Ezvcard;
|
||||
import ezvcard.VCard;
|
||||
import ezvcard.property.Email;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class UpdateContactJob extends ProtonMailEndlessJob {
|
||||
|
||||
private final String mContactId;
|
||||
private final String mContactName;
|
||||
private final List<ContactEmail> mContactEmails;
|
||||
private final String mEncryptedData;
|
||||
private final String mSignedData;
|
||||
private final Map<ContactEmail, List<ContactLabelUiModel>> mMapEmailGroupsIds;
|
||||
private final LabelRepository labelRepository;
|
||||
|
||||
private transient ContactDao mContactDao;
|
||||
|
||||
public UpdateContactJob(
|
||||
String contactId,
|
||||
@NonNull String contactName,
|
||||
@NonNull List<ContactEmail> contactEmails,
|
||||
String encryptedData,
|
||||
String signedData,
|
||||
Map<ContactEmail, List<ContactLabelUiModel>> mapEmailGroupsIds,
|
||||
LabelRepository labelRepository
|
||||
) {
|
||||
super(new Params(Priority.MEDIUM).requireNetwork().persist().groupBy(Constants.JOB_GROUP_CONTACT));
|
||||
mContactId = contactId;
|
||||
mContactName = contactName;
|
||||
mContactEmails = contactEmails;
|
||||
mEncryptedData = encryptedData;
|
||||
mSignedData = signedData;
|
||||
mMapEmailGroupsIds = mapEmailGroupsIds;
|
||||
this.labelRepository = labelRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdded() {
|
||||
UserCrypto crypto = Crypto.forUser(getUserManager(), getUserId());
|
||||
try {
|
||||
CipherText tct = crypto.encrypt(mEncryptedData, false);
|
||||
String encryptedDataSignature = crypto.sign(mEncryptedData);
|
||||
String signedDataSignature = crypto.sign(mSignedData);
|
||||
|
||||
updateContact(mContactName, mContactEmails, tct.getArmored(), encryptedDataSignature, signedDataSignature, false);
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.SAVED, true));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!getQueueNetworkUtil().isConnected()) {
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.NO_NETWORK, true));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Throwable {
|
||||
UserCrypto crypto = Crypto.forUser(getUserManager(), getUserId());
|
||||
requireContactDao();
|
||||
|
||||
CipherText tct = crypto.encrypt(mEncryptedData, false);
|
||||
String encryptedDataSignature = crypto.sign(mEncryptedData);
|
||||
String signedDataSignature = crypto.sign(mSignedData);
|
||||
|
||||
CreateContactV2BodyItem body = new CreateContactV2BodyItem(mSignedData, signedDataSignature,
|
||||
tct.getArmored(), encryptedDataSignature);
|
||||
FullContactDetailsResponse response = getApi().updateContact(mContactId, body);
|
||||
|
||||
if (response != null) {
|
||||
Timber.v("Update contacts response code:%s error:%s", response.getCode(), response.getError());
|
||||
if (response.getCode() == RESPONSE_CODE_ERROR_EMAIL_EXIST) {
|
||||
// TODO: 9/14/17 todoContacts throw error
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.ALREADY_EXIST, true));
|
||||
} else if (response.getCode() == RESPONSE_CODE_ERROR_INVALID_EMAIL) {
|
||||
// TODO: 9/14/17 todoContacts throw error
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.INVALID_EMAIL, true));
|
||||
} else if (response.getCode() == RESPONSE_CODE_ERROR_EMAIL_DUPLICATE_FAILED) {
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.DUPLICATE_EMAIL, true));
|
||||
} else {
|
||||
updateContact(mContactName, response.getContact().getEmails(), tct.getArmored(), encryptedDataSignature, signedDataSignature, true);
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.SUCCESS, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateContact(
|
||||
@NonNull String contactName,
|
||||
@NonNull List<ContactEmail> contactEmails,
|
||||
String encryptedData,
|
||||
String encryptedDataSignature,
|
||||
String signedDataSignature,
|
||||
boolean updateJoins
|
||||
) {
|
||||
|
||||
requireContactDao();
|
||||
final ContactData contactData = mContactDao.findContactDataById(mContactId);
|
||||
if (contactData != null) {
|
||||
contactData.setName(contactName);
|
||||
mContactDao.saveContactData(contactData);
|
||||
}
|
||||
|
||||
List<ContactEmail> emails = mContactDao.findContactEmailsByContactIdBlocking(mContactId);
|
||||
mContactDao.deleteAllContactsEmails(emails);
|
||||
|
||||
for (ContactEmail email : contactEmails) {
|
||||
final String emailToClear = email.getEmail();
|
||||
mContactDao.clearByEmailBlocking(emailToClear);
|
||||
}
|
||||
mContactDao.saveAllContactsEmailsBlocking(contactEmails);
|
||||
Map<ContactLabelUiModel, List<String>> mapContactGroupContactEmails = new HashMap<>();
|
||||
if (updateJoins) {
|
||||
for (ContactEmail email : contactEmails) {
|
||||
List<ContactLabelUiModel> labels = findContactLabelsByEmail(email);
|
||||
for (ContactLabelUiModel label : labels) {
|
||||
List<String> labelEmails = mapContactGroupContactEmails.get(label);
|
||||
if (labelEmails == null) {
|
||||
labelEmails = new ArrayList<>();
|
||||
}
|
||||
labelEmails.add(email.getContactEmailId());
|
||||
mapContactGroupContactEmails.put(label, labelEmails);
|
||||
}
|
||||
}
|
||||
}
|
||||
FullContactDetails contact = null;
|
||||
try {
|
||||
contact = mContactDao.findFullContactDetailsByIdBlocking(mContactId);
|
||||
} catch (SQLiteBlobTooBigException tooBigException) {
|
||||
Timber.i(tooBigException,"Data too big to be fetched");
|
||||
}
|
||||
if (contact != null) {
|
||||
ContactEncryptedData contactEncryptedData = new ContactEncryptedData(encryptedData, encryptedDataSignature, Constants.VCardType.SIGNED_ENCRYPTED);
|
||||
ContactEncryptedData contactSignedData = new ContactEncryptedData(mSignedData, signedDataSignature, Constants.VCardType.SIGNED);
|
||||
ContactEncryptedData contactEncryptedDataType0 = null;
|
||||
List<ContactEncryptedData> contactEncryptedDataList = contact.getEncryptedData();
|
||||
for (ContactEncryptedData data : contactEncryptedDataList) {
|
||||
if (data.getType() == 0) {
|
||||
contactEncryptedDataType0 = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (contactEncryptedDataType0 != null) {
|
||||
String vCardType0String = contactEncryptedDataType0.getData();
|
||||
final VCard vCardType0 = vCardType0String != null ? Ezvcard.parse(vCardType0String).first() : null;
|
||||
List<Email> emailsType0 = vCardType0.getEmails();
|
||||
vCardType0.getEmails().removeAll(emailsType0);
|
||||
contact.addEncryptedData(new ContactEncryptedData(vCardType0.write(), "", Constants.VCardType.UNSIGNED));
|
||||
}
|
||||
contact.addEncryptedData(contactSignedData);
|
||||
contact.addEncryptedData(contactEncryptedData);
|
||||
contact.setName(contactName);
|
||||
contact.setEmails(contactEmails);
|
||||
mContactDao.insertFullContactDetailsBlocking(contact);
|
||||
if (updateJoins) {
|
||||
for (Map.Entry<ContactLabelUiModel, List<String>> entry : mapContactGroupContactEmails.entrySet()) {
|
||||
updateJoins(entry.getKey().getId().getId(), entry.getKey().getName(), entry.getValue());
|
||||
}
|
||||
} else {
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.SAVED, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateJoins(String contactGroupId, String contactGroupName, List<String> membersList) {
|
||||
LabelContactsBody labelContactsBody = new LabelContactsBody(contactGroupId, membersList);
|
||||
try {
|
||||
getApi().labelContacts(labelContactsBody)
|
||||
.doOnComplete(() -> {
|
||||
//List<ContactEmailContactLabelJoin> joins = mContactDao.fetchJoinsBlocking(contactGroupId);
|
||||
for (String contactEmail : membersList) {
|
||||
// joins.add(new ContactEmailContactLabelJoin(contactEmail, contactGroupId));
|
||||
}
|
||||
//mContactDao.saveContactEmailContactLabelBlocking(joins);
|
||||
})
|
||||
.doOnError(throwable ->
|
||||
getJobManager().addJobInBackground(
|
||||
new SetMembersForContactGroupJob(contactGroupId, contactGroupName, membersList, labelRepository)
|
||||
)
|
||||
)
|
||||
.subscribeOn(ThreadSchedulers.io())
|
||||
.observeOn(ThreadSchedulers.io())
|
||||
.subscribe();
|
||||
} catch (Exception e) {
|
||||
AppUtil.postEventOnUi(new ContactEvent(ContactEvent.ERROR, false));
|
||||
}
|
||||
}
|
||||
|
||||
private List<ContactLabelUiModel> findContactLabelsByEmail(ContactEmail contactEmail) {
|
||||
for (Map.Entry<ContactEmail, List<ContactLabelUiModel>> entry : mMapEmailGroupsIds.entrySet()) {
|
||||
if (entry.getKey().getEmail().equals(contactEmail.getEmail())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private void requireContactDao() {
|
||||
if (mContactDao == null) {
|
||||
mContactDao = ContactDatabase.Companion
|
||||
.getInstance(getApplicationContext(), getUserId())
|
||||
.getDao();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
package ch.protonmail.android.jobs
|
||||
|
||||
import android.database.sqlite.SQLiteBlobTooBigException
|
||||
import ch.protonmail.android.api.models.ContactEncryptedData
|
||||
import ch.protonmail.android.api.models.CreateContactV2BodyItem
|
||||
import ch.protonmail.android.api.models.contacts.send.LabelContactsBody
|
||||
import ch.protonmail.android.api.segments.RESPONSE_CODE_ERROR_EMAIL_DUPLICATE_FAILED
|
||||
import ch.protonmail.android.api.segments.RESPONSE_CODE_ERROR_EMAIL_EXIST
|
||||
import ch.protonmail.android.api.segments.RESPONSE_CODE_ERROR_INVALID_EMAIL
|
||||
import ch.protonmail.android.contacts.details.presentation.model.ContactLabelUiModel
|
||||
import ch.protonmail.android.contacts.groups.jobs.SetMembersForContactGroupJob
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.crypto.Crypto.Companion.forUser
|
||||
import ch.protonmail.android.data.local.ContactDao
|
||||
import ch.protonmail.android.data.local.ContactDatabase
|
||||
import ch.protonmail.android.data.local.model.ContactEmail
|
||||
import ch.protonmail.android.data.local.model.FullContactDetails
|
||||
import ch.protonmail.android.events.ContactEvent
|
||||
import ch.protonmail.android.labels.data.LabelRepository
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import com.birbit.android.jobqueue.Params
|
||||
import ezvcard.Ezvcard
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import timber.log.Timber
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
class UpdateContactJob(
|
||||
private val contactId: String,
|
||||
private val contactName: String,
|
||||
private val contactEmails: List<ContactEmail>,
|
||||
private val encryptedData: String,
|
||||
private val signedData: String,
|
||||
private val mapEmailGroupsIds: Map<ContactEmail, List<ContactLabelUiModel>>,
|
||||
private val labelRepository: LabelRepository
|
||||
) : ProtonMailEndlessJob(Params(Priority.MEDIUM).requireNetwork().persist().groupBy(Constants.JOB_GROUP_CONTACT)) {
|
||||
|
||||
@Transient
|
||||
private var contactDao: ContactDao? = null
|
||||
override fun onAdded() {
|
||||
val crypto = forUser(getUserManager(), userId!!)
|
||||
try {
|
||||
val tct = crypto.encrypt(encryptedData, false)
|
||||
val encryptedDataSignature = crypto.sign(encryptedData)
|
||||
val signedDataSignature = crypto.sign(signedData)
|
||||
updateContact(contactName, contactEmails, tct.armored, encryptedDataSignature, signedDataSignature, false)
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.SAVED, true))
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "UpdateContact error")
|
||||
}
|
||||
if (!getQueueNetworkUtil().isConnected()) {
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.NO_NETWORK, true))
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun onRun() {
|
||||
val crypto = forUser(getUserManager(), userId!!)
|
||||
requireContactDao()
|
||||
val tct = crypto.encrypt(encryptedData, false)
|
||||
val encryptedDataSignature = crypto.sign(encryptedData)
|
||||
val signedDataSignature = crypto.sign(signedData)
|
||||
val body = CreateContactV2BodyItem(
|
||||
signedData, signedDataSignature,
|
||||
tct.armored, encryptedDataSignature
|
||||
)
|
||||
val response = getApi().updateContact(contactId, body)
|
||||
if (response != null) {
|
||||
Timber.v("Update contacts response code:%s error:%s", response.code, response.error)
|
||||
if (response.code == RESPONSE_CODE_ERROR_EMAIL_EXIST) {
|
||||
// TODO: 9/14/17 todoContacts throw error
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.ALREADY_EXIST, true))
|
||||
} else if (response.code == RESPONSE_CODE_ERROR_INVALID_EMAIL) {
|
||||
// TODO: 9/14/17 todoContacts throw error
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.INVALID_EMAIL, true))
|
||||
} else if (response.code == RESPONSE_CODE_ERROR_EMAIL_DUPLICATE_FAILED) {
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.DUPLICATE_EMAIL, true))
|
||||
} else {
|
||||
updateContact(
|
||||
contactName, response.contact.emails!!, tct.armored, encryptedDataSignature, signedDataSignature,
|
||||
true
|
||||
)
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.SUCCESS, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateContact(
|
||||
contactName: String,
|
||||
contactEmails: List<ContactEmail>,
|
||||
encryptedData: String,
|
||||
encryptedDataSignature: String,
|
||||
signedDataSignature: String,
|
||||
updateJoins: Boolean
|
||||
) {
|
||||
requireContactDao()
|
||||
val contactData = contactDao!!.findContactDataById(contactId)
|
||||
if (contactData != null) {
|
||||
contactData.name = contactName
|
||||
contactDao!!.saveContactData(contactData)
|
||||
}
|
||||
val emails = contactDao!!.findContactEmailsByContactIdBlocking(contactId)
|
||||
contactDao!!.deleteAllContactsEmails(emails)
|
||||
for (email in contactEmails) {
|
||||
val emailToClear = email.email
|
||||
contactDao!!.clearByEmailBlocking(emailToClear)
|
||||
}
|
||||
contactDao!!.saveAllContactsEmailsBlocking(contactEmails)
|
||||
val mapContactGroupContactEmails: MutableMap<ContactLabelUiModel, MutableList<String>> = HashMap()
|
||||
if (updateJoins) {
|
||||
for (email in contactEmails) {
|
||||
val labels = findContactLabelsByEmail(email)
|
||||
for (label in labels) {
|
||||
var labelEmails = mapContactGroupContactEmails[label]
|
||||
if (labelEmails == null) {
|
||||
labelEmails = ArrayList()
|
||||
}
|
||||
labelEmails.add(email.contactEmailId)
|
||||
mapContactGroupContactEmails[label] = labelEmails
|
||||
}
|
||||
}
|
||||
}
|
||||
var contact: FullContactDetails? = null
|
||||
try {
|
||||
contact = contactDao!!.findFullContactDetailsByIdBlocking(contactId)
|
||||
} catch (tooBigException: SQLiteBlobTooBigException) {
|
||||
Timber.i(tooBigException, "Data too big to be fetched")
|
||||
}
|
||||
if (contact != null) {
|
||||
val contactEncryptedData =
|
||||
ContactEncryptedData(encryptedData, encryptedDataSignature, Constants.VCardType.SIGNED_ENCRYPTED)
|
||||
val contactSignedData = ContactEncryptedData(signedData, signedDataSignature, Constants.VCardType.SIGNED)
|
||||
var contactEncryptedDataType0: ContactEncryptedData? = null
|
||||
val contactEncryptedDataList: List<ContactEncryptedData>? = contact.encryptedData
|
||||
for (data in contactEncryptedDataList!!) {
|
||||
if (data.type == 0) {
|
||||
contactEncryptedDataType0 = data
|
||||
break
|
||||
}
|
||||
}
|
||||
if (contactEncryptedDataType0 != null) {
|
||||
val vCardType0String = contactEncryptedDataType0.data
|
||||
val vCardType0 = if (vCardType0String != null) Ezvcard.parse(vCardType0String).first() else null
|
||||
val emailsType0 = vCardType0!!.emails
|
||||
vCardType0.emails.removeAll(emailsType0)
|
||||
contact.addEncryptedData(ContactEncryptedData(vCardType0.write(), "", Constants.VCardType.UNSIGNED))
|
||||
}
|
||||
contact.addEncryptedData(contactSignedData)
|
||||
contact.addEncryptedData(contactEncryptedData)
|
||||
contact.name = contactName
|
||||
contact.emails = contactEmails
|
||||
contactDao!!.insertFullContactDetailsBlocking(contact)
|
||||
if (updateJoins) {
|
||||
for ((key, value) in mapContactGroupContactEmails) {
|
||||
updateJoins(key.id.id, key.name, value)
|
||||
}
|
||||
} else {
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.SAVED, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateJoins(
|
||||
contactGroupId: String,
|
||||
contactGroupName: String,
|
||||
membersList: List<String>
|
||||
) {
|
||||
val labelContactsBody = LabelContactsBody(contactGroupId, membersList)
|
||||
runBlocking {
|
||||
runCatching {
|
||||
getApi().labelContacts(labelContactsBody)
|
||||
}.onFailure {
|
||||
getJobManager().addJobInBackground(
|
||||
SetMembersForContactGroupJob(contactGroupId, contactGroupName, membersList, labelRepository)
|
||||
)
|
||||
AppUtil.postEventOnUi(ContactEvent(ContactEvent.ERROR, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findContactLabelsByEmail(contactEmail: ContactEmail): List<ContactLabelUiModel> {
|
||||
for ((key, value) in mapEmailGroupsIds) {
|
||||
if (key.email == contactEmail.email) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun requireContactDao() {
|
||||
if (contactDao == null) {
|
||||
contactDao = ContactDatabase
|
||||
.getInstance(applicationContext, userId!!)
|
||||
.getDao()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import kotlinx.coroutines.async
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import me.proton.core.domain.entity.UserId
|
||||
|
@ -52,6 +53,9 @@ internal class LabelRepositoryImpl @Inject constructor(
|
|||
fetchAndSaveAllLabels(userId)
|
||||
}
|
||||
}
|
||||
.onEach {
|
||||
Timber.v("Emitting new labels size: ${it.size} user: $userId")
|
||||
}
|
||||
|
||||
override suspend fun findAllLabels(userId: UserId, shallRefresh: Boolean): List<LabelEntity> =
|
||||
observeAllLabels(userId, shallRefresh).first()
|
||||
|
|
|
@ -47,7 +47,11 @@ class LabelApi(private val apiProvider: ApiProvider) : LabelApiSpec {
|
|||
createLabel(label)
|
||||
}
|
||||
|
||||
override suspend fun updateLabel(userId: UserId, labelId: String, labelRequestBody: LabelRequestBody):
|
||||
override suspend fun updateLabel(
|
||||
userId: UserId,
|
||||
labelId: String,
|
||||
labelRequestBody: LabelRequestBody
|
||||
):
|
||||
ApiResult<LabelResponse> = apiProvider.get<LabelService>(userId).invoke {
|
||||
updateLabel(labelId, labelRequestBody)
|
||||
}
|
||||
|
|
|
@ -100,10 +100,10 @@ class ContactGroupEditCreateRepositoryTest {
|
|||
name = "name",
|
||||
color = "color",
|
||||
type = testType.typeInt,
|
||||
notify = 0,
|
||||
notify = null,
|
||||
parentId = testParentId,
|
||||
expanded = 0,
|
||||
sticky = 0
|
||||
expanded = null,
|
||||
sticky = null
|
||||
)
|
||||
|
||||
// when
|
||||
|
|
Loading…
Reference in New Issue