com.WazaBe.GooglePlusStream.SocialStreamFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.WazaBe.GooglePlusStream.SocialStreamFragment.java

Source

package com.WazaBe.GooglePlusStream;

/*
 * Copyright 2012 Google Inc.
 *
 * 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.
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Color;
import android.net.ParseException;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.google.api.client.googleapis.services.GoogleKeyInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpRequestInitializer;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.plus.Plus;
import com.google.api.services.plus.model.Activity;
import com.google.api.services.plus.model.ActivityFeed;

/**
 * A {@link WebView}-based fragment that shows Google+ public search results for
 * a given query, provided as the {@link SocialStreamFragment#EXTRA_QUERY} extra
 * in the fragment arguments. If no search query is provided, the conference
 * hashtag is used as the default query.
 * 
 * <p>
 * WARNING! This fragment uses the Google+ API, and is subject to quotas. If you
 * expect to write a wildly popular app based on this code, please check the at
 * <a href="https://developers.google.com/+/">Google+ Platform documentation</a>
 * on latest best practices and quota details. You can check your current quota
 * at the <a href="https://code.google.com/apis/console">APIs console</a>.
 */
public class SocialStreamFragment extends SherlockListFragment
        implements AbsListView.OnScrollListener, LoaderManager.LoaderCallbacks<List<Activity>> {

    // Configure keyword here:
    private static final String KEYWORD = "Waza_be";

    public static final String EXTRA_QUERY = "com.google.android.iosched.extra.QUERY";
    private static final String STATE_POSITION = "position";
    private static final String STATE_TOP = "top";

    private static final long MAX_RESULTS_PER_REQUEST = 20;
    private static final int STREAM_LOADER_ID = 0;

    private String mSearchString;

    private List<Activity> mStream = new ArrayList<Activity>();
    private StreamAdapter mStreamAdapter = new StreamAdapter();
    private int mListViewStatePosition;
    private int mListViewStateTop;

    private ImageFetcher mImageFetcher;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Intent intent = fragmentArgumentsToIntent(getArguments());

        // mSearchString can be populated before onCreate() by called
        // refresh(String)
        if (TextUtils.isEmpty(mSearchString)) {
            mSearchString = intent.getStringExtra(EXTRA_QUERY);
        }
        if (TextUtils.isEmpty(mSearchString)) {
            mSearchString = KEYWORD;
        }

        // if (!mSearchString.startsWith("#")) {
        // mSearchString = "#" + mSearchString;
        // }

        mImageFetcher = getImageFetcher(getActivity());
        setListAdapter(mStreamAdapter);
        setHasOptionsMenu(true);
    }

    /**
     * Converts a fragment arguments bundle into an intent.
     */
    public static Intent fragmentArgumentsToIntent(Bundle arguments) {
        Intent intent = new Intent();
        if (arguments == null) {
            return intent;
        }

        final Uri data = arguments.getParcelable("_uri");
        if (data != null) {
            intent.setData(data);
        }

        intent.putExtras(arguments);
        intent.removeExtra("_uri");
        return intent;
    }

    public static ImageFetcher getImageFetcher(final FragmentActivity activity) {
        // The ImageFetcher takes care of loading remote images into our
        // ImageView
        ImageFetcher fetcher = new ImageFetcher(activity);
        fetcher.setImageCache(ImageCache.findOrCreateCache(activity, "imageFetcher"));
        return fetcher;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mListViewStatePosition = savedInstanceState.getInt(STATE_POSITION, -1);
            mListViewStateTop = savedInstanceState.getInt(STATE_TOP, 0);
        } else {
            mListViewStatePosition = -1;
            mListViewStateTop = 0;
        }
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setEmptyText(getString(R.string.empty_social_stream));

        // In support library r8, calling initLoader for a fragment in a
        // FragmentPagerAdapter
        // in the fragment's onCreate may cause the same LoaderManager to be
        // dealt to multiple
        // fragments because their mIndex is -1 (haven't been added to the
        // activity yet). Thus,
        // we do this in onActivityCreated.
        getLoaderManager().initLoader(STREAM_LOADER_ID, null, this);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setBackgroundColor(Color.WHITE);

        final ListView listView = getListView();
        listView.setCacheColorHint(Color.WHITE);
        listView.setOnScrollListener(this);
        listView.setDrawSelectorOnTop(true);
        // TypedValue v = new TypedValue();
        // getActivity().getTheme().resolveAttribute(R.attr.activatableItemBackground,
        // v, true);
        // listView.setSelector(v.resourceId);
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mImageFetcher != null) {
            mImageFetcher.closeCache();
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // inflater.inflate(R.menu.social_stream, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    /*
     * @Override public boolean onOptionsItemSelected(MenuItem item) { switch
     * (item.getItemId()) { case R.id.menu_compose: Intent intent =
     * ShareCompat.IntentBuilder.from(getActivity()) .setType("text/plain")
     * .setText(mSearchString + "\n\n") .getIntent();
     * 
     * UIUtils.preferPackageForIntent(getActivity(), intent,
     * UIUtils.GOOGLE_PLUS_PACKAGE_NAME);
     * 
     * startActivity(intent);
     * 
     * EasyTracker.getTracker().trackEvent("Home Screen Dashboard", "Click",
     * "Post to Google+", 0L); LOGD("Tracker",
     * "Home Screen Dashboard: Click, post to Google+");
     * 
     * return true; } return super.onOptionsItemSelected(item); }
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (isAdded()) {
            View v = getListView().getChildAt(0);
            int top = (v == null) ? 0 : v.getTop();
            outState.putInt(STATE_POSITION, getListView().getFirstVisiblePosition());
            outState.putInt(STATE_TOP, top);
        }
        super.onSaveInstanceState(outState);
    }

    public void refresh(String newQuery) {
        mSearchString = newQuery;
        refresh(true);
    }

    public void refresh() {
        refresh(false);
    }

    public void refresh(boolean forceRefresh) {
        if (isStreamLoading() && !forceRefresh) {
            return;
        }

        // clear current items
        mStream.clear();
        mStreamAdapter.notifyDataSetInvalidated();

        if (isAdded()) {
            Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID);
            ((StreamLoader) loader).init(mSearchString);
        }

        loadMoreResults();
    }

    public void loadMoreResults() {
        if (isAdded()) {
            Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID);
            if (loader != null) {
                loader.forceLoad();
            }
        }
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        Activity activity = mStream.get(position);
        /*
         * Intent postDetailIntent = new Intent(Intent.ACTION_VIEW,
         * Uri.parse(activity.getUrl()));
         * postDetailIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
         * ); preferPackageForIntent(getActivity(), postDetailIntent,
         * "com.google.android.apps.plus"); safeOpenLink(getActivity(),
         * postDetailIntent);
         */
    }

    public static void safeOpenLink(Context context, Intent linkIntent) {
        try {
            context.startActivity(linkIntent);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, "Couldn't open link", Toast.LENGTH_SHORT).show();
        }
    }

    public static void preferPackageForIntent(Context context, Intent intent, String packageName) {
        PackageManager pm = context.getPackageManager();
        for (ResolveInfo resolveInfo : pm.queryIntentActivities(intent, 0)) {
            if (resolveInfo.activityInfo.packageName.equals(packageName)) {
                intent.setPackage(packageName);
                break;
            }
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) {
        // Pause disk cache access to ensure smoother scrolling
        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
                || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
            mImageFetcher.setPauseDiskCache(true);
        } else {
            mImageFetcher.setPauseDiskCache(false);
        }
    }

    @Override
    public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // Simple implementation of the infinite scrolling UI pattern; loads
        // more Google+
        // search results as the user scrolls to the end of the list.
        if (!isStreamLoading() && streamHasMoreResults() && visibleItemCount != 0
                && firstVisibleItem + visibleItemCount >= totalItemCount - 1) {
            loadMoreResults();
        }
    }

    @Override
    public Loader<List<Activity>> onCreateLoader(int id, Bundle args) {
        return new StreamLoader(getActivity(), mSearchString);

    }

    @Override
    public void onLoadFinished(Loader<List<Activity>> listLoader, List<Activity> activities) {
        if (activities != null) {
            mStream = activities;
        }
        mStreamAdapter.notifyDataSetChanged();
        if (mListViewStatePosition != -1 && isAdded()) {
            getListView().setSelectionFromTop(mListViewStatePosition, mListViewStateTop);
            mListViewStatePosition = -1;
        }
    }

    @Override
    public void onLoaderReset(Loader<List<Activity>> listLoader) {
    }

    private boolean isStreamLoading() {
        if (isAdded()) {
            final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID);
            if (loader != null) {
                return ((StreamLoader) loader).isLoading();
            }
        }
        return true;
    }

    private boolean streamHasMoreResults() {
        if (isAdded()) {
            final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID);
            if (loader != null) {
                return ((StreamLoader) loader).hasMoreResults();
            }
        }
        return false;
    }

    private boolean streamHasError() {
        if (isAdded()) {
            final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID);
            if (loader != null) {
                return ((StreamLoader) loader).hasError();
            }
        }
        return false;
    }

    /**
     * An {@link AsyncTaskLoader} that loads activities from the public Google+
     * stream for a given search query. The loader maintains a page state with
     * the Google+ API and thus supports pagination.
     */
    private static class StreamLoader extends AsyncTaskLoader<List<Activity>> {
        List<Activity> mActivities;
        private String mSearchString;
        private String mNextPageToken;
        private boolean mIsLoading;
        private boolean mHasError;

        public StreamLoader(Context context, String searchString) {
            super(context);
            init(searchString);
        }

        private void init(String searchString) {
            mSearchString = searchString;
            mHasError = false;
            mNextPageToken = null;
            mIsLoading = true;
            mActivities = null;
        }

        @Override
        public List<Activity> loadInBackground() {
            mIsLoading = true;

            // Set up the HTTP transport and JSON factory
            HttpTransport httpTransport = new NetHttpTransport();
            JsonFactory jsonFactory = new JacksonFactory();

            JsonHttpRequestInitializer initializer = new GoogleKeyInitializer(Private.API_KEY);

            // Set up the main Google+ class
            Plus plus = Plus.builder(httpTransport, jsonFactory).setApplicationName("WazaBe - Developement tests")
                    .setJsonHttpRequestInitializer(initializer).build();

            ActivityFeed activities = null;
            try {
                activities = plus.activities().search(mSearchString).setPageToken(mNextPageToken)
                        .setMaxResults(MAX_RESULTS_PER_REQUEST).execute();

                mHasError = false;
                mNextPageToken = activities.getNextPageToken();

            } catch (IOException e) {
                e.printStackTrace();
                mHasError = true;
                mNextPageToken = null;
            }

            return (activities != null) ? activities.getItems() : null;
        }

        @Override
        public void deliverResult(List<Activity> activities) {
            mIsLoading = false;
            if (activities != null) {
                if (mActivities == null) {
                    mActivities = activities;
                } else {
                    mActivities.addAll(activities);
                }
            }
            if (isStarted()) {
                // Need to return new ArrayList for some reason or
                // onLoadFinished() is not called
                super.deliverResult(mActivities == null ? null : new ArrayList<Activity>(mActivities));
            }
        }

        @Override
        protected void onStartLoading() {
            if (mActivities != null) {
                // If we already have results and are starting up, deliver what
                // we already have.
                deliverResult(null);
            } else {
                forceLoad();
            }
        }

        @Override
        protected void onStopLoading() {
            mIsLoading = false;
            cancelLoad();
        }

        @Override
        protected void onReset() {
            super.onReset();
            onStopLoading();
            mActivities = null;
        }

        public boolean isLoading() {
            return mIsLoading;
        }

        public boolean hasMoreResults() {
            return mNextPageToken != null;
        }

        public boolean hasError() {
            return mHasError;
        }

        public void setSearchString(String searchString) {
            mSearchString = searchString;
        }

        public void refresh(String searchString) {
            setSearchString(searchString);
            refresh();
        }

        public void refresh() {
            reset();
            startLoading();
        }
    }

    /**
     * A list adapter that shows individual Google+ activities as list items. If
     * another page is available, the last item is a "loading" view to support
     * the infinite scrolling UI pattern.
     */
    private class StreamAdapter extends BaseAdapter {
        private static final int VIEW_TYPE_ACTIVITY = 0;
        private static final int VIEW_TYPE_LOADING = 1;

        @Override
        public boolean areAllItemsEnabled() {
            return false;
        }

        @Override
        public boolean isEnabled(int position) {
            return getItemViewType(position) == VIEW_TYPE_ACTIVITY;
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        @Override
        public int getCount() {
            return mStream.size() + (
            // show the status list row if...
            ((isStreamLoading() && mStream.size() == 0) // ...this is
                    // the first
                    // load
                    || streamHasMoreResults() // ...or there's another
                    // page
                    || streamHasError()) // ...or there's an error
                            ? 1
                            : 0);
        }

        @Override
        public int getItemViewType(int position) {
            return (position >= mStream.size()) ? VIEW_TYPE_LOADING : VIEW_TYPE_ACTIVITY;
        }

        @Override
        public Object getItem(int position) {
            return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mStream.get(position) : null;
        }

        @Override
        public long getItemId(int position) {
            // TODO: better unique ID heuristic
            return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mStream.get(position).getId().hashCode()
                    : -1;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (getItemViewType(position) == VIEW_TYPE_LOADING) {
                if (convertView == null) {
                    convertView = getLayoutInflater(null).inflate(R.layout.list_item_stream_status, parent, false);
                }

                if (streamHasError()) {
                    convertView.findViewById(android.R.id.progress).setVisibility(View.GONE);
                    ((TextView) convertView.findViewById(android.R.id.text1)).setText("R.string.stream_error");
                } else {
                    convertView.findViewById(android.R.id.progress).setVisibility(View.VISIBLE);
                    ((TextView) convertView.findViewById(android.R.id.text1)).setText("R.string.loading");
                }

                return convertView;

            } else {
                Activity activity = (Activity) getItem(position);
                if (convertView == null) {
                    convertView = getLayoutInflater(null).inflate(R.layout.list_item_stream_activity, parent,
                            false);
                }

                StreamRowViewBinder.bindActivityView(convertView, activity, mImageFetcher);
                return convertView;
            }
        }
    }

    /**
     * A helper class to bind data from a Google+ {@link Activity} to the list
     * item view.
     */
    private static class StreamRowViewBinder {
        private static class ViewHolder {
            private TextView[] detail;
            private ImageView[] detailIcon;
            private ImageView[] media;
            private ImageView[] mediaOverlay;
            private TextView originalAuthor;
            private View reshareLine;
            private View reshareSpacer;
            private ImageView userImage;
            private TextView userName;
            private TextView content;
            private View plusOneIcon;
            private TextView plusOneCount;
            private View commentIcon;
            private TextView commentCount;
        }

        private static void bindActivityView(final View rootView, Activity activity, ImageFetcher imageFetcher) {
            // Prepare view holder.
            ViewHolder temp = (ViewHolder) rootView.getTag();
            final ViewHolder views;
            if (temp != null) {
                views = temp;
            } else {
                views = new ViewHolder();
                rootView.setTag(views);
                views.detail = new TextView[] { (TextView) rootView.findViewById(R.id.stream_detail_text) };
                views.detailIcon = new ImageView[] {
                        (ImageView) rootView.findViewById(R.id.stream_detail_media_small) };
                views.media = new ImageView[] { (ImageView) rootView.findViewById(R.id.stream_media_1_1),
                        (ImageView) rootView.findViewById(R.id.stream_media_1_2), };
                views.mediaOverlay = new ImageView[] {
                        (ImageView) rootView.findViewById(R.id.stream_media_overlay_1_1),
                        (ImageView) rootView.findViewById(R.id.stream_media_overlay_1_2), };
                views.originalAuthor = (TextView) rootView.findViewById(R.id.stream_original_author);
                views.reshareLine = rootView.findViewById(R.id.stream_reshare_line);
                views.reshareSpacer = rootView.findViewById(R.id.stream_reshare_spacer);
                views.userImage = (ImageView) rootView.findViewById(R.id.stream_user_image);
                views.userName = (TextView) rootView.findViewById(R.id.stream_user_name);
                views.content = (TextView) rootView.findViewById(R.id.stream_content);
                views.plusOneIcon = rootView.findViewById(R.id.stream_plus_one_icon);
                views.plusOneCount = (TextView) rootView.findViewById(R.id.stream_plus_one_count);
                views.commentIcon = rootView.findViewById(R.id.stream_comment_icon);
                views.commentCount = (TextView) rootView.findViewById(R.id.stream_comment_count);
            }

            final Resources res = rootView.getContext().getResources();

            // Hide all the array items.
            int detailIndex = 0;
            int mediaIndex = 0;

            for (View v : views.detail) {
                v.setVisibility(View.GONE);
            }

            for (View v : views.detailIcon) {
                v.setVisibility(View.GONE);
            }

            for (View v : views.media) {
                v.setVisibility(View.GONE);
            }

            for (View v : views.mediaOverlay) {
                v.setVisibility(View.GONE);
            }

            // Determine if this is a reshare (affects how activity fields are
            // to be
            // interpreted).
            boolean isReshare = (activity.getObject().getActor() != null);

            // Set user name.
            views.userName.setText(activity.getActor().getDisplayName());

            if (activity.getActor().getImage() != null) {
                imageFetcher.loadThumbnailImage(activity.getActor().getImage().getUrl(), views.userImage,
                        R.drawable.person_image_empty);
            } else {
                views.userImage.setImageResource(R.drawable.person_image_empty);
            }

            // Set +1 and comment counts.
            final int plusOneCount = (activity.getObject().getPlusoners() != null)
                    ? activity.getObject().getPlusoners().getTotalItems().intValue()
                    : 0;
            if (plusOneCount == 0) {
                views.plusOneIcon.setVisibility(View.GONE);
                views.plusOneCount.setVisibility(View.GONE);
            } else {
                views.plusOneIcon.setVisibility(View.VISIBLE);
                views.plusOneCount.setVisibility(View.VISIBLE);
                views.plusOneCount.setText(Integer.toString(plusOneCount));
            }

            final int commentCount = (activity.getObject().getReplies() != null)
                    ? activity.getObject().getReplies().getTotalItems().intValue()
                    : 0;
            if (commentCount == 0) {
                views.commentIcon.setVisibility(View.GONE);
                views.commentCount.setVisibility(View.GONE);
            } else {
                views.commentIcon.setVisibility(View.VISIBLE);
                views.commentCount.setVisibility(View.VISIBLE);
                views.commentCount.setText(Integer.toString(commentCount));
            }

            // Set content.
            String selfContent = isReshare ? activity.getAnnotation() : activity.getObject().getContent();

            if (!TextUtils.isEmpty(selfContent)) {
                views.content.setVisibility(View.VISIBLE);
                views.content.setText(Html.fromHtml(selfContent));
            } else {
                views.content.setVisibility(View.GONE);
            }

            // Set original author.
            if (activity.getObject().getActor() != null) {
                views.originalAuthor.setVisibility(View.VISIBLE);
                views.originalAuthor.setText(res.getString(R.string.stream_originally_shared,
                        activity.getObject().getActor().getDisplayName()));

                views.reshareLine.setVisibility(View.VISIBLE);
                views.reshareSpacer.setVisibility(View.INVISIBLE);

            } else {
                views.originalAuthor.setVisibility(View.GONE);
                views.reshareLine.setVisibility(View.GONE);
                views.reshareSpacer.setVisibility(View.GONE);
            }

            // Set document content.
            if (isReshare && !TextUtils.isEmpty(activity.getObject().getContent())
                    && detailIndex < views.detail.length) {
                views.detail[detailIndex].setVisibility(View.VISIBLE);
                views.detail[detailIndex].setTextColor(res.getColor(R.color.stream_content_color));
                views.detail[detailIndex].setText(Html.fromHtml(activity.getObject().getContent()));
                ++detailIndex;
            }

            // Set location.
            String location = activity.getPlaceName();
            if (!TextUtils.isEmpty(location)) {
                location = activity.getAddress();
            }
            if (!TextUtils.isEmpty(location)) {
                location = activity.getGeocode();
            }

            if (!TextUtils.isEmpty(location) && detailIndex < views.detail.length) {
                views.detail[detailIndex].setVisibility(View.VISIBLE);
                views.detail[detailIndex].setTextColor(res.getColor(R.color.stream_link_color));
                views.detailIcon[detailIndex].setVisibility(View.VISIBLE);
                views.detail[detailIndex].setText(location);

                if ("checkin".equals(activity.getVerb())) {
                    views.detailIcon[detailIndex].setImageResource(R.drawable.stream_ic_checkin);
                } else {
                    views.detailIcon[detailIndex].setImageResource(R.drawable.stream_ic_location);
                }

                ++detailIndex;
            }

            // Set media content.
            if (activity.getObject().getAttachments() != null) {
                for (Activity.PlusObject.Attachments attachment : activity.getObject().getAttachments()) {
                    String objectType = attachment.getObjectType();
                    if (("photo".equals(objectType) || "video".equals(objectType))
                            && mediaIndex < views.media.length) {
                        if (attachment.getImage() == null) {
                            continue;
                        }

                        final ImageView mediaView = views.media[mediaIndex];
                        mediaView.setVisibility(View.VISIBLE);
                        imageFetcher.loadThumbnailImage(attachment.getImage().getUrl(), mediaView);

                        if ("video".equals(objectType)) {
                            views.mediaOverlay[mediaIndex].setVisibility(View.VISIBLE);
                        }

                        ++mediaIndex;

                    } else if (("article".equals(objectType)) && detailIndex < views.detail.length) {

                        try {
                            String faviconUrl = "http://www.google.com/s2/favicons?domain="
                                    + Uri.parse(attachment.getUrl()).getHost();

                            final ImageView iconView = views.detailIcon[detailIndex];
                            iconView.setVisibility(View.VISIBLE);
                            imageFetcher.loadThumbnailImage(faviconUrl, iconView);
                        } catch (ParseException ignored) {
                        }

                        views.detail[detailIndex].setVisibility(View.VISIBLE);
                        views.detail[detailIndex].setTextColor(res.getColor(R.color.stream_link_color));
                        views.detail[detailIndex].setText(attachment.getDisplayName());

                        ++detailIndex;
                    }
                }
            }
        }
    }
}