forked from upstream-mirrors/quik
Store contact’s photoUri
So much less complicated now!
This commit is contained in:
@@ -41,6 +41,7 @@ class CursorToContactImpl @Inject constructor(
|
||||
Phone.TYPE,
|
||||
Phone.LABEL,
|
||||
Phone.DISPLAY_NAME,
|
||||
Phone.PHOTO_URI,
|
||||
Phone.STARRED,
|
||||
Phone.CONTACT_LAST_UPDATED_TIMESTAMP
|
||||
)
|
||||
@@ -52,13 +53,15 @@ class CursorToContactImpl @Inject constructor(
|
||||
const val COLUMN_TYPE = 4
|
||||
const val COLUMN_LABEL = 5
|
||||
const val COLUMN_DISPLAY_NAME = 6
|
||||
const val COLUMN_STARRED = 7
|
||||
const val CONTACT_LAST_UPDATED = 8
|
||||
const val COLUMN_PHOTO_URI = 7
|
||||
const val COLUMN_STARRED = 8
|
||||
const val CONTACT_LAST_UPDATED = 9
|
||||
}
|
||||
|
||||
override fun map(from: Cursor) = Contact().apply {
|
||||
lookupKey = from.getString(COLUMN_LOOKUP_KEY)
|
||||
name = from.getString(COLUMN_DISPLAY_NAME) ?: ""
|
||||
photoUri = from.getString(COLUMN_PHOTO_URI)
|
||||
numbers.add(PhoneNumber(
|
||||
id = from.getLong(COLUMN_ID),
|
||||
accountType = from.getString(COLUMN_ACCOUNT_TYPE),
|
||||
|
||||
@@ -126,6 +126,7 @@ class QkRealmMigration : RealmMigration {
|
||||
|
||||
realm.schema.get("Contact")
|
||||
?.addField("starred", Boolean::class.java, FieldAttribute.REQUIRED)
|
||||
?.addField("photoUri", String::class.java)
|
||||
|
||||
realm.schema.get("PhoneNumber")
|
||||
?.addField("id", Long::class.java, FieldAttribute.PRIMARY_KEY, FieldAttribute.REQUIRED)
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.moez.QKSMS.util
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.ContactsContract
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.Key
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import com.moez.QKSMS.repository.ContactRepository
|
||||
import com.moez.QKSMS.repository.ContactRepositoryImpl
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.io.InputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
class ContactImageLoader(
|
||||
private val context: Context,
|
||||
private val contactRepo: ContactRepository,
|
||||
private val phoneNumberUtils: PhoneNumberUtils
|
||||
) : ModelLoader<String, InputStream> {
|
||||
|
||||
override fun handles(model: String): Boolean {
|
||||
return model.startsWith("tel:")
|
||||
}
|
||||
|
||||
override fun buildLoadData(
|
||||
model: String,
|
||||
width: Int,
|
||||
height: Int,
|
||||
options: Options
|
||||
): ModelLoader.LoadData<InputStream>? {
|
||||
return ModelLoader.LoadData(
|
||||
ContactImageKey(phoneNumberUtils.normalizeNumber(model)),
|
||||
ContactImageFetcher(context, contactRepo, model))
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val context: Context,
|
||||
private val prefs: Preferences
|
||||
) : ModelLoaderFactory<String, InputStream> {
|
||||
|
||||
override fun build(multiFactory: MultiModelLoaderFactory): ContactImageLoader {
|
||||
return ContactImageLoader(context, ContactRepositoryImpl(context, prefs), PhoneNumberUtils(context))
|
||||
}
|
||||
|
||||
override fun teardown() {} // nothing to do here
|
||||
}
|
||||
|
||||
class ContactImageKey(private val address: String) : Key {
|
||||
override fun updateDiskCacheKey(digest: MessageDigest) = digest.update(address.toByteArray())
|
||||
}
|
||||
|
||||
class ContactImageFetcher(
|
||||
private val context: Context,
|
||||
private val contactRepo: ContactRepository,
|
||||
private val address: String
|
||||
) : DataFetcher<InputStream> {
|
||||
|
||||
private var loadPhotoDisposable: Disposable? = null
|
||||
|
||||
override fun cleanup() {}
|
||||
override fun getDataClass() = InputStream::class.java
|
||||
override fun getDataSource() = DataSource.LOCAL
|
||||
|
||||
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
|
||||
loadPhotoDisposable = contactRepo.findContactUri(address)
|
||||
.map { uri ->
|
||||
ContactsContract.Contacts.openContactPhotoInputStream(context.contentResolver, uri, true)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ inputStream -> callback.onDataReady(inputStream) },
|
||||
{ error -> callback.onLoadFailed(Exception(error)) })
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
loadPhotoDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,15 +19,12 @@
|
||||
package com.moez.QKSMS.util
|
||||
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.GlideBuilder
|
||||
import com.bumptech.glide.Registry
|
||||
import com.bumptech.glide.annotation.GlideModule
|
||||
import com.bumptech.glide.module.AppGlideModule
|
||||
import com.f2prateek.rx.preferences2.RxSharedPreferences
|
||||
import java.io.InputStream
|
||||
|
||||
@GlideModule
|
||||
class GlideAppModule : AppGlideModule() {
|
||||
@@ -37,10 +34,6 @@ class GlideAppModule : AppGlideModule() {
|
||||
}
|
||||
|
||||
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||
// TODO use DI to create the ContactImageLoader.Factory
|
||||
val rxPrefs = RxSharedPreferences.create(PreferenceManager.getDefaultSharedPreferences(context))
|
||||
val prefs = Preferences(context, rxPrefs)
|
||||
registry.append(String::class.java, InputStream::class.java, ContactImageLoader.Factory(context, prefs))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ open class Contact(
|
||||
@PrimaryKey var lookupKey: String = "",
|
||||
var numbers: RealmList<PhoneNumber> = RealmList(),
|
||||
var name: String = "",
|
||||
var photoUri: String? = null,
|
||||
var starred: Boolean = false,
|
||||
var lastUpdate: Long = 0
|
||||
) : RealmObject() {
|
||||
|
||||
@@ -154,11 +154,10 @@ class NotificationManagerImpl @Inject constructor(
|
||||
.firstOrNull { phoneNumberUtils.compare(it.address, message.address) }
|
||||
|
||||
person.setName(recipient?.getDisplayName() ?: message.address)
|
||||
|
||||
person.setIcon(GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.circleCrop()
|
||||
.load("tel:${message.address}")
|
||||
.load(recipient?.contact?.photoUri)
|
||||
.submit(64.dpToPx(context), 64.dpToPx(context))
|
||||
.let { futureGet -> tryOrNull(false) { futureGet.get() } }
|
||||
?.let(IconCompat::createWithBitmap))
|
||||
@@ -178,12 +177,12 @@ class NotificationManagerImpl @Inject constructor(
|
||||
|
||||
// Set the large icon
|
||||
val avatar = conversation.recipients.takeIf { it.size == 1 }
|
||||
?.first()?.address
|
||||
?.let { address ->
|
||||
?.first()?.contact?.photoUri
|
||||
?.let { photoUri ->
|
||||
GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.circleCrop()
|
||||
.load("tel:$address")
|
||||
.load(photoUri)
|
||||
.submit(64.dpToPx(context), 64.dpToPx(context))
|
||||
}
|
||||
?.let { futureGet -> tryOrNull(false) { futureGet.get() } }
|
||||
|
||||
@@ -52,13 +52,13 @@ class QkChooserTargetService : ChooserTargetService() {
|
||||
}
|
||||
|
||||
private fun createShortcutForConversation(conversation: Conversation): ChooserTarget {
|
||||
val icon = when {
|
||||
conversation.recipients.size == 1 -> {
|
||||
val address = conversation.recipients.first()!!.address
|
||||
val icon = when (conversation.recipients.size) {
|
||||
1 -> {
|
||||
val photoUri = conversation.recipients.first()?.contact?.photoUri
|
||||
val request = GlideApp.with(this)
|
||||
.asBitmap()
|
||||
.circleCrop()
|
||||
.load("tel:$address")
|
||||
.load(photoUri)
|
||||
.submit()
|
||||
val bitmap = tryOrNull(false) { request.get() }
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import com.moez.QKSMS.R
|
||||
import com.moez.QKSMS.common.Navigator
|
||||
import com.moez.QKSMS.common.util.Colors
|
||||
@@ -30,7 +29,6 @@ import com.moez.QKSMS.common.util.extensions.setBackgroundTint
|
||||
import com.moez.QKSMS.common.util.extensions.setTint
|
||||
import com.moez.QKSMS.injection.appComponent
|
||||
import com.moez.QKSMS.model.Contact
|
||||
import com.moez.QKSMS.model.Recipient
|
||||
import com.moez.QKSMS.util.GlideApp
|
||||
import kotlinx.android.synthetic.main.avatar_view.view.*
|
||||
import javax.inject.Inject
|
||||
@@ -54,7 +52,7 @@ class AvatarView @JvmOverloads constructor(
|
||||
|
||||
private var lookupKey: String? = null
|
||||
private var name: String? = null
|
||||
private var address: String? = null
|
||||
private var photoUri: String? = null
|
||||
private var lastUpdated: Long? = null
|
||||
|
||||
init {
|
||||
@@ -68,33 +66,13 @@ class AvatarView @JvmOverloads constructor(
|
||||
clipToOutline = true
|
||||
}
|
||||
|
||||
/**
|
||||
* If the [recipient] has a contact: use the contact's avatar, but keep the address.
|
||||
* Use the recipient address otherwise.
|
||||
*/
|
||||
fun setContact(recipient: Recipient?) {
|
||||
// If the recipient has a contact, just use that and return
|
||||
recipient?.contact?.let { contact ->
|
||||
setContact(contact, recipient.address)
|
||||
return
|
||||
}
|
||||
|
||||
lookupKey = null
|
||||
name = null
|
||||
address = recipient?.address
|
||||
lastUpdated = 0
|
||||
updateView()
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the [contact] information to display the avatar.
|
||||
* A specific [contactAddress] can be specified (useful when the contact has several addresses).
|
||||
*/
|
||||
fun setContact(contact: Contact?, contactAddress: String? = null) {
|
||||
fun setContact(contact: Contact?) {
|
||||
lookupKey = contact?.lookupKey
|
||||
name = contact?.name
|
||||
// If a contactAddress has been given, we use it. Use the contact address otherwise.
|
||||
address = contactAddress?.takeIf { it.isNotEmpty() } ?: contact?.numbers?.firstOrNull()?.address
|
||||
photoUri = contact?.photoUri
|
||||
lastUpdated = contact?.lastUpdate
|
||||
updateView()
|
||||
}
|
||||
@@ -108,7 +86,7 @@ class AvatarView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun applyTheme(threadId: Long) {
|
||||
private fun applyTheme(threadId: Long) {
|
||||
colors.theme(threadId).run {
|
||||
setBackgroundTint(theme)
|
||||
initial.setTextColor(textPrimary)
|
||||
@@ -130,11 +108,10 @@ class AvatarView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
photo.setImageDrawable(null)
|
||||
address?.let { address ->
|
||||
photoUri?.let { photoUri ->
|
||||
GlideApp.with(photo)
|
||||
.load("tel:$address")
|
||||
.signature(ObjectKey(lastUpdated ?: 0L))
|
||||
.load(photoUri)
|
||||
.into(photo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ class GroupAvatarView @JvmOverloads constructor(
|
||||
}
|
||||
avatar2.isVisible = contacts.size > 1
|
||||
|
||||
avatar1.setContact(contacts.getOrNull(0))
|
||||
avatar2.setContact(contacts.getOrNull(1))
|
||||
avatar1.setContact(contacts.getOrNull(0)?.contact)
|
||||
avatar2.setContact(contacts.getOrNull(1)?.contact)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ class MessagesAdapter @Inject constructor(
|
||||
// Bind the avatar
|
||||
if (!message.isMe()) {
|
||||
view.avatar.threadId = conversation?.id ?: 0
|
||||
view.avatar.setContact(contactCache[message.address])
|
||||
view.avatar.setContact(contactCache[message.address]?.contact)
|
||||
view.avatar.setVisible(!canGroup(message, next), View.INVISIBLE)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class ChipsAdapter @Inject constructor() : QkAdapter<Chip>() {
|
||||
val chip = getItem(position)
|
||||
val view = holder.containerView
|
||||
|
||||
view.avatar.setContact(chip.contact, chip.address)
|
||||
view.avatar.setContact(chip.contact)
|
||||
view.name.text = chip.contact?.name?.takeIf { it.isNotBlank() } ?: chip.address
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class DetailedChipView(context: Context) : RelativeLayout(context) {
|
||||
}
|
||||
|
||||
fun setChip(chip: Chip) {
|
||||
avatar.setContact(chip.contact, chip.address)
|
||||
avatar.setContact(chip.contact)
|
||||
name.text = chip.contact?.name?.takeIf { it.isNotBlank() } ?: chip.address
|
||||
info.text = chip.address
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ class ConversationRecipientAdapter @Inject constructor() : QkRealmAdapter<Recipi
|
||||
val view = holder.containerView
|
||||
|
||||
view.avatar.threadId = threadId
|
||||
view.avatar.setContact(recipient)
|
||||
view.avatar.setContact(recipient.contact)
|
||||
|
||||
view.name.text = recipient.contact?.name ?: recipient.address
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.moez.QKSMS.model.PhoneNumber
|
||||
import com.moez.QKSMS.repository.ConversationRepository
|
||||
import com.moez.QKSMS.util.GlideApp
|
||||
import com.moez.QKSMS.util.Preferences
|
||||
import com.moez.QKSMS.util.tryOrNull
|
||||
import javax.inject.Inject
|
||||
|
||||
class WidgetAdapter(intent: Intent) : RemoteViewsService.RemoteViewsFactory {
|
||||
@@ -130,17 +131,11 @@ class WidgetAdapter(intent: Intent) : RemoteViewsService.RemoteViewsFactory {
|
||||
}
|
||||
|
||||
remoteViews.setImageViewBitmap(R.id.photo, null)
|
||||
contact?.numbers?.firstOrNull()?.address?.let { address ->
|
||||
val futureGet = GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load("tel:$address")
|
||||
.submit(48.dpToPx(context), 48.dpToPx(context))
|
||||
|
||||
try {
|
||||
remoteViews.setImageViewBitmap(R.id.photo, futureGet.get())
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
val futureGet = GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(contact?.photoUri)
|
||||
.submit(48.dpToPx(context), 48.dpToPx(context))
|
||||
tryOrNull(false) { remoteViews.setImageViewBitmap(R.id.photo, futureGet.get()) }
|
||||
|
||||
// Name
|
||||
remoteViews.setTextColor(R.id.name, textPrimary)
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.moez.QKSMS.feature.widget.WidgetAdapter
|
||||
import com.moez.QKSMS.injection.android.ActivityBuilderModule
|
||||
import com.moez.QKSMS.injection.android.BroadcastReceiverBuilderModule
|
||||
import com.moez.QKSMS.injection.android.ServiceBuilderModule
|
||||
import com.moez.QKSMS.util.ContactImageLoader
|
||||
import dagger.Component
|
||||
import dagger.android.support.AndroidSupportInjectionModule
|
||||
import javax.inject.Singleton
|
||||
@@ -73,8 +72,6 @@ interface AppComponent {
|
||||
|
||||
fun inject(dialog: QkDialog)
|
||||
|
||||
fun inject(fetcher: ContactImageLoader.ContactImageFetcher)
|
||||
|
||||
fun inject(service: WidgetAdapter)
|
||||
|
||||
/**
|
||||
@@ -91,4 +88,4 @@ interface AppComponent {
|
||||
fun inject(view: QkSwitch)
|
||||
fun inject(view: QkTextView)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user