fr.free.nrw.commons.media.MediaDetailFragment.java Source code

Java tutorial

Introduction

Here is the source code for fr.free.nrw.commons.media.MediaDetailFragment.java

Source

package fr.free.nrw.commons.media;

import android.content.Intent;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;

import com.android.volley.toolbox.ImageLoader;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;

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

import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.License;
import fr.free.nrw.commons.LicenseList;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.MediaWikiImageView;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;

public class MediaDetailFragment extends Fragment {

    private boolean editable;
    private DisplayImageOptions displayOptions;
    private fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider detailProvider;
    private int index;

    public static MediaDetailFragment forMedia(int index) {
        return forMedia(index, false);
    }

    public static MediaDetailFragment forMedia(int index, boolean editable) {
        MediaDetailFragment mf = new MediaDetailFragment();

        Bundle state = new Bundle();
        state.putBoolean("editable", editable);
        state.putInt("index", index);
        state.putInt("listIndex", 0);
        state.putInt("listTop", 0);

        mf.setArguments(state);

        return mf;
    }

    private ImageView image;
    //private EditText title;
    private ProgressBar loadingProgress;
    private ImageView loadingFailed;
    private fr.free.nrw.commons.media.MediaDetailSpacer spacer;
    private int initialListTop = 0;

    private TextView title;
    private TextView desc;
    private TextView license;
    private LinearLayout categoryContainer;
    private ScrollView scrollView;
    private ArrayList<String> categoryNames;
    private boolean categoriesLoaded = false;
    private boolean categoriesPresent = false;
    private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
    private ViewTreeObserver.OnScrollChangedListener scrollListener;
    DataSetObserver dataObserver;
    private AsyncTask<Void, Void, Boolean> detailFetchTask;
    private LicenseList licenseList;

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("index", index);
        outState.putBoolean("editable", editable);

