107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor/*
207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Copyright (C) 2012 The Android Open Source Project
307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor *
407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Licensed under the Apache License, Version 2.0 (the "License");
507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * you may not use this file except in compliance with the License.
607ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * You may obtain a copy of the License at
707ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor *
807ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor *      http://www.apache.org/licenses/LICENSE-2.0
907ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor *
1007ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * Unless required by applicable law or agreed to in writing, software
1107ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * distributed under the License is distributed on an "AS IS" BASIS,
1207ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * See the License for the specific language governing permissions and
1407ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor * limitations under the License.
1507ddb5c577a10e5aa3b4442426a055f3b95d5202Tom Taylor */
1651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
1751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorpackage com.android.mms.util;
1851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
1951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.ArrayList;
2051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.HashMap;
2151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.HashSet;
2251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.Set;
2351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.Executor;
2451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.LinkedBlockingQueue;
2551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.ThreadFactory;
2651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.ThreadPoolExecutor;
2751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.TimeUnit;
2851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport java.util.concurrent.atomic.AtomicInteger;
2951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
3051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.content.Context;
3151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.net.Uri;
3251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.os.Handler;
3351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.util.Log;
3451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
3551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor/**
3651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Base class {@link BackgroundLoaderManager} used by {@link MessagingApplication} for loading
3751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * items (images, thumbnails, pdus, etc.) in the background off of the UI thread.
3851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p>
3951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Public methods should only be used from a single thread (typically the UI
4051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * thread). Callbacks will be invoked on the thread where the ThumbnailManager
4151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * was instantiated.
4251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * <p>
4351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Uses a thread-pool ExecutorService instead of AsyncTasks since clients may
4451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * request lots of images around the same time, and AsyncTask may reject tasks
4551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * in that case and has no way of bounding the number of threads used by those
4651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * tasks.
4751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor *
4851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor * Based on BooksImageManager by Virgil King.
4951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor */
5051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorabstract class BackgroundLoaderManager {
5151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    private static final String TAG = "BackgroundLoaderManager";
5251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
5351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    private static final int MAX_THREADS = 2;
5451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
5551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
5651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * URIs for which tasks are currently enqueued. Don't enqueue new tasks for
5751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * these, just add new callbacks.
5851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
5951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    protected final Set<Uri> mPendingTaskUris;
6051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
6151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    protected final HashMap<Uri, Set<ItemLoadedCallback>> mCallbacks;
6251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
6351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    protected final Executor mExecutor;
6451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
6551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    protected final Handler mCallbackHandler;
6651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
6751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    BackgroundLoaderManager(Context context) {
6851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        mPendingTaskUris = new HashSet<Uri>();
6951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        mCallbacks = new HashMap<Uri, Set<ItemLoadedCallback>>();
7051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
7151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        final int poolSize = MAX_THREADS;
7251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        mExecutor = new ThreadPoolExecutor(
7351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                poolSize, poolSize, 5, TimeUnit.SECONDS, queue,
7451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                new BackgroundLoaderThreadFactory(getTag()));
7551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        mCallbackHandler = new Handler();
7651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
7751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
7851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
7951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * Release memory if possible.
8051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
8151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    public void onLowMemory() {
8251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        clear();
8351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
8451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
8551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    public void clear() {
8651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
8751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
8851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
8951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * Return a tag that will be used to name threads so they'll be visible in the debugger.
9051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
9151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    public abstract String getTag();
9251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
9351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
9451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * Attempts to add a callback for a resource.
9551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *
9651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * @param uri the {@link Uri} of the resource for which a callback is
9751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *            desired.
9851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * @param callback the callback to register.
9951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * @return {@code true} if the callback is guaranteed to be invoked with
10051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *         a non-null result (as long as there is no error and the
10151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *         callback is not canceled), or {@code false} if the callback
10251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *         cannot be registered with this task because the result for
10351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *         the desired {@link Uri} has already been discarded due to
10451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     *         low-memory.
10551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * @throws NullPointerException if either argument is {@code null}
10651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
10751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    public boolean addCallback(Uri uri, ItemLoadedCallback callback) {
10851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (Log.isLoggable(TAG, Log.DEBUG)) {
10951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            Log.d(TAG, "Adding image callback " + callback);
11051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
11151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (uri == null) {
11251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            throw new NullPointerException("uri is null");
11351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
11451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (callback == null) {
11551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            throw new NullPointerException("callback is null");
11651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
11751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        Set<ItemLoadedCallback> callbacks = mCallbacks.get(uri);
11851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (callbacks == null) {
11951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            callbacks = new HashSet<ItemLoadedCallback>(4);
12051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            mCallbacks.put(uri, callbacks);
12151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
12251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        callbacks.add(callback);
12351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        return true;
12451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
12551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
12651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    public void cancelCallback(ItemLoadedCallback callback) {
12751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (Log.isLoggable(TAG, Log.DEBUG)) {
12851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            Log.d(TAG, "Cancelling image callback " + callback);
12951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
13051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        for (final Uri uri : mCallbacks.keySet()) {
13151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            final Set<ItemLoadedCallback> callbacks = mCallbacks.get(uri);
13251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            callbacks.remove(callback);
13351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
13451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
13551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
13651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
13751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * Copies the elements of a {@link Set} into an {@link ArrayList}.
13851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
13951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    @SuppressWarnings("unchecked")
14051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    protected static <T> ArrayList<T> asList(Set<T> source) {
14151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        return new ArrayList<T>(source);
14251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
14351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
14451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    /**
14551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     * {@link ThreadFactory} which sets a meaningful name for the thread.
14651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor     */
14751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    private static class BackgroundLoaderThreadFactory implements ThreadFactory {
14851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        private final AtomicInteger mCount = new AtomicInteger(1);
14951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        private final String mTag;
15051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
15151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        public BackgroundLoaderThreadFactory(String tag) {
15251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            mTag = tag;
15351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
15451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
15551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        public Thread newThread(final Runnable r) {
156721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Thread t =  new Thread(r, mTag + "-" + mCount.getAndIncrement());
157721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
158721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (t.getPriority() != Thread.MIN_PRIORITY)
159721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                t.setPriority(Thread.MIN_PRIORITY);
160721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
161721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return t;
16251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
16351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
16451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor}
165