Android Open Source - CCSocialNetwork Image Downloader






From Project

Back to project page CCSocialNetwork.

License

The source code is released under:

GNU Lesser General Public License

If you think the Android project CCSocialNetwork listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2010-present Facebook.//from  w w w  .  jav a  2  s  . com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import com.facebook.FacebookException;
import com.facebook.internal.Utility;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

class ImageDownloader {
    private static final int DOWNLOAD_QUEUE_MAX_CONCURRENT = WorkQueue.DEFAULT_MAX_CONCURRENT;
    private static final int CACHE_READ_QUEUE_MAX_CONCURRENT = 2;
    private static final Handler handler = new Handler();
    private static WorkQueue downloadQueue = new WorkQueue(DOWNLOAD_QUEUE_MAX_CONCURRENT);
    private static WorkQueue cacheReadQueue = new WorkQueue(CACHE_READ_QUEUE_MAX_CONCURRENT);

    private static final Map<RequestKey, DownloaderContext> pendingRequests = new HashMap<RequestKey, DownloaderContext>();

    /**
     * Downloads the image specified in the passed in request.
     * If a callback is specified, it is guaranteed to be invoked on the calling thread.
     * @param request Request to process
     */
    static void downloadAsync(ImageRequest request) {
        if (request == null) {
            return;
        }

        // NOTE: This is the ONLY place where the original request's Url is read. From here on,
        // we will keep track of the Url separately. This is because we might be dealing with a
        // redirect response and the Url might change. We can't create our own new ImageRequests
        // for these changed Urls since the caller might be doing some book-keeping with the request's
        // object reference. So we keep the old references and just map them to new urls in the downloader
        RequestKey key = new RequestKey(request.getImageUrl(), request.getCallerTag());
        synchronized (pendingRequests) {
            DownloaderContext downloaderContext = pendingRequests.get(key);
            if (downloaderContext != null) {
                downloaderContext.request = request;
                downloaderContext.isCancelled = false;
                downloaderContext.workItem.moveToFront();
            } else {
                enqueueCacheRead(request, key, request.isCachedRedirectAllowed());
            }
        }
    }

    static boolean cancelRequest(ImageRequest request) {
        boolean cancelled = false;
        RequestKey key = new RequestKey(request.getImageUrl(), request.getCallerTag());
        synchronized (pendingRequests) {
            DownloaderContext downloaderContext = pendingRequests.get(key);
            if (downloaderContext != null) {
                // If we were able to find the request in our list of pending requests, then we will
                // definitely be able to prevent an ImageResponse from being issued. This is regardless
                // of whether a cache-read or network-download is underway for this request.
                cancelled = true;

                if (downloaderContext.workItem.cancel()) {
                    pendingRequests.remove(key);
                } else {
                    // May be attempting a cache-read right now. So keep track of the cancellation
                    // to prevent network calls etc
                    downloaderContext.isCancelled = true;
                }
            }
        }

        return cancelled;
    }

    static void prioritizeRequest(ImageRequest request) {
        RequestKey key = new RequestKey(request.getImageUrl(), request.getCallerTag());
        synchronized (pendingRequests) {
            DownloaderContext downloaderContext = pendingRequests.get(key);
            if (downloaderContext != null) {
                downloaderContext.workItem.moveToFront();
            }
        }
    }

    private static void enqueueCacheRead(ImageRequest request, RequestKey key, boolean allowCachedRedirects) {
        enqueueRequest(
                request,
                key,
                cacheReadQueue,
                new CacheReadWorkItem(request.getContext(), key, allowCachedRedirects));
    }

    private static void enqueueDownload(ImageRequest request, RequestKey key) {
        enqueueRequest(
                request,
                key,
                downloadQueue,
                new DownloadImageWorkItem(request.getContext(), key));
    }

    private static void enqueueRequest(
            ImageRequest request,
            RequestKey key,
            WorkQueue workQueue,
            Runnable workItem) {
        synchronized (pendingRequests) {
            DownloaderContext downloaderContext = new DownloaderContext();
            downloaderContext.request = request;
            pendingRequests.put(key, downloaderContext);

            // The creation of the WorkItem should be done after the pending request has been registered.
            // This is necessary since the WorkItem might kick off right away and attempt to retrieve
            // the request's DownloaderContext prior to it being ready for access.
            //
            // It is also necessary to hold on to the lock until after the workItem is created, since
            // calls to cancelRequest or prioritizeRequest might come in and expect a registered
            // request to have a workItem available as well.
            downloaderContext.workItem = workQueue.addActiveWorkItem(workItem);
        }
    }

