1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.calendar; 18 19 import android.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.net.Uri; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.os.Looper; 25 import android.os.Message; 26 import android.provider.ContactsContract.Contacts; 27 import android.util.Log; 28 import android.view.View; 29 import android.widget.ImageView; 30 31 import java.io.InputStream; 32 33 /** 34 * Helper class for async access of images. 35 */ 36 public class ContactsAsyncHelper extends Handler { 37 38 private static final boolean DBG = false; 39 private static final String LOG_TAG = "ContactsAsyncHelper"; 40 41 /** 42 * Interface for a WorkerHandler result return. 43 */ 44 public interface OnImageLoadCompleteListener { 45 /** 46 * Called when the image load is complete. 47 * 48 * @param imagePresent true if an image was found 49 */ 50 public void onImageLoadComplete(int token, Object cookie, ImageView iView, 51 boolean imagePresent); 52 } 53 54 // constants 55 private static final int EVENT_LOAD_IMAGE = 1; 56 private static final int DEFAULT_TOKEN = -1; 57 58 // static objects 59 private static Handler sThreadHandler; 60 private static ContactsAsyncHelper sInstance; 61 62 static { 63 sInstance = new ContactsAsyncHelper(); 64 } 65 66 private static final class WorkerArgs { 67 public Context context; 68 public ImageView view; 69 public Uri uri; 70 public int defaultResource; 71 public Object result; 72 } 73 74 /** 75 * Thread worker class that handles the task of opening the stream and loading 76 * the images. 77 */ 78 private class WorkerHandler extends Handler { 79 public WorkerHandler(Looper looper) { 80 super(looper); 81 } 82 83 @Override 84 public void handleMessage(Message msg) { 85 WorkerArgs args = (WorkerArgs) msg.obj; 86 87 switch (msg.arg1) { 88 case EVENT_LOAD_IMAGE: 89 InputStream inputStream = null; 90 try { 91 inputStream = Contacts.openContactPhotoInputStream( 92 args.context.getContentResolver(), args.uri); 93 } catch (Exception e) { 94 Log.e(LOG_TAG, "Error opening photo input stream", e); 95 } 96 97 if (inputStream != null) { 98 args.result = Drawable.createFromStream(inputStream, args.uri.toString()); 99 100 if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 + 101 " token: " + msg.what + " image URI: " + args.uri); 102 } else { 103 args.result = null; 104 if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 + 105 " token: " + msg.what + " image URI: " + args.uri + 106 ", using default image."); 107 } 108 break; 109 default: 110 } 111 112 // send the reply to the enclosing class. 113 Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what); 114 reply.arg1 = msg.arg1; 115 reply.obj = msg.obj; 116 reply.sendToTarget(); 117 } 118 } 119 120 /** 121 * Private constructor for static class 122 */ 123 private ContactsAsyncHelper() { 124 HandlerThread thread = new HandlerThread("ContactsAsyncWorker"); 125 thread.start(); 126 sThreadHandler = new WorkerHandler(thread.getLooper()); 127 } 128 129 /** 130 * Start an image load, attach the result to the specified CallerInfo object. 131 * Note, when the query is started, we make the ImageView INVISIBLE if the 132 * placeholderImageResource value is -1. When we're given a valid (!= -1) 133 * placeholderImageResource value, we make sure the image is visible. 134 */ 135 public static final void updateImageViewWithContactPhotoAsync(Context context, 136 ImageView imageView, Uri person, int placeholderImageResource) { 137 138 // in case the source caller info is null, the URI will be null as well. 139 // just update using the placeholder image in this case. 140 if (person == null) { 141 if (DBG) Log.d(LOG_TAG, "target image is null, just display placeholder."); 142 imageView.setVisibility(View.VISIBLE); 143 imageView.setImageResource(placeholderImageResource); 144 return; 145 } 146 147 // Added additional Cookie field in the callee to handle arguments 148 // sent to the callback function. 149 150 // setup arguments 151 WorkerArgs args = new WorkerArgs(); 152 args.context = context; 153 args.view = imageView; 154 args.uri = person; 155 args.defaultResource = placeholderImageResource; 156 157 // setup message arguments 158 Message msg = sThreadHandler.obtainMessage(DEFAULT_TOKEN); 159 msg.arg1 = EVENT_LOAD_IMAGE; 160 msg.obj = args; 161 162 if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri + 163 ", displaying default image for now."); 164 165 // set the default image first, when the query is complete, we will 166 // replace the image with the correct one. 167 if (placeholderImageResource != -1) { 168 imageView.setVisibility(View.VISIBLE); 169 imageView.setImageResource(placeholderImageResource); 170 } else { 171 imageView.setVisibility(View.INVISIBLE); 172 } 173 174 // notify the thread to begin working 175 sThreadHandler.sendMessage(msg); 176 } 177 178 /** 179 * Called when loading is done. 180 */ 181 @Override 182 public void handleMessage(Message msg) { 183 WorkerArgs args = (WorkerArgs) msg.obj; 184 switch (msg.arg1) { 185 case EVENT_LOAD_IMAGE: 186 boolean imagePresent = false; 187 188 // if the image has been loaded then display it, otherwise set default. 189 // in either case, make sure the image is visible. 190 if (args.result != null) { 191 args.view.setVisibility(View.VISIBLE); 192 args.view.setImageDrawable((Drawable) args.result); 193 imagePresent = true; 194 } else if (args.defaultResource != -1) { 195 args.view.setVisibility(View.VISIBLE); 196 args.view.setImageResource(args.defaultResource); 197 } 198 break; 199 default: 200 } 201 } 202 } 203