New way of faetching of contact thumbnail image added.
MAILAND-1875
This commit is contained in:
parent
cb243c87dc
commit
bb6d4af6c4
|
@ -8,8 +8,8 @@
|
|||
<inspection_tool class="FunctionName" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="namePattern" value="[a-zA-Z][A-Za-z\d]*" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IllegalIdentifier" enabled="false" level="ERROR" enabled_by_default="false">
|
||||
<scope name="Gradle files" level="None" enabled="false" />
|
||||
<inspection_tool class="IllegalIdentifier" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<scope name="Gradle files" level="None" enabled="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IncompatibleAPI" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ObjectPropertyName" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
|
|
|
@ -341,7 +341,8 @@ dependencies {
|
|||
`viewStateStore-paging`,
|
||||
`remark`,
|
||||
`okio`,
|
||||
`store`
|
||||
`store`,
|
||||
`coil`
|
||||
)
|
||||
|
||||
debugImplementation(
|
||||
|
|
|
@ -167,7 +167,7 @@ internal class ContactDaoTest {
|
|||
saveAllContactsData(contactData)
|
||||
saveAllContactsEmails(contactEmails)
|
||||
}
|
||||
fullContactDetails.forEach(this::insertFullContactDetails)
|
||||
fullContactDetails.forEach(this::insertFullContactDetailsBlocking)
|
||||
}
|
||||
|
||||
private fun assertDatabaseState(
|
||||
|
@ -528,7 +528,7 @@ internal class ContactDaoTest {
|
|||
)
|
||||
)
|
||||
val expected = fullContactDetails + inserted
|
||||
database.insertFullContactDetails(inserted)
|
||||
database.insertFullContactDetailsBlocking(inserted)
|
||||
assertDatabaseState(expectedFullContactDetails = expected)
|
||||
}
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ public class SendPreferencesFactory {
|
|||
continue;
|
||||
}
|
||||
FullContactDetails contact = contactDetails.get(contactID).getContact();
|
||||
contactDao.insertFullContactDetails(contact);
|
||||
contactDao.insertFullContactDetailsBlocking(contact);
|
||||
result.put(email, contact);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -453,7 +453,7 @@ class EventHandler @AssistedInject constructor(
|
|||
val contactName = contact.name
|
||||
val contactData = ContactData(contactId, contactName!!)
|
||||
contactDao.saveContactData(contactData)
|
||||
contactDao.insertFullContactDetails(contact)
|
||||
contactDao.insertFullContactDetailsBlocking(contact)
|
||||
}
|
||||
|
||||
EventType.UPDATE -> {
|
||||
|
@ -474,7 +474,7 @@ class EventHandler @AssistedInject constructor(
|
|||
if (localFullContact != null) {
|
||||
contactDao.deleteFullContactsDetails(localFullContact)
|
||||
}
|
||||
contactDao.insertFullContactDetails(fullContact)
|
||||
contactDao.insertFullContactDetailsBlocking(fullContact)
|
||||
}
|
||||
|
||||
EventType.DELETE -> {
|
||||
|
|
|
@ -219,7 +219,7 @@ open class ContactDetailsRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun insertFullContactDetails(fullContactDetails: FullContactDetails) =
|
||||
suspend fun insertFullContactDetails(fullContactDetails: FullContactDetails) =
|
||||
contactDao.insertFullContactDetails(fullContactDetails)
|
||||
|
||||
}
|
||||
|
|
|
@ -21,16 +21,20 @@ package ch.protonmail.android.contacts.details.presentation
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.ContactsContract
|
||||
import android.view.MenuItem
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -43,15 +47,19 @@ import ch.protonmail.android.contacts.details.presentation.model.ContactDetailsV
|
|||
import ch.protonmail.android.databinding.ActivityContactDetailsBinding
|
||||
import ch.protonmail.android.utils.extensions.showToast
|
||||
import ch.protonmail.android.views.ListItemThumbnail
|
||||
import coil.request.ImageRequest
|
||||
import coil.transform.CircleCropTransformation
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.core.util.kotlin.EMPTY_STRING
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ContactDetailsActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var thumbnailImage: ImageView
|
||||
private lateinit var detailsAdapter: ContactDetailsAdapter
|
||||
private lateinit var thumbnail: ListItemThumbnail
|
||||
private lateinit var contactName: TextView
|
||||
|
@ -77,13 +85,14 @@ class ContactDetailsActivity : AppCompatActivity() {
|
|||
detailsContainer = binding.scrollViewContactDetails
|
||||
contactName = binding.textViewContactDetailsContactName
|
||||
thumbnail = binding.thumbnailContactDetails
|
||||
thumbnailImage = binding.imageViewContactDetailsThumbnail
|
||||
|
||||
detailsAdapter = ContactDetailsAdapter(
|
||||
::onWriteToContact,
|
||||
::onCallContact,
|
||||
::onAddressClicked,
|
||||
::onUrlClicked
|
||||
)
|
||||
)
|
||||
binding.recyclerViewContactsDetails.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = detailsAdapter
|
||||
|
@ -174,6 +183,50 @@ class ContactDetailsActivity : AppCompatActivity() {
|
|||
) {
|
||||
contactName.text = title
|
||||
thumbnail.bind(isSelectedActive = false, isMultiselectActive = false, initials = initials)
|
||||
|
||||
if (!photoBytes.isNullOrEmpty()) {
|
||||
setThumbnailImage(photoBytes)
|
||||
} else if (photoUrl != null) {
|
||||
loadThumbnailImage(photoUrl)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setThumbnailImage(photoBytes: List<Byte>) {
|
||||
val byteArray = photoBytes.toByteArray()
|
||||
val imageBitmap: Bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
|
||||
val roundedImage = RoundedBitmapDrawableFactory.create(resources, imageBitmap).apply {
|
||||
setAntiAlias(true)
|
||||
cornerRadius = resources.getDimensionPixelSize(R.dimen.avatar_size) / 2f
|
||||
}
|
||||
thumbnailImage.apply {
|
||||
setImageDrawable(roundedImage)
|
||||
isVisible = true
|
||||
}
|
||||
thumbnail.isVisible = false
|
||||
}
|
||||
|
||||
private fun loadThumbnailImage(photoUrl: String?) {
|
||||
Timber.v("Loading photo url: $photoUrl")
|
||||
val targetSize = resources.getDimensionPixelSize(R.dimen.avatar_size)
|
||||
ImageRequest.Builder(this)
|
||||
.data(photoUrl)
|
||||
.size(targetSize, targetSize)
|
||||
.transformations(CircleCropTransformation())
|
||||
.target(
|
||||
onSuccess = { drawable ->
|
||||
Timber.d("Thumbnail loading finished")
|
||||
thumbnailImage.apply {
|
||||
setImageDrawable(drawable)
|
||||
isVisible = true
|
||||
}
|
||||
thumbnail.isVisible = false
|
||||
},
|
||||
onError = {
|
||||
Timber.i("Thumbnail loading error")
|
||||
thumbnailImage.isVisible = false
|
||||
thumbnail.isVisible = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun setContactDataForActionButtons(item: ContactDetailsUiItem) {
|
||||
|
|
|
@ -59,7 +59,9 @@ class ContactDetailsViewModel @Inject constructor(
|
|||
Timber.v("getContactDetails for $contactId")
|
||||
viewModelScope.launch {
|
||||
fetchContactDetails(contactId)
|
||||
.catch { ContactDetailsViewState.Error(it) }
|
||||
.catch {
|
||||
mutableContactsResultFlow.value = ContactDetailsViewState.Error(it)
|
||||
}
|
||||
.collect { fetchResult ->
|
||||
mutableContactsResultFlow.value = mapper.mapToContactViewData(fetchResult)
|
||||
}
|
||||
|
|
|
@ -400,7 +400,10 @@ interface ContactDao {
|
|||
|
||||
//region Full contact details
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertFullContactDetails(fullContactDetails: FullContactDetails)
|
||||
fun insertFullContactDetailsBlocking(fullContactDetails: FullContactDetails)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertFullContactDetails(fullContactDetails: FullContactDetails)
|
||||
|
||||
@Query("SELECT * FROM $TABLE_FULL_CONTACT_DETAILS WHERE $COLUMN_CONTACT_ID = :id")
|
||||
fun findFullContactDetailsByIdBlocking(id: String): FullContactDetails?
|
||||
|
|
|
@ -71,7 +71,6 @@ class FullContactDetailsResponse : ResponseBody() {
|
|||
@SerializedName(FIELD_CONTACT)
|
||||
private lateinit var serverContact: ServerFullContactDetails
|
||||
|
||||
|
||||
val contact: FullContactDetails by lazy {
|
||||
val factory = FullContactDetailsFactory()
|
||||
factory.createFullContactDetails(serverContact)
|
||||
|
|
|
@ -122,7 +122,7 @@ public class ResignContactJob extends ProtonMailEndlessJob {
|
|||
AppUtil.postEventOnUi(new ResignContactEvent(mSendPreference, ContactEvent.ERROR, mDestination));
|
||||
return;
|
||||
}
|
||||
contactDao.insertFullContactDetails(fullContactDetails);
|
||||
contactDao.insertFullContactDetailsBlocking(fullContactDetails);
|
||||
|
||||
ContactEncryptedData encCard = getCardByType(fullContactDetails.getEncryptedData(), ContactEncryption.ENCRYPTED_AND_SIGNED);
|
||||
CreateContactV2BodyItem body;
|
||||
|
@ -145,7 +145,7 @@ public class ResignContactJob extends ProtonMailEndlessJob {
|
|||
AppUtil.postEventOnUi(new ResignContactEvent(mSendPreference, ContactEvent.DUPLICATE_EMAIL, mDestination));
|
||||
} else {
|
||||
//TODO this insert is probably not needed as it is already saved some lines above
|
||||
contactDao.insertFullContactDetails(fullContactDetails);
|
||||
contactDao.insertFullContactDetailsBlocking(fullContactDetails);
|
||||
AppUtil.postEventOnUi(new ResignContactEvent(mSendPreference, ContactEvent.SUCCESS, mDestination));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ public class UpdateContactJob extends ProtonMailEndlessJob {
|
|||
contact.addEncryptedData(contactEncryptedData);
|
||||
contact.setName(contactName);
|
||||
contact.setEmails(contactEmails);
|
||||
mContactDao.insertFullContactDetails(contact);
|
||||
mContactDao.insertFullContactDetailsBlocking(contact);
|
||||
if (updateJoins) {
|
||||
for (Map.Entry<ContactLabel, List<String>> entry : mapContactGroupContactEmails.entrySet()) {
|
||||
updateJoins(entry.getKey().getID(), entry.getKey().getName(), entry.getValue());
|
||||
|
|
|
@ -82,7 +82,6 @@ class FetchVerificationKeys @Inject constructor(
|
|||
emptyList()
|
||||
}
|
||||
)
|
||||
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import androidx.core.content.withStyledAttributes
|
|||
import ch.protonmail.android.R
|
||||
import timber.log.Timber
|
||||
|
||||
private const val TEXT_SIZE_PROPORTION = 2.5f
|
||||
private const val TEXT_SIZE_PROPORTION = 3f
|
||||
private const val DEFAULT_CIRCLE_SIZE = 100
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,14 +43,24 @@ along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
|||
android:layout_margin="@dimen/padding_l"
|
||||
app:circleSize="@dimen/avatar_size" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view_contact_details_thumbnail"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="@dimen/padding_l"
|
||||
android:contentDescription="@string/contact_details"
|
||||
android:scaleType="fitXY"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view_contact_details_contact_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Proton.Text.Headline"
|
||||
android:layout_gravity="center|bottom"
|
||||
android:layout_marginBottom="@dimen/padding_l"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Proton.Text.Headline"
|
||||
tools:text="@tools:sample/full_names" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
|
|
@ -71,7 +71,7 @@ class FetchVerificationKeysTest : CoroutinesTest {
|
|||
every { contactId } returns testContactId
|
||||
}
|
||||
every { contactDao.findContactEmailByEmail(testEmail) } returns testContactEmail
|
||||
every { contactDao.insertFullContactDetails(any()) } returns Unit
|
||||
every { contactDao.insertFullContactDetailsBlocking(any()) } returns Unit
|
||||
val fullContactDetailsResponse = mockk<FullContactDetailsResponse> {
|
||||
every { contact } returns mockk {
|
||||
every { contactId } returns "contactId"
|
||||
|
@ -123,7 +123,7 @@ class FetchVerificationKeysTest : CoroutinesTest {
|
|||
every { contactId } returns testContactId
|
||||
}
|
||||
every { contactDao.findContactEmailByEmail(testEmail) } returns testContactEmail
|
||||
every { contactDao.insertFullContactDetails(any()) } returns Unit
|
||||
every { contactDao.insertFullContactDetailsBlocking(any()) } returns Unit
|
||||
val fullContactDetailsResponse = mockk<FullContactDetailsResponse> {
|
||||
every { contact } returns mockk {
|
||||
every { contactId } returns "contactId"
|
||||
|
|
|
@ -122,6 +122,8 @@ val DependencyHandler.`fasterxml-jackson-anno` get() = dependency("com.fast
|
|||
val DependencyHandler.`fasterxml-jackson-databind` get() = dependency("com.fasterxml.jackson.core", module = "jackson-databind") version `jackson version`
|
||||
val DependencyHandler.`remark` get() = dependency("com.overzealous", module = "remark") version `remark version`
|
||||
val DependencyHandler.`store` get() = dependency("com.dropbox.mobile.store", module = "store4") version `store version`
|
||||
val DependencyHandler.`coil` get() = dependency("io.coil-kt", module="coil") version `coil version`
|
||||
val DependencyHandler.`coil-base` get() = dependency("io.coil-kt", module="coil-base") version `coil version`
|
||||
// endregion
|
||||
|
||||
// endregion
|
||||
|
|
|
@ -134,3 +134,4 @@ const val `timber version` = "4.7.1" // Released:
|
|||
const val `trustKit version` = "1.1.2" // Released: Jun 09, 2019
|
||||
const val `remark version` = "1.1.0" // Released: Dec 08, 2016
|
||||
const val `store version` = "4.0.0" // Released: Nov 30, 2020
|
||||
const val `coil version` = "1.2.1" // Released: Apr 28, 2021
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.io.InputStream
|
|||
*
|
||||
* @author Davide Farella
|
||||
*/
|
||||
@Deprecated("Replaced with coil library")
|
||||
interface DownloadFile {
|
||||
|
||||
suspend operator fun invoke(url: String): InputStream
|
||||
|
|
Loading…
Reference in New Issue