138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa/* 238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Copyright (C) 2008 The Android Open Source Project 338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * 438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * you may not use this file except in compliance with the License. 638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * You may obtain a copy of the License at 738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * 838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * 1038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 1138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 1238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * See the License for the specific language governing permissions and 1438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * limitations under the License. 1538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 1638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 1738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawapackage com.android.phone; 1838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 1925428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawaimport android.app.Notification; 2038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.content.ContentUris; 2138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.content.Context; 2225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawaimport android.graphics.Bitmap; 2325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawaimport android.graphics.drawable.BitmapDrawable; 2438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.graphics.drawable.Drawable; 2538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.net.Uri; 2638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.os.Handler; 2738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.os.HandlerThread; 2838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.os.Looper; 2938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.os.Message; 3038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.provider.ContactsContract.Contacts; 3138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport android.util.Log; 3238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 3325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawaimport com.android.internal.telephony.CallerInfo; 3425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawaimport com.android.internal.telephony.Connection; 3525428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa 3638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawaimport java.io.InputStream; 3738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 3838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa/** 39dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Helper class for loading contacts photo asynchronously. 4038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 41dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawapublic class ContactsAsyncHelper { 4238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 4338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static final boolean DBG = false; 4438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static final String LOG_TAG = "ContactsAsyncHelper"; 4538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 4638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 4738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Interface for a WorkerHandler result return. 4838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 4938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public interface OnImageLoadCompleteListener { 5038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 5138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Called when the image load is complete. 5238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * 53dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param token Integer passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int, 54dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Context, Uri, OnImageLoadCompleteListener, Object)}. 55dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param photo Drawable object obtained by the async load. 56dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param photoIcon Bitmap object obtained by the async load. 57dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param cookie Object passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int, 58dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Context, Uri, OnImageLoadCompleteListener, Object)}. Can be null iff. the original 59dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * cookie is null. 6038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 61dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, 62dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa Object cookie); 6338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 6438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 6538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // constants 6638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static final int EVENT_LOAD_IMAGE = 1; 6738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 68dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa private final Handler mResultHandler = new Handler() { 69dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa /** Called when loading is done. */ 70dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa @Override 71dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa public void handleMessage(Message msg) { 72dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa WorkerArgs args = (WorkerArgs) msg.obj; 73dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa switch (msg.arg1) { 74dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa case EVENT_LOAD_IMAGE: 75dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa if (args.listener != null) { 76dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa if (DBG) { 77dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() + 78dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa " image: " + args.uri + " completed"); 79dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa } 80dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon, 81dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa args.cookie); 82dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa } 83dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa break; 84dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa default: 85dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa } 86dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa } 87dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa }; 88dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa 89dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa /** Handler run on a worker thread to load photo asynchronously. */ 9038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static Handler sThreadHandler; 9138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 9238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** For forcing the system to call its constructor */ 9338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa @SuppressWarnings("unused") 9438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static ContactsAsyncHelper sInstance; 9538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 9638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa static { 9738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa sInstance = new ContactsAsyncHelper(); 9838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 9938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 10038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private static final class WorkerArgs { 10138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public Context context; 10238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public Uri uri; 10325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa public Drawable photo; 10425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa public Bitmap photoIcon; 10538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public Object cookie; 10638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public OnImageLoadCompleteListener listener; 10738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 10838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 10938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 11038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * public inner class to help out the ContactsAsyncHelper callers 11138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * with tracking the state of the CallerInfo Queries and image 11238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * loading. 11338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * 11438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Logic contained herein is used to remove the race conditions 11538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * that exist as the CallerInfo queries run and mix with the image 11638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * loads, which then mix with the Phone state changes. 11738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 11838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public static class ImageTracker { 11938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 12038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // Image display states 12138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public static final int DISPLAY_UNDEFINED = 0; 12238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public static final int DISPLAY_IMAGE = -1; 12338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public static final int DISPLAY_DEFAULT = -2; 12438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 12538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // State of the image on the imageview. 12638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private CallerInfo mCurrentCallerInfo; 12738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private int displayMode; 12838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 12938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public ImageTracker() { 13038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa mCurrentCallerInfo = null; 13138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa displayMode = DISPLAY_UNDEFINED; 13238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 13338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 13438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 13538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Used to see if the requested call / connection has a 13638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * different caller attached to it than the one we currently 13738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * have in the CallCard. 13838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 13938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public boolean isDifferentImageRequest(CallerInfo ci) { 14038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // note, since the connections are around for the lifetime of the 14138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // call, and the CallerInfo-related items as well, we can 14238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // definitely use a simple != comparison. 14338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return (mCurrentCallerInfo != ci); 14438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 14538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 14638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public boolean isDifferentImageRequest(Connection connection) { 14738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // if the connection does not exist, see if the 14838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // mCurrentCallerInfo is also null to match. 14938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (connection == null) { 15038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null"); 15138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return (mCurrentCallerInfo != null); 15238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 15338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa Object o = connection.getUserData(); 15438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 15538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // if the call does NOT have a callerInfo attached 15638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // then it is ok to query. 15738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa boolean runQuery = true; 15838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (o instanceof CallerInfo) { 15938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa runQuery = isDifferentImageRequest((CallerInfo) o); 16038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 16138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return runQuery; 16238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 16338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 16438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 16538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Simple setter for the CallerInfo object. 16638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 16738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public void setPhotoRequest(CallerInfo ci) { 16838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa mCurrentCallerInfo = ci; 16938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 17038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 17138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 17238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Convenience method used to retrieve the URI 17338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * representing the Photo file recorded in the attached 17438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * CallerInfo Object. 17538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 17638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public Uri getPhotoUri() { 17738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (mCurrentCallerInfo != null) { 17838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return ContentUris.withAppendedId(Contacts.CONTENT_URI, 17938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa mCurrentCallerInfo.person_id); 18038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 18138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return null; 18238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 18338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 18438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 18538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Simple setter for the Photo state. 18638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 18738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public void setPhotoState(int state) { 18838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa displayMode = state; 18938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 19038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 19138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 19238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Simple getter for the Photo state. 19338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 19438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public int getPhotoState() { 19538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return displayMode; 19638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 19738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 19838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 19938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 20038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Thread worker class that handles the task of opening the stream and loading 20138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * the images. 20238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 20338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private class WorkerHandler extends Handler { 20438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public WorkerHandler(Looper looper) { 20538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa super(looper); 20638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 20738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 20838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa @Override 20938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa public void handleMessage(Message msg) { 21038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa WorkerArgs args = (WorkerArgs) msg.obj; 21138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 21238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa switch (msg.arg1) { 21338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa case EVENT_LOAD_IMAGE: 21438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa InputStream inputStream = null; 21538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa try { 21638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa inputStream = Contacts.openContactPhotoInputStream( 21738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa args.context.getContentResolver(), args.uri, true); 21838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } catch (Exception e) { 21938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa Log.e(LOG_TAG, "Error opening photo input stream", e); 22038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 22138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 22238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (inputStream != null) { 22325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa args.photo = Drawable.createFromStream(inputStream, args.uri.toString()); 22425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa 22525428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // This assumes Drawable coming from contact database is usually 22625428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // BitmapDrawable and thus we can have (down)scaled version of it. 22725428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa args.photoIcon = getPhotoIconWhenAppropriate(args.context, args.photo); 22838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 22938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 + 23038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa " token: " + msg.what + " image URI: " + args.uri); 23138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } else { 23225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa args.photo = null; 23325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa args.photoIcon = null; 23438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 + 23538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa " token: " + msg.what + " image URI: " + args.uri + 23638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa ", using default image."); 23738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 23838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa break; 23938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa default: 24038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 24138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 24238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // send the reply to the enclosing class. 243dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa Message reply = ContactsAsyncHelper.this.mResultHandler.obtainMessage(msg.what); 24438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa reply.arg1 = msg.arg1; 24538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa reply.obj = msg.obj; 24638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa reply.sendToTarget(); 24738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 24825428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa 24925428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa /** 25025428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa * Returns a Bitmap object suitable for {@link Notification}'s large icon. This might 25125428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa * return null when the given Drawable isn't BitmapDrawable, or if the system fails to 25225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa * create a scaled Bitmap for the Drawable. 25325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa */ 25425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa private Bitmap getPhotoIconWhenAppropriate(Context context, Drawable photo) { 25525428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa if (!(photo instanceof BitmapDrawable)) { 25625428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa return null; 25725428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa } 25825428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int iconSize = context.getResources() 25925428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa .getDimensionPixelSize(R.dimen.notification_icon_size); 26025428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa Bitmap orgBitmap = ((BitmapDrawable) photo).getBitmap(); 26125428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int orgWidth = orgBitmap.getWidth(); 26225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int orgHeight = orgBitmap.getHeight(); 26325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int longerEdge = orgWidth > orgHeight ? orgWidth : orgHeight; 26425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // We want downscaled one only when the original icon is too big. 26525428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa if (longerEdge > iconSize) { 26625428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa float ratio = ((float) longerEdge) / iconSize; 26725428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int newWidth = (int) (orgWidth / ratio); 26825428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa int newHeight = (int) (orgHeight / ratio); 26925428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // If the longer edge is much longer than the shorter edge, the latter may 27025428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // become 0 which will cause a crash. 27125428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa if (newWidth <= 0 || newHeight <= 0) { 27225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa Log.w(LOG_TAG, "Photo icon's width or height become 0."); 27325428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa return null; 27425428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa } 27525428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa 27625428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // It is sure ratio >= 1.0f in any case and thus the newly created Bitmap 27725428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa // should be smaller than the original. 278a990977fd5f2474167ec90c1f2b78f47362bce25Daisuke Miyakawa return Bitmap.createScaledBitmap(orgBitmap, newWidth, newHeight, true); 27925428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa } else { 28025428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa return orgBitmap; 28125428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa } 28225428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa } 28338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 28438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 28538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 28638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa * Private constructor for static class 28738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 28838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa private ContactsAsyncHelper() { 28938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa HandlerThread thread = new HandlerThread("ContactsAsyncWorker"); 29038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa thread.start(); 29138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa sThreadHandler = new WorkerHandler(thread.getLooper()); 29238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 29338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 29438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa /** 295dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Starts an asynchronous image load. After finishing the load, 296dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)} 297dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * will be called. 298bda8f0cf8bb1f347d7e48dd03ede5c6413051891Daisuke Miyakawa * 299bda8f0cf8bb1f347d7e48dd03ede5c6413051891Daisuke Miyakawa * @param token Arbitrary integer which will be returned as the first argument of 300dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)} 301bda8f0cf8bb1f347d7e48dd03ede5c6413051891Daisuke Miyakawa * @param context Context object used to do the time-consuming operation. 302dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param personUri Uri to be used to fetch the photo 303dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param listener Callback object which will be used when the asynchronous load is done. 304dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Can be null, which means only the asynchronous load is done while there's no way to 305dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * obtain the loaded photos. 306dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * @param cookie Arbitrary object the caller wants to remember, which will become the 307dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * fourth argument of {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, 308dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa * Bitmap, Object)}. Can be null, at which the callback will also has null for the argument. 30938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa */ 310dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa public static final void startObtainPhotoAsync(int token, Context context, Uri personUri, 311dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa OnImageLoadCompleteListener listener, Object cookie) { 31238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // in case the source caller info is null, the URI will be null as well. 31338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // just update using the placeholder image in this case. 314dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa if (personUri == null) { 31538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa Log.wtf(LOG_TAG, "Uri is missing"); 31638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa return; 31738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 31838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 31938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // Added additional Cookie field in the callee to handle arguments 32038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // sent to the callback function. 32138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 32238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // setup arguments 32338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa WorkerArgs args = new WorkerArgs(); 32438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa args.cookie = cookie; 32538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa args.context = context; 326dba9a8971738f350a1c7af9dde1fd3727a818066Daisuke Miyakawa args.uri = personUri; 32738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa args.listener = listener; 32838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 32938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // setup message arguments 33038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa Message msg = sThreadHandler.obtainMessage(token); 33138667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa msg.arg1 = EVENT_LOAD_IMAGE; 33238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa msg.obj = args; 33338667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 33438667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri + 33538667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa ", displaying default image for now."); 33638667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 33738667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa // notify the thread to begin working 33838667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa sThreadHandler.sendMessage(msg); 33938667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa } 34038667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa 34125428a6eb620a5cd4bc320352797df276cafb412Daisuke Miyakawa 34238667a875d1ac88e41ad1863b7e6f23bf79d88ddDaisuke Miyakawa} 343