Home | History | Annotate | Download | only in calendar
      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 com.android.calendar.event.EditEventHelper.AttendeeItem;
     20 
     21 import android.content.Context;
     22 import android.graphics.drawable.Drawable;
     23 import android.net.Uri;
     24 import android.os.Handler;
     25 import android.os.HandlerThread;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.provider.ContactsContract.Contacts;
     29 import android.util.Log;
     30 import android.view.View;
     31 import android.widget.ImageView;
     32 
     33 import java.io.InputStream;
     34 
     35 /**
     36  * Helper class for async access of images.
     37  */
     38 public class ContactsAsyncHelper extends Handler {
     39 
     40     private static final boolean DBG = false;
     41     private static final String LOG_TAG = "ContactsAsyncHelper";
     42 
     43     private static ContactsAsyncHelper mInstance = null;
     44 
     45     /**
     46      * Interface for a WorkerHandler result return.
     47      */
     48     public interface OnImageLoadCompleteListener {
     49         /**
     50          * Called when the image load is complete.
     51          *
     52          * @param imagePresent true if an image was found
     53          */
     54         public void onImageLoadComplete(int token, Object cookie, ImageView iView,
     55                 boolean imagePresent);
     56     }
     57 
     58     // constants
     59     private static final int EVENT_LOAD_IMAGE = 1;
     60     private static final int EVENT_LOAD_DRAWABLE = 2;
     61     private static final int DEFAULT_TOKEN = -1;
     62 
     63     // static objects
     64     private static Handler sThreadHandler;
     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         public AttendeeItem item;
     73         public Runnable callback;
     74     }
     75 
     76     /**
     77      * Thread worker class that handles the task of opening the stream and loading
     78      * the images.
     79      */
     80     private class WorkerHandler extends Handler {
     81         public WorkerHandler(Looper looper) {
     82             super(looper);
     83         }
     84 
     85         @Override
     86         public void handleMessage(Message msg) {
     87             WorkerArgs args = (WorkerArgs) msg.obj;
     88 
     89             switch (msg.arg1) {
     90                 case EVENT_LOAD_DRAWABLE:
     91                 case EVENT_LOAD_IMAGE:
     92                     InputStream inputStream = null;
     93                     try {
     94                         inputStream = Contacts.openContactPhotoInputStream(
     95                                 args.context.getContentResolver(), args.uri);
     96                     } catch (Exception e) {
     97                         Log.e(LOG_TAG, "Error opening photo input stream", e);
     98                     }
     99 
    100                     if (inputStream != null) {
    101                         args.result = Drawable.createFromStream(inputStream, args.uri.toString());
    102 
    103                         if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 +
    104                                 " token: " + msg.what + " image URI: " + args.uri);
    105                     } else {
    106                         args.result = null;
    107                         if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
    108                                 " token: " + msg.what + " image URI: " + args.uri +
    109                                 ", using default image.");
    110                     }
    111                     break;
    112                 default:
    113             }
    114 
    115             // send the reply to the enclosing class.
    116             Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
    117             reply.arg1 = msg.arg1;
    118             reply.obj = msg.obj;
    119             reply.sendToTarget();
    120         }
    121     }
    122 
    123     /**
    124      * Private constructor for static class
    125      */
    126     private ContactsAsyncHelper() {
    127         HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
    128         thread.start();
    129         sThreadHandler = new WorkerHandler(thread.getLooper());
    130     }
    131 
    132     /**
    133      * Start an image load, attach the result to the specified CallerInfo object.
    134      * Note, when the query is started, we make the ImageView INVISIBLE if the
    135      * placeholderImageResource value is -1.  When we're given a valid (!= -1)
    136      * placeholderImageResource value, we make sure the image is visible.
    137      */
    138     public static final void updateImageViewWithContactPhotoAsync(Context context,
    139             ImageView imageView, Uri contact, int placeholderImageResource) {
    140 
    141         // in case the source caller info is null, the URI will be null as well.
    142         // just update using the placeholder image in this case.
    143         if (contact == null) {
    144             if (DBG) Log.d(LOG_TAG, "target image is null, just display placeholder.");
    145             imageView.setVisibility(View.VISIBLE);
    146             imageView.setImageResource(placeholderImageResource);
    147             return;
    148         }
    149 
    150         // Added additional Cookie field in the callee to handle arguments
    151         // sent to the callback function.
    152 
    153         // setup arguments
    154         WorkerArgs args = new WorkerArgs();
    155         args.context = context;
    156         args.view = imageView;
    157         args.uri = contact;
    158         args.defaultResource = placeholderImageResource;
    159 
    160         if (mInstance == null) {
    161             mInstance = new ContactsAsyncHelper();
    162         }
    163         // setup message arguments
    164         Message msg = sThreadHandler.obtainMessage(DEFAULT_TOKEN);
    165         msg.arg1 = EVENT_LOAD_IMAGE;
    166         msg.obj = args;
    167 
    168         if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
    169                 ", displaying default image for now.");
    170 
    171         // set the default image first, when the query is complete, we will
    172         // replace the image with the correct one.
    173         if (placeholderImageResource != -1) {
    174             imageView.setVisibility(View.VISIBLE);
    175             imageView.setImageResource(placeholderImageResource);
    176         } else {
    177             imageView.setVisibility(View.INVISIBLE);
    178         }
    179 
    180         // notify the thread to begin working
    181         sThreadHandler.sendMessage(msg);
    182     }
    183 
    184     /**
    185      * Start an image load, attach the result to the specified CallerInfo object.
    186      * Note, when the query is started, we make the ImageView INVISIBLE if the
    187      * placeholderImageResource value is -1.  When we're given a valid (!= -1)
    188      * placeholderImageResource value, we make sure the image is visible.
    189      */
    190     public static final void retrieveContactPhotoAsync(Context context,
    191             AttendeeItem item, Runnable run, Uri photoUri) {
    192 
    193         // in case the source caller info is null, the URI will be null as well.
    194         // just return as there's nothing to do.
    195         if (photoUri == null) {
    196             return;
    197         }
    198 
    199         // Added additional Cookie field in the callee to handle arguments
    200         // sent to the callback function.
    201 
    202         // setup arguments
    203         WorkerArgs args = new WorkerArgs();
    204         args.context = context;
    205         args.item = item;
    206         args.uri = photoUri;
    207         args.callback = run;
    208 
    209         if (mInstance == null) {
    210             mInstance = new ContactsAsyncHelper();
    211         }
    212         // setup message arguments
    213         Message msg = sThreadHandler.obtainMessage(DEFAULT_TOKEN);
    214         msg.arg1 = EVENT_LOAD_DRAWABLE;
    215         msg.obj = args;
    216 
    217         if (DBG) Log.d(LOG_TAG, "Begin loading drawable: " + args.uri);
    218 
    219 
    220         // notify the thread to begin working
    221         sThreadHandler.sendMessage(msg);
    222     }
    223 
    224     /**
    225      * Called when loading is done.
    226      */
    227     @Override
    228     public void handleMessage(Message msg) {
    229         WorkerArgs args = (WorkerArgs) msg.obj;
    230         switch (msg.arg1) {
    231             case EVENT_LOAD_IMAGE:
    232                 // if the image has been loaded then display it, otherwise set default.
    233                 // in either case, make sure the image is visible.
    234                 if (args.result != null) {
    235                     args.view.setVisibility(View.VISIBLE);
    236                     args.view.setImageDrawable((Drawable) args.result);
    237                 } else if (args.defaultResource != -1) {
    238                     args.view.setVisibility(View.VISIBLE);
    239                     args.view.setImageResource(args.defaultResource);
    240                 }
    241                 break;
    242             case EVENT_LOAD_DRAWABLE:
    243                 if (args.result != null) {
    244                     args.item.mBadge = (Drawable) args.result;
    245                     if (args.callback != null) {
    246                         args.callback.run();
    247                     }
    248                 }
    249                 break;
    250             default:
    251         }
    252     }
    253 }
    254