199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon/*
299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * Copyright (C) 2014 The Android Open Source Project
399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon *
499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * you may not use this file except in compliance with the License.
699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * You may obtain a copy of the License at
799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon *
899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon *
1099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * Unless required by applicable law or agreed to in writing, software
1199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
1299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * See the License for the specific language governing permissions and
1499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * limitations under the License.
1599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon */
1699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
1899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
1999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.app.Notification;
2099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.content.Context;
2199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.graphics.Bitmap;
2299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.graphics.drawable.BitmapDrawable;
2399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.graphics.drawable.Drawable;
2499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.net.Uri;
2599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.os.Handler;
2699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.os.HandlerThread;
2799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.os.Looper;
2899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport android.os.Message;
2999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
3091d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn// TODO: Needed for move to system service: import com.android.internal.R;
3191d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunn
329ccc43d63648800e92396c430228772f8d0738cdHall Liuimport java.io.FileNotFoundException;
3399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport java.io.IOException;
3499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordonimport java.io.InputStream;
3599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
3699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon/**
3799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon * Helper class for loading contacts photo asynchronously.
3899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon */
395b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liupublic class ContactsAsyncHelper {
4099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    private static final String LOG_TAG = ContactsAsyncHelper.class.getSimpleName();
4199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
4299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    /**
4399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * Interface for a WorkerHandler result return.
4499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     */
4599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    public interface OnImageLoadCompleteListener {
4699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        /**
4799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * Called when the image load is complete.
4899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         *
4999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * @param token Integer passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int,
5099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * Context, Uri, OnImageLoadCompleteListener, Object)}.
5199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * @param photo Drawable object obtained by the async load.
5299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * @param photoIcon Bitmap object obtained by the async load.
5399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * @param cookie Object passed in {@link ContactsAsyncHelper#startObtainPhotoAsync(int,
5499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * Context, Uri, OnImageLoadCompleteListener, Object)}. Can be null iff. the original
5599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * cookie is null.
5699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         */
5799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon,
5899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                Object cookie);
5999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    }
6099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
619ccc43d63648800e92396c430228772f8d0738cdHall Liu    /**
629ccc43d63648800e92396c430228772f8d0738cdHall Liu     * Interface to enable stubbing of the call to openInputStream
639ccc43d63648800e92396c430228772f8d0738cdHall Liu     */
649ccc43d63648800e92396c430228772f8d0738cdHall Liu    public interface ContentResolverAdapter {
659ccc43d63648800e92396c430228772f8d0738cdHall Liu        InputStream openInputStream(Context context, Uri uri) throws FileNotFoundException;
669ccc43d63648800e92396c430228772f8d0738cdHall Liu    }
679ccc43d63648800e92396c430228772f8d0738cdHall Liu
6899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    // constants
6999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    private static final int EVENT_LOAD_IMAGE = 1;
7099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
7199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    /** Handler run on a worker thread to load photo asynchronously. */
728d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    private Handler mThreadHandler;
739ccc43d63648800e92396c430228772f8d0738cdHall Liu    private final ContentResolverAdapter mContentResolverAdapter;
7499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
759ccc43d63648800e92396c430228772f8d0738cdHall Liu    public ContactsAsyncHelper(ContentResolverAdapter contentResolverAdapter) {
769ccc43d63648800e92396c430228772f8d0738cdHall Liu        mContentResolverAdapter = contentResolverAdapter;
7799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    }
7899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
7999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    private static final class WorkerArgs {
8099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public Context context;
81a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki        public Uri displayPhotoUri;
8299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public Drawable photo;
8399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public Bitmap photoIcon;
8499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public Object cookie;
8599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public OnImageLoadCompleteListener listener;
8699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    }
8799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
8899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    /**
8999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * Thread worker class that handles the task of opening the stream and loading
9099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * the images.
9199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     */
928d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    private class WorkerHandler extends Handler {
9399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public WorkerHandler(Looper looper) {
9499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            super(looper);
9599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        }
9699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
9799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        @Override
9899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        public void handleMessage(Message msg) {
9999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            WorkerArgs args = (WorkerArgs) msg.obj;
10099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
10199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            switch (msg.arg1) {
10299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                case EVENT_LOAD_IMAGE:
10399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    InputStream inputStream = null;
10499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    try {
10599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        try {
1069ccc43d63648800e92396c430228772f8d0738cdHall Liu                            inputStream = mContentResolverAdapter.openInputStream(
1079ccc43d63648800e92396c430228772f8d0738cdHall Liu                                    args.context, args.displayPhotoUri);
10899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        } catch (Exception e) {
10999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            Log.e(this, e, "Error opening photo input stream");
11099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        }
11199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
11299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        if (inputStream != null) {
11399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            args.photo = Drawable.createFromStream(inputStream,
114a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki                                    args.displayPhotoUri.toString());
11599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
11699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            // This assumes Drawable coming from contact database is usually
11799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            // BitmapDrawable and thus we can have (down)scaled version of it.
11899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            args.photoIcon = getPhotoIconWhenAppropriate(args.context, args.photo);
11999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
12099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            Log.d(this, "Loading image: " + msg.arg1 +
121a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki                                    " token: " + msg.what + " image URI: " + args.displayPhotoUri);
12299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        } else {
12399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            args.photo = null;
12499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            args.photoIcon = null;
12599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            Log.d(this, "Problem with image: " + msg.arg1 +
126a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki                                    " token: " + msg.what + " image URI: " + args.displayPhotoUri +
12799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                                    ", using default image.");
12899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        }
12999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    } finally {
13099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        if (inputStream != null) {
13199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            try {
13299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                                inputStream.close();
13399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            } catch (IOException e) {
13499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                                Log.e(this, e, "Unable to close input stream.");
13599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                            }
13699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                        }
13799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    }
138abcbce4441720c52a443d292d5adc2d95f446494Ihab Awad
139abcbce4441720c52a443d292d5adc2d95f446494Ihab Awad                    // Listener will synchronize as needed
140abcbce4441720c52a443d292d5adc2d95f446494Ihab Awad                    Log.d(this, "Notifying listener: " + args.listener.toString() +
141abcbce4441720c52a443d292d5adc2d95f446494Ihab Awad                            " image: " + args.displayPhotoUri + " completed");
142abcbce4441720c52a443d292d5adc2d95f446494Ihab Awad                    args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon,
1438d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                                args.cookie);
14499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    break;
14599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                default:
1468d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad                    break;
14799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            }
14899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        }
14999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
15099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        /**
15199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * Returns a Bitmap object suitable for {@link Notification}'s large icon. This might
15299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * return null when the given Drawable isn't BitmapDrawable, or if the system fails to
15399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         * create a scaled Bitmap for the Drawable.
15499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon         */
15599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        private Bitmap getPhotoIconWhenAppropriate(Context context, Drawable photo) {
15699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            if (!(photo instanceof BitmapDrawable)) {
15799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                return null;
15899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            }
15999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            int iconSize = context.getResources()
16099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    .getDimensionPixelSize(R.dimen.notification_icon_size);
16199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            Bitmap orgBitmap = ((BitmapDrawable) photo).getBitmap();
16299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            int orgWidth = orgBitmap.getWidth();
16399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            int orgHeight = orgBitmap.getHeight();
16499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            int longerEdge = orgWidth > orgHeight ? orgWidth : orgHeight;
16599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            // We want downscaled one only when the original icon is too big.
16699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            if (longerEdge > iconSize) {
16799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                float ratio = ((float) longerEdge) / iconSize;
16899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                int newWidth = (int) (orgWidth / ratio);
16999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                int newHeight = (int) (orgHeight / ratio);
17099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                // If the longer edge is much longer than the shorter edge, the latter may
17199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                // become 0 which will cause a crash.
17299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                if (newWidth <= 0 || newHeight <= 0) {
17399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    Log.w(this, "Photo icon's width or height become 0.");
17499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                    return null;
17599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                }
17699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
17799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                // It is sure ratio >= 1.0f in any case and thus the newly created Bitmap
17899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                // should be smaller than the original.
17999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                return Bitmap.createScaledBitmap(orgBitmap, newWidth, newHeight, true);
18099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            } else {
18199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                return orgBitmap;
18299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            }
18399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        }
18499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    }
18599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
18699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    /**
18799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * Starts an asynchronous image load. After finishing the load,
18899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)}
18999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * will be called.
19099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     *
19199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * @param token Arbitrary integer which will be returned as the first argument of
19299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable, Bitmap, Object)}
19399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * @param context Context object used to do the time-consuming operation.
194a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki     * @param displayPhotoUri Uri to be used to fetch the photo
19599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * @param listener Callback object which will be used when the asynchronous load is done.
19699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * Can be null, which means only the asynchronous load is done while there's no way to
19799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * obtain the loaded photos.
19899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * @param cookie Arbitrary object the caller wants to remember, which will become the
19999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * fourth argument of {@link OnImageLoadCompleteListener#onImageLoadComplete(int, Drawable,
20099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     * Bitmap, Object)}. Can be null, at which the callback will also has null for the argument.
20199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon     */
2025b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public void startObtainPhotoAsync(int token, Context context, Uri displayPhotoUri,
20399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            OnImageLoadCompleteListener listener, Object cookie) {
2048d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        ensureAsyncHandlerStarted();
20599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
20699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // in case the source caller info is null, the URI will be null as well.
20799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // just update using the placeholder image in this case.
208a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki        if (displayPhotoUri == null) {
20999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            Log.wtf(LOG_TAG, "Uri is missing");
21099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon            return;
21199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        }
21299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
21399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // Added additional Cookie field in the callee to handle arguments
21499c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // sent to the callback function.
21599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
21699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // setup arguments
21799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        WorkerArgs args = new WorkerArgs();
21899c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        args.cookie = cookie;
21999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        args.context = context;
220a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki        args.displayPhotoUri = displayPhotoUri;
22199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        args.listener = listener;
22299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
22399c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // setup message arguments
2248d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        Message msg = mThreadHandler.obtainMessage(token);
22599c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        msg.arg1 = EVENT_LOAD_IMAGE;
22699c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        msg.obj = args;
22799c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
228a1662d0ae89d9fb514a5ac68cbc76660e34dae00Makoto Onuki        Log.d(LOG_TAG, "Begin loading image: " + args.displayPhotoUri +
22999c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon                ", displaying default image for now.");
23099c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon
23199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon        // notify the thread to begin working
2328d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        mThreadHandler.sendMessage(msg);
2338d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    }
2348d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad
2358d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad    private void ensureAsyncHandlerStarted() {
2368d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        if (mThreadHandler == null) {
2378d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            HandlerThread thread = new HandlerThread("ContactsAsyncWorker");
2388d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            thread.start();
2398d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad            mThreadHandler = new WorkerHandler(thread.getLooper());
2408d5d9ddc66b55b6906364ab3c0e244dab4d58f13Ihab Awad        }
24199c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon    }
24299c8a6fc0ae4c50878a748280a0ae2d8dd6b040eSantos Cordon}
243