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>