    private static void issueResponse(
            RequestKey key,
            final Exception error,
            final Bitmap bitmap,
            final boolean isCachedRedirect) {
        // Once the old downloader context is removed, we are thread-safe since this is the
        // only reference to it
        DownloaderContext completedRequestContext = removePendingRequest(key);
        if (completedRequestContext != null && !completedRequestContext.isCancelled) {
            final ImageRequest request = completedRequestContext.request;
            final ImageRequest.Callback callback = request.getCallback();
            if (callback != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        ImageResponse response = new ImageResponse(
                                request,
                                error,
                                isCachedRedirect,
                                bitmap);
                        callback.onCompleted(response);
                    }
                });
            }
        }
    }

    private static void readFromCache(RequestKey key, Context context, boolean allowCachedRedirects) {
        InputStream cachedStream = null;
        boolean isCachedRedirect = false;
        if (allowCachedRedirects) {
            URL redirectUrl = UrlRedirectCache.getRedirectedUrl(context, key.url);
            if (redirectUrl != null) {
                cachedStream = ImageResponseCache.getCachedImageStream(redirectUrl, context);
                isCachedRedirect = cachedStream != null;
            }
        }

        if (!isCachedRedirect) {
            cachedStream = ImageResponseCache.getCachedImageStream(key.url, context);
        }

        if (cachedStream != null) {
            // We were able to find a cached image.
            Bitmap bitmap = BitmapFactory.decodeStream(cachedStream);
            Utility.closeQuietly(cachedStream);
            issueResponse(key, null, bitmap, isCachedRedirect);
        } else {
            // Once the old downloader context is removed, we are thread-safe since this is the
            // only reference to it
            DownloaderContext downloaderContext = removePendingRequest(key);
            if (downloaderContext != null && !downloaderContext.isCancelled) {
                enqueueDownload(downloaderContext.request, key);
            }
        }
    }

    private static void download(RequestKey key, Context context) {
        HttpURLConnection connection = null;
        InputStream stream = null;
        Exception error = null;
        Bitmap bitmap = null;
        boolean issueResponse = true;

        try {
            connection = (HttpURLConnection) key.url.openConnection();
            connection.setInstanceFollowRedirects(false);

            switch (connection.getResponseCode()) {
                case HttpURLConnection.HTTP_MOVED_PERM:
                case HttpURLConnection.HTTP_MOVED_TEMP:
                    // redirect. So we need to perform further requests
                    issueResponse = false;

                    String redirectLocation = connection.getHeaderField("location");
                    if (!Utility.isNullOrEmpty(redirectLocation)) {
                        URL redirectUrl = new URL(redirectLocation);
                        UrlRedirectCache.cacheUrlRedirect(context, key.url, redirectUrl);

                        // Once the old downloader context is removed, we are thread-safe since this is the
                        // only reference to it
                        DownloaderContext downloaderContext = removePendingRequest(key);
                        if (downloaderContext != null && !downloaderContext.isCancelled) {
                            enqueueCacheRead(
                                    downloaderContext.request,
                                    new RequestKey(redirectUrl, key.tag),
                                    false);
                        }
                    }
                    break;

                case HttpURLConnection.HTTP_OK:
                    // image should be available
                    stream = ImageResponseCache.interceptAndCacheImageStream(context, connection);
                    bitmap = BitmapFactory.decodeStream(stream);
                    break;

                default:
                    stream = connection.getErrorStream();
                    InputStreamReader reader = new InputStreamReader(stream);
                    char[] buffer = new char[128];
                    int bufferLength;
                    StringBuilder errorMessageBuilder = new StringBuilder();
                    while ((bufferLength = reader.read(buffer, 0, buffer.length)) > 0) {
                        errorMessageBuilder.append(buffer, 0, bufferLength);
                    }
                    Utility.closeQuietly(reader);

                    error = new FacebookException(errorMessageBuilder.toString());
                    break;
            }
        } catch (IOException e) {
            error = e;
        } finally {
            Utility.closeQuietly(stream);
            Utility.disconnectQuietly(connection);
        }

        if (issueResponse) {
            issueResponse(key, error, bitmap, false);
        }
    }

    private static DownloaderContext removePendingRequest(RequestKey key) {
        synchronized (pendingRequests) {
            return pendingRequests.remove(key);
        }
    }

    private static class RequestKey {
        private static final int HASH_SEED = 29; // Some random prime number
        private static final int HASH_MULTIPLIER = 37; // Some random prime number

        URL url;
        Object tag;

        RequestKey(URL url, Object tag) {
            this.url = url;
            this.tag = tag;
        }

        @Override
        public int hashCode() {
            int result = HASH_SEED;

            result = (result * HASH_MULTIPLIER) + url.hashCode();
            result = (result * HASH_MULTIPLIER) + tag.hashCode();

            return result;
        }

        @Override
        public boolean equals(Object o) {
            boolean isEqual = false;

            if (o != null && o instanceof RequestKey) {
                RequestKey compareTo = (RequestKey)o;
                isEqual = compareTo.url == url && compareTo.tag == tag;
            }

            return isEqual;
        }
    }

    private static class DownloaderContext {
        WorkQueue.WorkItem workItem;
        ImageRequest request;
        boolean isCancelled;
    }

    private static class CacheReadWorkItem implements Runnable {
        private Context context;
        private RequestKey key;
        private boolean allowCachedRedirects;

        CacheReadWorkItem(Context context, RequestKey key, boolean allowCachedRedirects) {
            this.context = context;
            this.key = key;
            this.allowCachedRedirects = allowCachedRedirects;
        }

        @Override
        public void run() {
            readFromCache(key, context, allowCachedRedirects);
        }
    }

    private static class DownloadImageWorkItem implements Runnable {
        private Context context;
        private RequestKey key;

        DownloadImageWorkItem(Context context, RequestKey key) {
            this.context = context;
            this.key = key;
        }

        @Override
        public void run() {
            download(key, context);
        }

    }
}




