2

I am a newcomer to Android development and I am using the book "Android Apps with Eclipse" by Onur Cinar to get started.

I have completed chapter 6 which develops a MoviePlayer app (source code: http://www.apress.com/9781430244349), however when running the app on my phone I am unable to see any thumbnails of my listed movies. When I record new movies using my phone, the new movies are added to the movie list with the default green Android icon as a thumbnail with the original movies still having no thumbnail icons.

My code appears to match that of the given source code. Is there something wrong with the code given in the book or is this expected behaviour? If it is the latter, under what circumstances will the (non-default) thumbnail icons appear in the movie list?

Movie.java:

package com.apress.movieplayer;

import android.database.Cursor;
import android.provider.MediaStore;

/**
 * Movie file meta data.
 * 
 * @author Josh
 */
public class Movie
{
/** Movie title */
private final String title;

/** Movie file */
private final String moviePath;

/** MIME type */
private final String mimeType;

/** Movie duration in ms */
private final long duration;

/** Thumbnail file */
private final String thumbnailPath;

/**
 * Constructor.
 * 
 * @param mediaCursor media cursor.
 * @param thumbnailCursor thumbnail cursor.
 */
public Movie(Cursor mediaCursor, Cursor thumbnailCursor)
{
    title = mediaCursor.getString(mediaCursor.getColumnIndexOrThrow(
            MediaStore.Video.Media.TITLE));

    moviePath = mediaCursor.getString(mediaCursor.getColumnIndex(
            MediaStore.Video.Media.DATA));

    mimeType = mediaCursor.getString(mediaCursor.getColumnIndex(
            MediaStore.Video.Media.MIME_TYPE));

    duration = mediaCursor.getLong(mediaCursor.getColumnIndex(
            MediaStore.Video.Media.DURATION));

    if ( (thumbnailCursor != null) && thumbnailCursor.moveToFirst() )
    {
        thumbnailPath = thumbnailCursor.getString(
                thumbnailCursor.getColumnIndex(
                        MediaStore.Video.Thumbnails.DATA));
    }
    else
    {
        thumbnailPath = null;
    }
}

/**
 * Get the movie title.
 * 
 * @return movie title.
 */
public String getTitle() {
    return title;
}

/**
 * Get the movie path.
 * 
 * @return movie path.
 */
public String getMoviePath() {
    return moviePath;
}

/**
 * Get the MIME type.
 * 
 * @return MIME type.
 */
public String getMimeType() {
    return mimeType;
}

/**
 * Get the movie duration.
 * 
 * @return movie duration.
 */
public long getDuration() {
    return duration;
}

/**
 * Get the thumbnail path.
 * 
 * @return thumbnail path.
 */
public String getThumbnailPath() {
    return thumbnailPath;
}

/*
 * @see java.lang.Object#toString()
 */
@Override
public String toString()
{
    return "Movie [title=" + title + ", moviePath=" + moviePath
            + ", mimeType=" + mimeType + ", duration=" + duration
            + ", thumbnailPath=" + thumbnailPath + "]";
}
}

MovieListAdapter.java:

package com.apress.movieplayer;

import java.util.ArrayList;

import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

/**
 * Movie list view adapter.
 * 
 * @author Josh
 */
public class MovieListAdapter extends BaseAdapter
{
/** Context instance */
private final Context context;

/** Movie list */
private final ArrayList<Movie> movieList;

/**
 * Constructor.
 * 
 * @param context context instance.
 * @param movieList movie list.
 */
public MovieListAdapter(Context context, ArrayList<Movie> movieList)
{
    this.context = context;
    this.movieList = movieList;
}

/**
 * Gets the number of elements in movie list.
 * 
 * @see BaseAdapter#getCount()
 */
public int getCount() {
    return movieList.size();
}

/**
 * Gets the movie item at given position.
 * 
 * @param position item position.
 * @see BaseAdapter#getItem(int)
 */
public Object getItem(int position) {
    return movieList.get(position);
}

/**
 * Gets the movie id at given position.
 * 
 * @param position item position.
 * @return movie id.
 * @see BaseAdapter#getItemId(int)
 */
public long getItemId(int position) {
    return position;
}

/**
 * Gets the item view for given position.
 * 
 * @param position item position.
 * @param convertView existing view to use.
 * @param parent parent view to use.
 */
public View getView(int position, View convertView, ViewGroup parent) {
    // check if convert view exists or inflate the layout
    if (convertView == null)
    {
        LayoutInflater layoutInflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.movie_item, null);
    }

    // Get the movie at given position
    Movie movie = (Movie) getItem(position);

    // Set thumbnail
    ImageView thumbnail = (ImageView) convertView.findViewById(
            R.id.thumbnail);

    if (movie.getThumbnailPath() != null)
    {
        thumbnail.setImageURI(Uri.parse(movie.getThumbnailPath()));
    }
    else
    {
        thumbnail.setImageResource(R.drawable.ic_launcher);
    }

    // Set title
    TextView title = (TextView) convertView.findViewById(R.id.title);
    title.setText(movie.getTitle());

    // Set duration
    TextView duration = (TextView) convertView.findViewById(R.id.duration);
    duration.setText(getDurationAsString(movie.getDuration()));

    return convertView;
}

private static String getDurationAsString(long duration)
{
    // Calculate milliseconds
    long milliseconds = duration % 1000;
    long seconds = duration / 1000;

    // Calculate seconds
    long minutes = seconds / 60;
    seconds %= 60;

    // Calculate hours and minutes
    long hours = minutes / 60;
    minutes %= 60;

    // Build the duration string
    String durationString = String.format("%1$02d:%2$02d:%3$02d.%4$03d",
            hours, minutes, seconds, milliseconds);

    return durationString;
}

}

