michael@0: /* michael@0: * Copyright (C) 2013 Square, Inc. michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: package com.squareup.picasso; michael@0: michael@0: import android.annotation.TargetApi; michael@0: import android.content.ContentResolver; michael@0: import android.content.Context; michael@0: import android.content.UriMatcher; michael@0: import android.graphics.Bitmap; michael@0: import android.graphics.BitmapFactory; michael@0: import android.net.Uri; michael@0: import android.provider.ContactsContract; michael@0: michael@0: import java.io.IOException; michael@0: import java.io.InputStream; michael@0: michael@0: import static android.os.Build.VERSION.SDK_INT; michael@0: import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; michael@0: import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream; michael@0: import static com.squareup.picasso.Picasso.LoadedFrom.DISK; michael@0: michael@0: class ContactsPhotoBitmapHunter extends BitmapHunter { michael@0: /** A lookup uri (e.g. content://com.android.contacts/contacts/lookup/3570i61d948d30808e537) */ michael@0: private static final int ID_LOOKUP = 1; michael@0: /** A contact thumbnail uri (e.g. content://com.android.contacts/contacts/38/photo) */ michael@0: private static final int ID_THUMBNAIL = 2; michael@0: /** A contact uri (e.g. content://com.android.contacts/contacts/38) */ michael@0: private static final int ID_CONTACT = 3; michael@0: /** michael@0: * A contact display photo (high resolution) uri michael@0: * (e.g. content://com.android.contacts/display_photo/5) michael@0: */ michael@0: private static final int ID_DISPLAY_PHOTO = 4; michael@0: michael@0: private static final UriMatcher matcher; michael@0: michael@0: static { michael@0: matcher = new UriMatcher(UriMatcher.NO_MATCH); michael@0: matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", ID_LOOKUP); michael@0: matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", ID_LOOKUP); michael@0: matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", ID_THUMBNAIL); michael@0: matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", ID_CONTACT); michael@0: matcher.addURI(ContactsContract.AUTHORITY, "display_photo/#", ID_DISPLAY_PHOTO); michael@0: } michael@0: michael@0: final Context context; michael@0: michael@0: ContactsPhotoBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, michael@0: Stats stats, Action action) { michael@0: super(picasso, dispatcher, cache, stats, action); michael@0: this.context = context; michael@0: } michael@0: michael@0: @Override Bitmap decode(Request data) throws IOException { michael@0: InputStream is = null; michael@0: try { michael@0: is = getInputStream(); michael@0: return decodeStream(is, data); michael@0: } finally { michael@0: Utils.closeQuietly(is); michael@0: } michael@0: } michael@0: michael@0: @Override Picasso.LoadedFrom getLoadedFrom() { michael@0: return DISK; michael@0: } michael@0: michael@0: private InputStream getInputStream() throws IOException { michael@0: ContentResolver contentResolver = context.getContentResolver(); michael@0: Uri uri = getData().uri; michael@0: switch (matcher.match(uri)) { michael@0: case ID_LOOKUP: michael@0: uri = ContactsContract.Contacts.lookupContact(contentResolver, uri); michael@0: if (uri == null) { michael@0: return null; michael@0: } michael@0: // Resolved the uri to a contact uri, intentionally fall through to process the resolved uri michael@0: case ID_CONTACT: michael@0: if (SDK_INT < ICE_CREAM_SANDWICH) { michael@0: return openContactPhotoInputStream(contentResolver, uri); michael@0: } else { michael@0: return ContactPhotoStreamIcs.get(contentResolver, uri); michael@0: } michael@0: case ID_THUMBNAIL: michael@0: case ID_DISPLAY_PHOTO: michael@0: return contentResolver.openInputStream(uri); michael@0: default: michael@0: throw new IllegalStateException("Invalid uri: " + uri); michael@0: } michael@0: } michael@0: michael@0: private Bitmap decodeStream(InputStream stream, Request data) throws IOException { michael@0: if (stream == null) { michael@0: return null; michael@0: } michael@0: BitmapFactory.Options options = null; michael@0: if (data.hasSize()) { michael@0: options = new BitmapFactory.Options(); michael@0: options.inJustDecodeBounds = true; michael@0: InputStream is = getInputStream(); michael@0: try { michael@0: BitmapFactory.decodeStream(is, null, options); michael@0: } finally { michael@0: Utils.closeQuietly(is); michael@0: } michael@0: calculateInSampleSize(data.targetWidth, data.targetHeight, options); michael@0: } michael@0: return BitmapFactory.decodeStream(stream, null, options); michael@0: } michael@0: michael@0: @TargetApi(ICE_CREAM_SANDWICH) michael@0: private static class ContactPhotoStreamIcs { michael@0: static InputStream get(ContentResolver contentResolver, Uri uri) { michael@0: return openContactPhotoInputStream(contentResolver, uri, true); michael@0: } michael@0: } michael@0: }