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

PhotoBuilder.kt « datarow « vcard4android « bitfire « at « java « main « src - github.com/bitfireAT/vcard4android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 3925ec97ac156b80eff006f930efc08b6b3a047b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package at.bitfire.vcard4android.datarow

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.ThumbnailUtils
import android.net.Uri
import android.provider.ContactsContract.CommonDataKinds.Photo
import at.bitfire.vcard4android.BatchOperation
import at.bitfire.vcard4android.Constants
import at.bitfire.vcard4android.Contact
import java.io.ByteArrayOutputStream
import java.util.*

class PhotoBuilder(mimeType: String, dataRowUri: Uri, rawContactId: Long?, contact: Contact)
    : DataRowBuilder(mimeType, dataRowUri, rawContactId, contact) {

    companion object {
        val MAX_PHOTO_BLOB_SIZE = 950*1024    // IPC limit 1 MB, minus 50 kB for the protocol itself = 950 kB
        val MAX_RESIZE_PASSES = 10
    }

    override fun build(): List<BatchOperation.CpoBuilder> {
        // The following approach would be correct, but it doesn't work:
        // the ContactsProvider handler will process the image in background and update
        // the raw contact with the new photo ID when it's finished, setting it to dirty again!
        // See https://code.google.com/p/android/issues/detail?id=226875

        /*Uri photoUri = addressBook.syncAdapterURI(Uri.withAppendedPath(
                ContentUris.withAppendedId(RawContacts.CONTENT_URI, id),
                RawContacts.DisplayPhoto.CONTENT_DIRECTORY));
        Constants.log.debug("Setting local photo " + photoUri);
        try {
            @Cleanup AssetFileDescriptor fd = addressBook.provider.openAssetFile(photoUri, "w");
            @Cleanup OutputStream stream = fd.createOutputStream();
            if (stream != null)
                stream.write(photo);
            else
                Constants.log.warn("Couldn't create local contact photo file");
        } catch (IOException|RemoteException e) {
            Constants.log.warn("Couldn't write local contact photo file", e);
        }*/

        val result = LinkedList<BatchOperation.CpoBuilder>()
        contact.photo?.let { photo ->
            val resized = resizeIfNecessary(photo)
            if (resized != null)
                result += newDataRow().withValue(Photo.PHOTO, resized)
        }
        return result
    }

    private fun resizeIfNecessary(blob: ByteArray): ByteArray? {
        if (blob.size > MAX_PHOTO_BLOB_SIZE) {
            Constants.log.fine("Photo larger than $MAX_PHOTO_BLOB_SIZE bytes, resizing")

            val bitmap = BitmapFactory.decodeByteArray(blob, 0, blob.size)
            if (bitmap == null) {
                Constants.log.warning("Image decoding failed")
                return null
            }

            var size = Math.min(bitmap.width, bitmap.height).toFloat()
            var resized: ByteArray = blob
            var count = 0
            var quality = 98
            do {
                if (++count > MAX_RESIZE_PASSES) {
                    Constants.log.warning("Couldn't resize photo within $MAX_RESIZE_PASSES passes")
                    return null
                }

                val sizeInt = size.toInt()
                val thumb = ThumbnailUtils.extractThumbnail(bitmap, sizeInt, sizeInt)
                val baos = ByteArrayOutputStream()
                if (thumb.compress(Bitmap.CompressFormat.JPEG, quality, baos))
                    resized = baos.toByteArray()

                size *= .9f
                quality--
            } while (resized.size >= MAX_PHOTO_BLOB_SIZE)

            return resized

        } else
            return blob
    }


    object Factory: DataRowBuilder.Factory<PhotoBuilder> {
        override fun mimeType() = Photo.CONTENT_ITEM_TYPE
        override fun newInstance(dataRowUri: Uri, rawContactId: Long?, contact: Contact) =
            PhotoBuilder(mimeType(), dataRowUri, rawContactId, contact)
    }

}