MoviePlayerActivity.java:

package com.apress.movieplayer;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
//import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;

/**
 * Movie Player.
 * 
 * @author Josh
 *
 */
public class MoviePlayerActivity extends Activity implements OnItemClickListener
{
/** Log tag. */
private static final String LOG_TAG = "MoviePlayer";


/**
 * On create lifecycle method.
 * 
 * @param savedInstanceState saved state.
 * @see Activity#onCreate(Bundle)
 */
@Override
    protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_movie_player);

    ArrayList<Movie> movieList = new ArrayList<Movie>();

    // Media columns to query
    String[] mediaColumns = {
            MediaStore.Video.Media._ID,
            MediaStore.Video.Media.TITLE,
            MediaStore.Video.Media.DURATION,
            MediaStore.Video.Media.DATA,
            MediaStore.Video.Media.MIME_TYPE };

    // Thumbnail columns to query
    String[] thumbnailColumns = { MediaStore.Video.Thumbnails.DATA };

    // Query external movie content for selected media columns
    Cursor mediaCursor = getContentResolver().query(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            mediaColumns, null, null, null);

            /*managedQuery(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns,
            null, null, null);*/

    // Loop through media results
    if ( (mediaCursor != null) && mediaCursor.moveToFirst() )
    {
        do
        {
            // Get the video id
            int id = mediaCursor.getInt(mediaCursor
                    .getColumnIndex(MediaStore.Video.Media._ID));

            // Get the thumbnail associated with the video
            Cursor thumbnailCursor = getContentResolver().query(
                    MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
                    thumbnailColumns, MediaStore.Video.Thumbnails.VIDEO_ID
                        + "=" + id, null, null);

                    /*managedQuery(
                    MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI,
                    thumbnailColumns, MediaStore.Video.Thumbnails.VIDEO_ID
                            + "=" + id, null, null);*/

            // New movie object from the data
            Movie movie = new Movie(mediaCursor, thumbnailCursor);
            Log.d(LOG_TAG, movie.toString());

            // Add to movie list
            movieList.add(movie);

        }
        while (mediaCursor.moveToNext());
    }

    // Define movie list adapter
    MovieListAdapter movieListAdapter = new MovieListAdapter(this,
            movieList);

    // Set list view adapter to movie list adapter
    ListView movieListView = (ListView) findViewById(R.id.movieListView);
    movieListView.setAdapter(movieListAdapter);

    // Set  item click listener
    movieListView.setOnItemClickListener(this);
}

@Override
/*public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.activity_movie_player, menu);
    return true;
}*/

    /**
     * On item click listener.
     */
    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
    {
        // Gets the selected movie
        Movie movie = (Movie) parent.getAdapter().getItem(position);

        // Plays the selected movie
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.parse(movie.getMoviePath()), movie.getMimeType());
        startActivity(intent);
    }
}

activity_movie_player.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/movieListView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </ListView>
</LinearLayout>

movie_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

<ImageView
    android:contentDescription="@string/thumbnail_description"
    android:id="@+id/thumbnail"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_marginRight="16dp"
    android:src="@drawable/ic_launcher" />
<TextView
    android:id="@+id/title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignTop="@+id/thumbnail"
    android:layout_toRightOf="@+id/thumbnail"
    android:text="@string/large_text"
    android:textAppearance="?android:attr/textAppearanceLarge" />

<TextView 
    android:id="@+id/duration"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/title"
    android:layout_below="@+id/title"
    android:text="@string/small_text"
    android:textAppearance="?android:attr/textAppearanceSmall" />

</RelativeLayout>
Josh
  • 1,357
  • 2
  • 23
  • 45
  • On what version are you (2.2 and below does not support thumbnails)? Where do you store your files (internal storage or sdcard)? – znat Dec 03 '12 at 21:10
  • My phone is using android version 4.0.3 and all movie files are stored on an sdcard. – Josh Dec 03 '12 at 21:19
  • If you show your code we might help you better – znat Dec 03 '12 at 21:26

1 Answers1

0

Given the code, it appears that this is normal functionality of the Android OS. Once the videos are opened by other video playing applications it seems as though the thumbnails are generated within these more sophisticated programs and made available to other applications (including this demo movie player app).

I would be happy if someone could elaborate as to how this actually works "behind the scenes" as my analysis is somewhat black box.

Josh
  • 1,357
  • 2
  • 23
  • 45