Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bitfireAT/vcard4android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRicki Hirner <hirner@bitfire.at>2022-03-20 21:22:31 +0300
committerRicki Hirner <hirner@bitfire.at>2022-03-20 22:05:53 +0300
commit76d56fed49c7d1dd08fd3d63f02aa7a11898e074 (patch)
tree6e67f685f8c95a004fa8bec2fa37422e6bee0c89
parent1af1eba9d22f777b5c3a8fa047ab1df8772d5f18 (diff)
Contact photos: always read them as JPEG
-rw-r--r--src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt23
-rw-r--r--src/androidTest/resources/small.jpgbin0 -> 1614 bytes
-rw-r--r--src/androidTest/resources/small.pngbin0 -> 757 bytes
-rw-r--r--src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt62
4 files changed, 80 insertions, 5 deletions
diff --git a/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt b/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt
index fea2217..004995e 100644
--- a/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt
+++ b/src/androidTest/java/at/bitfire/vcard4android/contactrow/PhotoHandlerTest.kt
@@ -23,7 +23,7 @@ import org.junit.Assert.*
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
-import kotlin.random.Random
+import java.util.*
class PhotoHandlerTest {
@@ -57,6 +57,25 @@ class PhotoHandlerTest {
@Test
+ fun testConvertToJpeg_Invalid() {
+ val blob = ByteArray(1024) { it.toByte() }
+ assertNull(PhotoHandler.convertToJpeg(blob, 75))
+ }
+
+ @Test
+ fun testConvertToJpeg_Jpeg() {
+ val blob = IOUtils.resourceToByteArray("/small.jpg")
+ assertArrayEquals(blob, PhotoHandler.convertToJpeg(blob, 75))
+ }
+
+ @Test
+ fun testConvertToJpeg_Png() {
+ val blob = IOUtils.resourceToByteArray("/small.png")
+ assertFalse(Arrays.equals(blob, PhotoHandler.convertToJpeg(blob, 75)))
+ }
+
+
+ @Test
fun testPhoto_Empty() {
val contact = Contact()
PhotoHandler(null).handle(ContentValues().apply {
@@ -67,7 +86,7 @@ class PhotoHandlerTest {
@Test
fun testPhoto_Blob() {
- val blob = ByteArray(1024) { Random.nextInt().toByte() }
+ val blob = IOUtils.resourceToByteArray("/small.jpg")
val contact = Contact()
PhotoHandler(null).handle(ContentValues().apply {
put(Photo.PHOTO, blob)
diff --git a/src/androidTest/resources/small.jpg b/src/androidTest/resources/small.jpg
new file mode 100644
index 0000000..683de52
--- /dev/null
+++ b/src/androidTest/resources/small.jpg
Binary files differ
diff --git a/src/androidTest/resources/small.png b/src/androidTest/resources/small.png
new file mode 100644
index 0000000..68f895a
--- /dev/null
+++ b/src/androidTest/resources/small.png
Binary files differ
diff --git a/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt b/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt
index de17724..d807f85 100644
--- a/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt
+++ b/src/main/java/at/bitfire/vcard4android/contactrow/PhotoHandler.kt
@@ -7,27 +7,76 @@ package at.bitfire.vcard4android.contactrow
import android.content.ContentProviderClient
import android.content.ContentUris
import android.content.ContentValues
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds.Photo
import at.bitfire.vcard4android.Constants
import at.bitfire.vcard4android.Contact
+import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
+import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.logging.Level
class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() {
+ companion object {
+
+ /**
+ * Converts non-JPEG images to JPEG (compression: 75).
+ * Does nothing if image is already in JPEG format.
+ *
+ * @param photo image in format that can be read by [BitmapFactory]
+ * @param quality JPEG quality (ignored when [photo] is already in JPEG format)
+ * @return same image in JPEG format or null if image couldn't be converted
+ */
+ fun convertToJpeg(photo: ByteArray, quality: Int): ByteArray? {
+ val opts = BitmapFactory.Options()
+ opts.inJustDecodeBounds = true
+ BitmapFactory.decodeByteArray(photo, 0, photo.size, opts)
+ if (opts.outMimeType == "image/jpeg")
+ // image is already a JPEG
+ return photo
+
+ // decode image
+ val bmp = BitmapFactory.decodeByteArray(photo, 0, photo.size)
+ if (bmp == null)
+ return null
+
+ // compress as JPEG
+ val result = ByteArrayOutputStream()
+ if (bmp.compress(Bitmap.CompressFormat.JPEG, quality, result))
+ return result.toByteArray()
+
+ return null
+ }
+
+ }
+
+
override fun forMimeType() = Photo.CONTENT_ITEM_TYPE
override fun handle(values: ContentValues, contact: Contact) {
super.handle(values, contact)
+ /* JPEG qualities are taken from
+ https://android.googlesource.com/platform/packages/providers/ContactsProvider.git/+/refs/heads/android12-release/src/com/android/providers/contacts/PhotoProcessor.java
+
+ Compression for display photos: 75
+ Compression for thumbnails that don't have a full-size photo: 95
+ */
+
values.getAsLong(Photo.PHOTO_FILE_ID)?.let { photoId ->
val photoUri = ContentUris.withAppendedId(ContactsContract.DisplayPhoto.CONTENT_URI, photoId)
try {
provider?.openAssetFile(photoUri, "r")?.let { file ->
file.createInputStream().use {
- contact.photo = IOUtils.toByteArray(it)
+ // Samsung Android 12 bug: they return a PNG image with MIME type image/jpeg
+ convertToJpeg(IOUtils.toByteArray(it), 75)?.let { jpeg ->
+ Constants.log.log(Level.FINE, "Got high-res contact photo (${FileUtils.byteCountToDisplaySize(jpeg.size.toLong())})")
+ contact.photo = jpeg
+ }
}
}
} catch(e: IOException) {
@@ -35,8 +84,15 @@ class PhotoHandler(val provider: ContentProviderClient?): DataRowHandler() {
}
}
- if (contact.photo == null)
- contact.photo = values.getAsByteArray(Photo.PHOTO)
+ if (contact.photo == null) {
+ values.getAsByteArray(Photo.PHOTO)?.let { thumbnail ->
+ // Samsung Android 12 bug: even the thumbnail is a PNG image
+ convertToJpeg(thumbnail, 95)?.let { jpeg ->
+ Constants.log.log(Level.FINE, "Got contact photo thumbnail (${FileUtils.byteCountToDisplaySize(jpeg.size.toLong())})")
+ contact.photo = jpeg
+ }
+ }
+ }
}
} \ No newline at end of file