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