18af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein/* 28af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * Copyright (C) 2014 The Android Open Source Project 38af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * 48af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 58af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * you may not use this file except in compliance with the License. 68af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * You may obtain a copy of the License at 78af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * 88af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 98af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * 108af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * Unless required by applicable law or agreed to in writing, software 118af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 128af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * See the License for the specific language governing permissions and 148af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * limitations under the License. 158af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein */ 168af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 178af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinpackage com.android.ex.chips; 188af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.content.ContentResolver; 208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.database.Cursor; 218af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.net.Uri; 228af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.os.AsyncTask; 238af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.provider.ContactsContract; 248af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.support.v4.util.LruCache; 258af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport android.util.Log; 268af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 278af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport java.io.ByteArrayOutputStream; 288af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport java.io.IOException; 298af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinimport java.io.InputStream; 308af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 318af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein/** 328af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * Default implementation of {@link com.android.ex.chips.PhotoManager} that 338af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * queries for photo bytes by using the {@link com.android.ex.chips.RecipientEntry}'s 348af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * photoThumbnailUri. 358af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein */ 368af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sappersteinpublic class DefaultPhotoManager implements PhotoManager { 378af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private static final String TAG = "DefaultPhotoManager"; 388af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 398af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private static final boolean DEBUG = false; 408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein /** 428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * For reading photos for directory contacts, this is the chunk size for 438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein * copying from the {@link InputStream} to the output stream. 448af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein */ 458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private static final int BUFFER_SIZE = 1024*16; 468af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 478af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private static class PhotoQuery { 488af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein public static final String[] PROJECTION = { 498af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein ContactsContract.CommonDataKinds.Photo.PHOTO 508af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein }; 518af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 528af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein public static final int PHOTO = 0; 538af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 548af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 558af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private final ContentResolver mContentResolver; 568af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private final LruCache<Uri, byte[]> mPhotoCacheMap; 578af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 588af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein public DefaultPhotoManager(ContentResolver contentResolver) { 598af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein mContentResolver = contentResolver; 608af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE); 618af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 628af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 638af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein @Override 648af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein public void populatePhotoBytesAsync(RecipientEntry entry, PhotoManagerCallback callback) { 658af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein final Uri photoThumbnailUri = entry.getPhotoThumbnailUri(); 668af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (photoThumbnailUri != null) { 678af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri); 688af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (photoBytes != null) { 698af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein entry.setPhotoBytes(photoBytes); 700efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao if (callback != null) { 7150429c51adbd79c5d7dd5beb2c267daf9465f20fAndrew Sapperstein callback.onPhotoBytesPopulated(); 720efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao } 738af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } else { 748af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (DEBUG) { 758af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein Log.d(TAG, "No photo cache for " + entry.getDisplayName() 768af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein + ". Fetch one asynchronously"); 778af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 788af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein fetchPhotoAsync(entry, photoThumbnailUri, callback); 798af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 800efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao } else if (callback != null) { 810efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao callback.onPhotoBytesAsyncLoadFailed(); 828af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 838af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 848af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 858af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein private void fetchPhotoAsync(final RecipientEntry entry, final Uri photoThumbnailUri, 868af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein final PhotoManagerCallback callback) { 878af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein final AsyncTask<Void, Void, byte[]> photoLoadTask = new AsyncTask<Void, Void, byte[]>() { 888af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein @Override 898af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein protected byte[] doInBackground(Void... params) { 908af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // First try running a query. Images for local contacts are 918af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // loaded by sending a query to the ContactsProvider. 928af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein final Cursor photoCursor = mContentResolver.query( 938af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null); 948af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (photoCursor != null) { 958af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein try { 968af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (photoCursor.moveToFirst()) { 978af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein return photoCursor.getBlob(PhotoQuery.PHOTO); 988af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 998af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } finally { 1008af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein photoCursor.close(); 1018af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1028af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } else { 1038af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // If the query fails, try streaming the URI directly. 1048af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // For remote directory images, this URI resolves to the 1058af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // directory provider and the images are loaded by sending 1068af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // an openFile call to the provider. 1078af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein try { 1088af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein InputStream is = mContentResolver.openInputStream( 1098af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein photoThumbnailUri); 1108af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (is != null) { 1118af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein byte[] buffer = new byte[BUFFER_SIZE]; 1128af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1138af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein try { 1148af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein int size; 1158af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein while ((size = is.read(buffer)) != -1) { 1168af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein baos.write(buffer, 0, size); 1178af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1188af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } finally { 1198af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein is.close(); 1208af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1218af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein return baos.toByteArray(); 1228af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1238af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } catch (IOException ex) { 1248af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein // ignore 1258af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1268af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1278af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein return null; 1288af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1298af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein 1308af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein @Override 1318af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein protected void onPostExecute(final byte[] photoBytes) { 1328af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein entry.setPhotoBytes(photoBytes); 1338af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (photoBytes != null) { 1348af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein mPhotoCacheMap.put(photoThumbnailUri, photoBytes); 1358af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein if (callback != null) { 1368af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein callback.onPhotoBytesAsynchronouslyPopulated(); 1378af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1380efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao } else if (callback != null) { 1390efdc53cd0d040c1a27a7d39003916e54e284be2Jin Cao callback.onPhotoBytesAsyncLoadFailed(); 1408af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1418af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1428af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein }; 1438af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein photoLoadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 1448af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein } 1458af0d3b6f34e03c08c8e67be2190da01c59889daAndrew Sapperstein} 146