Java Source Code List

com.facebook.AccessTokenSource.java
com.facebook.AccessToken.java
com.facebook.AuthorizationClient.java
com.facebook.FacebookAuthorizationException.java
com.facebook.FacebookDialogException.java
com.facebook.FacebookException.java
com.facebook.FacebookGraphObjectException.java
com.facebook.FacebookOperationCanceledException.java
com.facebook.FacebookRequestError.java
com.facebook.FacebookSdkVersion.java
com.facebook.FacebookServiceException.java
com.facebook.GetTokenClient.java
com.facebook.HttpMethod.java
com.facebook.InsightsLogger.java
com.facebook.LegacyHelper.java
com.facebook.LoggingBehavior.java
com.facebook.LoginActivity.java
com.facebook.NativeProtocol.java
com.facebook.NonCachingTokenCachingStrategy.java
com.facebook.RequestAsyncTask.java
com.facebook.RequestBatch.java
com.facebook.Request.java
com.facebook.Response.java
com.facebook.SessionDefaultAudience.java
com.facebook.SessionLoginBehavior.java
com.facebook.SessionState.java
com.facebook.Session.java
com.facebook.Settings.java
com.facebook.SharedPreferencesTokenCachingStrategy.java
com.facebook.TestSession.java
com.facebook.TokenCachingStrategy.java
com.facebook.UiLifecycleHelper.java
com.facebook.android.AsyncFacebookRunner.java
com.facebook.android.DialogError.java
com.facebook.android.FacebookError.java
com.facebook.android.Facebook.java
com.facebook.android.FbDialog.java
com.facebook.android.Util.java
com.facebook.internal.CacheableRequestBatch.java
com.facebook.internal.FileLruCache.java
com.facebook.internal.Logger.java
com.facebook.internal.ServerProtocol.java
com.facebook.internal.SessionAuthorizationType.java
com.facebook.internal.SessionTracker.java
com.facebook.internal.Utility.java
com.facebook.internal.Validate.java
com.facebook.internal.package-info.java
com.facebook.model.GraphLocation.java
com.facebook.model.GraphMultiResult.java
com.facebook.model.GraphObjectList.java
com.facebook.model.GraphObject.java
com.facebook.model.GraphPlace.java
com.facebook.model.GraphUser.java
com.facebook.model.JsonUtil.java
com.facebook.model.OpenGraphAction.java
com.facebook.model.PropertyName.java
com.facebook.widget.FacebookFragment.java
com.facebook.widget.FriendPickerFragment.java
com.facebook.widget.GraphObjectAdapter.java
com.facebook.widget.GraphObjectCursor.java
com.facebook.widget.GraphObjectPagingLoader.java
com.facebook.widget.ImageDownloader.java
com.facebook.widget.ImageRequest.java
com.facebook.widget.ImageResponseCache.java
com.facebook.widget.ImageResponse.java
com.facebook.widget.LoginButton.java
com.facebook.widget.PickerFragment.java
com.facebook.widget.PlacePickerFragment.java
com.facebook.widget.ProfilePictureView.java
com.facebook.widget.SimpleGraphObjectCursor.java
com.facebook.widget.UrlRedirectCache.java
com.facebook.widget.UserSettingsFragment.java
com.facebook.widget.WebDialog.java
com.facebook.widget.WorkQueue.java
org.example.SocialNetwork.CCSocialNetwork.java
org.example.SocialNetwork.EmailSender.java
org.example.SocialNetwork.InternetConnection.java