alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (PhotostreamActivity.java)

This example Android source code file (PhotostreamActivity.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, bitmap, drawing, graphics, gridlayout, imageview, intent, layoutanimationcontroller, loadedphoto, loadphotostask, notificationmanager, object, override, paint, photos_count_per_page, random, string, ui, view, viewanimator

The PhotostreamActivity.java Android example source code

/*
 * Copyright (C) 2008 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.
 */

package com.google.android.photostream;

import android.app.Activity;
import android.app.NotificationManager;
import android.os.Bundle;
import android.content.Intent;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.widget.ImageView;
import android.widget.ViewAnimator;

import java.util.Random;
import java.util.List;

/**
 * Activity used to display a Flickr user's photostream. This activity shows a fixed
 * number of photos at a time. The activity is invoked either by LoginActivity, when
 * the application is launched normally, or by a Home shortcut, or by an Intent with
 * the view action and a flickr://photos/nsid URI.
 */
public class PhotostreamActivity extends Activity implements
        View.OnClickListener, Animation.AnimationListener {

    static final String ACTION = "com.google.android.photostream.FLICKR_STREAM";

    static final String EXTRA_NOTIFICATION = "com.google.android.photostream.extra_notify_id";
    static final String EXTRA_NSID = "com.google.android.photostream.extra_nsid";
    static final String EXTRA_USER = "com.google.android.photostream.extra_user";

    private static final String STATE_USER = "com.google.android.photostream.state_user";
    private static final String STATE_PAGE = "com.google.android.photostream.state_page";
    private static final String STATE_PAGE_COUNT = "com.google.android.photostream.state_pagecount";

    private static final int PHOTOS_COUNT_PER_PAGE = 6;

    private Flickr.User mUser;
    private int mCurrentPage = 1;
    private int mPageCount = 0;

    private LayoutInflater mInflater;

    private ViewAnimator mSwitcher;
    private View mMenuNext;
    private View mMenuBack;
    private View mMenuSeparator;
    private GridLayout mGrid;

    private LayoutAnimationController mNextAnimation;
    private LayoutAnimationController mBackAnimation;

    private UserTask<?, ?, ?> mTask;
    private String mUsername;

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

        clearNotification();

        // Try to find a user name in the saved instance state or the intent
        // that launched the activity. If no valid user NSID can be found, we
        // just close the activity.
        if (!initialize(savedInstanceState)) {
            finish();
            return;
        }

        setContentView(R.layout.screen_photostream);
        setupViews();

        loadPhotos();
    }

    private void clearNotification() {
        final int notification = getIntent().getIntExtra(EXTRA_NOTIFICATION, -1);
        if (notification != -1) {
            NotificationManager manager = (NotificationManager)
                    getSystemService(Context.NOTIFICATION_SERVICE);
            manager.cancel(notification);
        }
    }

    /**
     * Starts the PhotostreamActivity for the specified user.
     *
     * @param context The application's environment.
     * @param user The user whose photos to display with a PhotostreamActivity.
     */
    static void show(Context context, Flickr.User user) {
        final Intent intent = new Intent(ACTION);
        intent.putExtra(EXTRA_USER, user);
        context.startActivity(intent);
    }

    /**
     * Restores a previously saved state or, if missing, finds the user's NSID
     * from the intent used to start the activity.
     *
     * @param savedInstanceState The saved state, if any.
     *
     * @return true if a {@link com.google.android.photostream.Flickr.User} was
     *         found either in the saved state or the intent.
     */
    private boolean initialize(Bundle savedInstanceState) {
        Flickr.User user;
        if (savedInstanceState != null) {
            user = savedInstanceState.getParcelable(STATE_USER);
            mCurrentPage = savedInstanceState.getInt(STATE_PAGE);
            mPageCount = savedInstanceState.getInt(STATE_PAGE_COUNT);
        } else {
            user = getUser();
        }
        mUser = user;
        return mUser != null || mUsername != null;
    }

    /**
     * Creates a {@link com.google.android.photostream.Flickr.User} instance
     * from the intent used to start this activity.
     *
     * @return The user whose photos will be displayed, or null if no
     *         user was found.
     */
    private Flickr.User getUser() {
        final Intent intent = getIntent();
        final String action = intent.getAction();

        Flickr.User user = null;

        if (ACTION.equals(action)) {
            final Bundle extras = intent.getExtras();
            if (extras != null) {
                user = extras.getParcelable(EXTRA_USER);

                if (user == null) {
                    final String nsid = extras.getString(EXTRA_NSID);
                    if (nsid != null) {
                        user = Flickr.User.fromId(nsid);
                    }
                }
            }
        } else if (Intent.ACTION_VIEW.equals(action)) {
            final List<String> segments = intent.getData().getPathSegments();
            if (segments.size() > 1) {
                mUsername = segments.get(1);
            }
        }

        return user;
    }

    private void setupViews() {
        mInflater = LayoutInflater.from(PhotostreamActivity.this);
        mNextAnimation = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_slide_next);
        mBackAnimation = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_slide_back);

        mSwitcher = (ViewAnimator) findViewById(R.id.switcher_menu);
        mMenuNext = findViewById(R.id.menu_next);
        mMenuBack = findViewById(R.id.menu_back);
        mMenuSeparator = findViewById(R.id.menu_separator);
        mGrid = (GridLayout) findViewById(R.id.grid_photos);

        mMenuNext.setOnClickListener(this);
        mMenuBack.setOnClickListener(this);
        mMenuBack.setVisibility(View.GONE);
        mMenuSeparator.setVisibility(View.GONE);
        mGrid.setClipToPadding(false);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putParcelable(STATE_USER, mUser);
        outState.putInt(STATE_PAGE, mCurrentPage);
        outState.putInt(STATE_PAGE_COUNT, mPageCount);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mTask != null && mTask.getStatus() == UserTask.Status.RUNNING) {
            mTask.cancel(true);
        }
    }

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.menu_next:
                onNext();
                break;
            case R.id.menu_back:
                onBack();
                break;
            default:
                onShowPhoto((Flickr.Photo) v.getTag());
                break;
        }
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        final GridLayout grid = mGrid;
        final int count = grid.getChildCount();
        final LoadedPhoto[] list = new LoadedPhoto[count];

        for (int i = 0; i < count; i++) {
            final ImageView v = (ImageView) grid.getChildAt(i);
            list[i] = new LoadedPhoto(((BitmapDrawable) v.getDrawable()).getBitmap(),
                    (Flickr.Photo) v.getTag());
        }

        return list;
    }

    private void prepareMenu(int pageCount) {
        final boolean backVisible = mCurrentPage > 1;
        final boolean nextVisible = mCurrentPage < pageCount;

        mMenuBack.setVisibility(backVisible ? View.VISIBLE : View.GONE);
        mMenuNext.setVisibility(nextVisible ? View.VISIBLE : View.GONE);

        mMenuSeparator.setVisibility(backVisible && nextVisible ? View.VISIBLE : View.GONE);
    }

    private void loadPhotos() {
        final Object data = getLastNonConfigurationInstance();
        if (data == null) {
            mTask = new GetPhotoListTask().execute(mCurrentPage);
        } else {
            final LoadedPhoto[] photos = (LoadedPhoto[]) data;
            for (LoadedPhoto photo : photos) {
                addPhoto(photo);
            }
            prepareMenu(mPageCount);
            mSwitcher.showNext();
        }
    }

    private void showPhotos(Flickr.PhotoList photos) {
        mTask = new LoadPhotosTask().execute(photos);
    }

    private void onShowPhoto(Flickr.Photo photo) {
        ViewPhotoActivity.show(this, photo);
    }

    private void onNext() {
        mCurrentPage++;
        animateAndLoadPhotos(mNextAnimation);
    }

    private void onBack() {
        mCurrentPage--;
        animateAndLoadPhotos(mBackAnimation);
    }

    private void animateAndLoadPhotos(LayoutAnimationController animation) {
        mSwitcher.showNext();
        mGrid.setLayoutAnimationListener(this);
        mGrid.setLayoutAnimation(animation);
        mGrid.invalidate();
    }

    public void onAnimationEnd(Animation animation) {
        mGrid.setLayoutAnimationListener(null);
        mGrid.setLayoutAnimation(null);
        mGrid.removeAllViews();
        loadPhotos();
    }

    public void onAnimationStart(Animation animation) {
    }

    public void onAnimationRepeat(Animation animation) {
    }

    private static Animation createAnimationForChild(int childIndex) {
        boolean firstColumn = (childIndex & 0x1) == 0;

        Animation translate = new TranslateAnimation(
                Animation.RELATIVE_TO_SELF, firstColumn ? -1.1f : 1.1f,
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);

        translate.setInterpolator(new AccelerateDecelerateInterpolator());
        translate.setFillAfter(false);
        translate.setDuration(300);

        return translate;
    }

    private void addPhoto(LoadedPhoto... value) {
        ImageView image = (ImageView) mInflater.inflate(R.layout.grid_item_photo, mGrid, false);
        image.setImageBitmap(value[0].mBitmap);
        image.startAnimation(createAnimationForChild(mGrid.getChildCount()));
        image.setTag(value[0].mPhoto);
        image.setOnClickListener(PhotostreamActivity.this);
        mGrid.addView(image);
    }    

    /**
     * Background task used to load each individual photo. The task loads each photo
     * in order and publishes each loaded Bitmap as a progress unit. The tasks ends
     * by hiding the progress bar and showing the menu.
     */
    private class LoadPhotosTask extends UserTask<Flickr.PhotoList, LoadedPhoto, Flickr.PhotoList> {
        private final Random mRandom;

        private LoadPhotosTask() {
            mRandom = new Random();
        }

        public Flickr.PhotoList doInBackground(Flickr.PhotoList... params) {
            final Flickr.PhotoList list = params[0];
            final int count = list.getCount();

            for (int i = 0; i < count; i++) {
                if (isCancelled()) break;

                final Flickr.Photo photo = list.get(i);
                Bitmap bitmap = photo.loadPhotoBitmap(Flickr.PhotoSize.THUMBNAIL);
                if (!isCancelled()) {
                    if (bitmap == null) {
                        final boolean portrait = mRandom.nextFloat() >= 0.5f;
                        bitmap = BitmapFactory.decodeResource(getResources(), portrait ?
                            R.drawable.not_found_small_1 : R.drawable.not_found_small_2);
                    }
                    publishProgress(new LoadedPhoto(ImageUtilities.rotateAndFrame(bitmap), photo));
                    bitmap.recycle();
                }
            }

            return list;
        }

        /**
         * Whenever a photo's Bitmap is loaded from the background thread, it is
         * displayed in this method by adding a new ImageView in the photos grid.
         * Each ImageView's tag contains the {@link com.google.android.photostream.Flickr.Photo}
         * it was loaded from.
         *
         * @param value The photo and its bitmap.
         */
        @Override
        public void onProgressUpdate(LoadedPhoto... value) {
            addPhoto(value);
        }

        @Override
        public void onPostExecute(Flickr.PhotoList result) {
            mPageCount = result.getPageCount();
            prepareMenu(mPageCount);
            mSwitcher.showNext();
            mTask = null;            
        }
    }

    /**
     * Background task used to load the list of photos. The tasks queries Flickr for the
     * list of photos to display and ends by starting the LoadPhotosTask.
     */
    private class GetPhotoListTask extends UserTask<Integer, Void, Flickr.PhotoList> {
        public Flickr.PhotoList doInBackground(Integer... params) {
            if (mUsername != null) {
                mUser = Flickr.get().findByUserName(mUsername);
                mUsername = null;
            }
            return Flickr.get().getPublicPhotos(mUser, PHOTOS_COUNT_PER_PAGE, params[0]);
        }

        @Override
        public void onPostExecute(Flickr.PhotoList photoList) {
            showPhotos(photoList);
            mTask = null;
        }
    }

    /**
     * A LoadedPhoto contains the Flickr photo and the Bitmap loaded for that photo.
     */
    private static class LoadedPhoto {
        Bitmap mBitmap;
        Flickr.Photo mPhoto;

        LoadedPhoto(Bitmap bitmap, Flickr.Photo photo) {
            mBitmap = bitmap;
            mPhoto = photo;
        }
    }
}

Other Android examples (source code examples)

Here is a short list of links related to this Android PhotostreamActivity.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.