        getScrollPosition();
        outState.putInt("listTop", initialListTop);
    }

    private void getScrollPosition() {
        initialListTop = scrollView.getScrollY();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        detailProvider = (fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider) getActivity();

        if (savedInstanceState != null) {
            editable = savedInstanceState.getBoolean("editable");
            index = savedInstanceState.getInt("index");
            initialListTop = savedInstanceState.getInt("listTop");
        } else {
            editable = getArguments().getBoolean("editable");
            index = getArguments().getInt("index");
            initialListTop = 0;
        }
        categoryNames = new ArrayList<String>();
        categoryNames.add(getString(R.string.detail_panel_cats_loading));

        final View view = inflater.inflate(R.layout.fragment_media_detail, container, false);

        image = (ImageView) view.findViewById(R.id.mediaDetailImage);
        loadingProgress = (ProgressBar) view.findViewById(R.id.mediaDetailImageLoading);
        loadingFailed = (ImageView) view.findViewById(R.id.mediaDetailImageFailed);
        scrollView = (ScrollView) view.findViewById(R.id.mediaDetailScrollView);

        // Detail consists of a list view with main pane in header view, plus category list.
        spacer = (fr.free.nrw.commons.media.MediaDetailSpacer) view.findViewById(R.id.mediaDetailSpacer);
        title = (TextView) view.findViewById(R.id.mediaDetailTitle);
        desc = (TextView) view.findViewById(R.id.mediaDetailDesc);
        license = (TextView) view.findViewById(R.id.mediaDetailLicense);
        categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer);

        licenseList = new LicenseList(getActivity());

        Media media = detailProvider.getMediaAtPosition(index);
        if (media == null) {
            // Ask the detail provider to ping us when we're ready
            Log.d("Commons", "MediaDetailFragment not yet ready to display details; registering observer");
            dataObserver = new DataSetObserver() {
                public void onChanged() {
                    Log.d("Commons", "MediaDetailFragment ready to display delayed details!");
                    detailProvider.unregisterDataSetObserver(dataObserver);
                    dataObserver = null;
                    displayMediaDetails(detailProvider.getMediaAtPosition(index));
                }
            };
            detailProvider.registerDataSetObserver(dataObserver);
        } else {
            Log.d("Commons", "MediaDetailFragment ready to display details");
            displayMediaDetails(media);
        }

        // Progressively darken the image in the background when we scroll detail pane up
        scrollListener = new ViewTreeObserver.OnScrollChangedListener() {
            public void onScrollChanged() {
                updateTheDarkness();
            }
        };
        view.getViewTreeObserver().addOnScrollChangedListener(scrollListener);

        // Layout layoutListener to size the spacer item relative to the available space.
        // There may be a .... better way to do this.
        layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            private int currentHeight = -1;

            public void onGlobalLayout() {
                int viewHeight = view.getHeight();
                //int textHeight = title.getLineHeight();
                int paddingDp = 112;
                float paddingPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingDp,
                        getResources().getDisplayMetrics());
                int newHeight = viewHeight - Math.round(paddingPx);

                if (newHeight != currentHeight) {
                    currentHeight = newHeight;
                    ViewGroup.LayoutParams params = spacer.getLayoutParams();
                    params.height = newHeight;
                    spacer.setLayoutParams(params);

                    scrollView.scrollTo(0, initialListTop);
                }

            }
        };
        view.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
        return view;
    }

    private void displayMediaDetails(final Media media) {
        //Always load image from Internet to allow viewing the desc, license, and cats
        String actualUrl = media.getThumbnailUrl(640);
        if (actualUrl.startsWith("http")) {
            Log.d("Volley", "Actual URL starts with http and is: " + actualUrl);

            ImageLoader loader = ((CommonsApplication) getActivity().getApplicationContext()).getImageLoader();
            MediaWikiImageView mwImage = (MediaWikiImageView) image;
            mwImage.setLoadingView(loadingProgress); //FIXME: Set this as an attribute
            mwImage.setMedia(media, loader);

            // FIXME: For transparent images
            // FIXME: keep the spinner going while we load data
            // FIXME: cache this data
            // Load image metadata: desc, license, categories
            detailFetchTask = new AsyncTask<Void, Void, Boolean>() {
                private MediaDataExtractor extractor;

                @Override
                protected void onPreExecute() {
                    extractor = new MediaDataExtractor(media.getFilename(), licenseList);
                }

                @Override
                protected Boolean doInBackground(Void... voids) {
                    try {
                        extractor.fetch();
                        return Boolean.TRUE;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return Boolean.FALSE;
                }

                @Override
                protected void onPostExecute(Boolean success) {
                    detailFetchTask = null;

                    if (success.booleanValue()) {
                        extractor.fill(media);

                        // Set text of desc, license, and categories
                        desc.setText(prettyDescription(media));
                        license.setText(prettyLicense(media));

                        categoryNames.removeAll(categoryNames);
                        categoryNames.addAll(media.getCategories());

                        categoriesLoaded = true;
                        categoriesPresent = (categoryNames.size() > 0);
                        if (!categoriesPresent) {
                            // Stick in a filler element.
                            categoryNames.add(getString(R.string.detail_panel_cats_none));
                        }
                        rebuildCatList();
                    } else {
                        Log.d("Commons", "Failed to load photo details.");
                    }
                }
            };
            Utils.executeAsyncTask(detailFetchTask);
        } else {
            //This should not usually happen, image along with associated details should always be loaded from Internet, but keeping this for now for backup.
            //Even if image is loaded from device storage, it will display, albeit with empty desc and cat.
            Log.d("Volley", "Actual URL does not start with http and is: " + actualUrl);
            com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(actualUrl, image,
                    displayOptions, new ImageLoadingListener() {
                        public void onLoadingStarted(String s, View view) {
                            loadingProgress.setVisibility(View.VISIBLE);
                        }

                        public void onLoadingFailed(String s, View view, FailReason failReason) {
                            loadingProgress.setVisibility(View.GONE);
                            loadingFailed.setVisibility(View.VISIBLE);
                        }

                        public void onLoadingComplete(String s, View view, Bitmap bitmap) {
                            loadingProgress.setVisibility(View.GONE);
                            loadingFailed.setVisibility(View.GONE);
                            image.setVisibility(View.VISIBLE);
                            if (bitmap.hasAlpha()) {
                                image.setBackgroundResource(android.R.color.white);
                            }

                            // Set text of desc, license, and categories
                            desc.setText(prettyDescription(media));
                            license.setText(prettyLicense(media));

                            categoryNames.removeAll(categoryNames);
                            categoryNames.addAll(media.getCategories());

                            categoriesLoaded = true;
                            categoriesPresent = (categoryNames.size() > 0);
                            if (!categoriesPresent) {
                                // Stick in a filler element.
                                categoryNames.add(getString(R.string.detail_panel_cats_none));
                            }
                            rebuildCatList();
                        }

                        public void onLoadingCancelled(String s, View view) {
                            Log.e("Volley", "Image loading cancelled. But why?");
                        }
                    });
        }

        title.setText(media.getDisplayTitle());
        desc.setText(""); // fill in from network...
        license.setText(""); // fill in from network...
    }

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

        displayOptions = Utils.getGenericDisplayOptions().build();
    }

    @Override
    public void onDestroyView() {
        if (detailFetchTask != null) {
            detailFetchTask.cancel(true);
            detailFetchTask = null;
        }
        if (layoutListener != null) {
            getView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener); // old Android was on crack. CRACK IS WHACK
            layoutListener = null;
        }
        if (scrollListener != null) {
            getView().getViewTreeObserver().removeOnScrollChangedListener(scrollListener);
            scrollListener = null;
        }
        if (dataObserver != null) {
            detailProvider.unregisterDataSetObserver(dataObserver);
            dataObserver = null;
        }
        super.onDestroyView();
    }

    private void rebuildCatList() {
        // @fixme add the category items
        for (String cat : categoryNames) {
            View catLabel = buildCatLabel(cat);
            categoryContainer.addView(catLabel);
        }
    }

    private View buildCatLabel(String cat) {
        final String catName = cat;
        final View item = getLayoutInflater(null).inflate(R.layout.detail_category_item, null, false);
        final TextView textView = (TextView) item.findViewById(R.id.mediaDetailCategoryItemText);

        textView.setText(cat);
        if (categoriesLoaded && categoriesPresent) {
            textView.setOnClickListener(new View.OnClickListener() {
                public void onClick(View view) {
                    String selectedCategoryTitle = "Category:" + catName;
                    Intent viewIntent = new Intent();
                    viewIntent.setAction(Intent.ACTION_VIEW);
                    viewIntent.setData(Utils.uriForWikiPage(selectedCategoryTitle));
                    startActivity(viewIntent);
                }
            });
        }
        return item;
    }

    private void updateTheDarkness() {
        // You must face the darkness alone
        int scrollY = scrollView.getScrollY();
        int scrollMax = getView().getHeight();
        float scrollPercentage = (float) scrollY / (float) scrollMax;
        final float transparencyMax = 0.75f;
        if (scrollPercentage > transparencyMax) {
            scrollPercentage = transparencyMax;
        }
        image.setAlpha(1.0f - scrollPercentage);
    }

    private String prettyDescription(Media media) {
        // @todo use UI language when multilingual descs are available
        String desc = media.getDescription("en").trim();
        if (desc.equals("")) {
            return getString(R.string.detail_description_empty);
        } else {
            return desc;
        }
    }

    private String prettyLicense(Media media) {
        String licenseKey = media.getLicense();
        Log.d("Commons", "Media license is: " + licenseKey);
        if (licenseKey == null || licenseKey.equals("")) {
            return getString(R.string.detail_license_empty);
        }
        License licenseObj = licenseList.get(licenseKey);
        if (licenseObj == null) {
            return licenseKey;
        } else {
            return licenseObj.getName();
        }
    }
}