Android Open Source - streaming_project Media List Fragment






From Project

Back to project page streaming_project.

License

The source code is released under:

GNU General Public License

If you think the Android project streaming_project listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.example.streaming.streaming;
// ww w  .  java2 s .c  om

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.text.Html;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;


// It is a subclass of ListFragment that comes with built-in help for displaying lists
// It will interact with MediaListActivity and MediaManager to access the data (list of media)
// in the model layer. It will then display the list of info about the media files that can be
// streamed or downloaded. The info consists in the media title, length, ... MORE
public class MediaListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
    // For debugging
    private static final String TAG = "MediaListFragment";

    private MediaCursorAdapter mAdapter;
    private DownloadManager mDownloadManager;
    private long mMyDownloadReference;
    private BroadcastReceiver mReceiverDownloadComplete;
    private BroadcastReceiver mReceiverNotificationClicked;

    public MediaListFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Let the FragmentManager know that MediaListFragment needs to receive options menu
        // callbacks, i.e. Fragment.onCreateOptionsMenu() needs to be called by the FragmentManager
        // The options Menu will allow the creation of a new media by the user
        // (TODO: not implemented yet)
        setHasOptionsMenu(true);
        // Retain the fragment so that the value of mSubtitleVisible (TODO: not used for the moment)
        // survives rotation
        // NOTE: if the subtitle is shown and you rotate, the subtitle will disappear when the
        // interface is created from scratch
        setRetainInstance(true);

        // Initialize the loader to load the list of media
        // TODO: Should it be called in onActivityCreated() like in com.stylingandroid.adapters.CursorAdapterFragment?
        getLoaderManager().initLoader(0, null, this);

        // Change what is displayed on the action bar
        getActivity().setTitle(R.string.media_list_title);

        // TODO: explain what we are doing here
        mDownloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
    }

    @Override
    // NOTE: MediaListFragment does not inflate its own layout in onCreateView() TODO: ??!!
    public View onCreateView(LayoutInflater inflater, ViewGroup parent,
                             Bundle savedInstanceState) {
        View v = super.onCreateView(inflater, parent, savedInstanceState);

        // Get a reference to the ListView
        // NOTE: android.R.id.list is used to retrieve the ListView managed by ListFragment within
        // onCreateView()
        ListView listView = (ListView)v.findViewById(android.R.id.list);

        // Based on the SDK version, develop a floating contextual menu (on API 10 and lower) or a
        // contextual action bar (on API 12 and higher)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            // TODO: test if we are able to show a floating context menu in older devices through the emulator
            // Use floating context menu on Froyo and Gingerbread

            // Register the ListView
            registerForContextMenu(listView);
        } else {
            // Use contextual action bar on Honeycomb and higher
            // Enable selecting multiple list items at one time by setting the list view's choice
            // mode to CHOICE_MODE_MULTIPLE_MODAL
            // NOTE: any action chosen from the contextual action bar will apply to all of the views
            // that have been selected
            listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
            // Set a listener that implements MultiChoiceModeListener on the list view
            // NOTE 1: MultiChoiceModeListener implements another interface, ActionMode.Callback
            // NOTE 2: you only need to take action in onCreateActionMode() and onActionItemClicked()
            listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {

                public void onItemCheckedStateChanged(ActionMode mode, int position,
                                                      long id, boolean checked) {
                    // TODO: this solution to highlight the selected items in CAB is too complicated
                    if (checked) {
                        mAdapter.setNewSelection(position, checked);
                    } else {
                        mAdapter.removeSelection(position);
                    }
                }

                // ActionMode.Callback methods

                // Called when the ActionMode is created
                // NOTE: returning false will abort the creation of the action mode
                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                    // Inflate the context menu resource to be displayed in the contextual action
                    // bar
                    // NOTE 1: we get the MenuInflater from the action mode rather than the activity
                    // NOTE 2: the action mode has details for configuring the contextual action
                    // bar, e.g. ActionMode.setTitle() gives the contextual action bar its own title
                    MenuInflater inflater = mode.getMenuInflater();
                    inflater.inflate(R.menu.media_list_item_context, menu);
                    return true;
                }

                // Called after onCreateActionMode() and whenever an existing CAB needs to be
                // refreshed with new data
                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                    return false;
                }

                // Called when the user selects an action from the CAB
                // The DownloadManger will download the selected files from the list of items
                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                    // Respond to contextual actions defined in the menu resource
                    switch (item.getItemId()) {
                        case R.id.menu_item_download_media:
                            // TODO: add explanations
                            Map<Integer, String> map = new HashMap<Integer, String>();
                            for (int i = 0; i < mAdapter.getCount(); i++) {
                                if (getListView().isItemChecked(i)) {
                                    MediaDatabaseHelper.MediaCursor c = (MediaDatabaseHelper.MediaCursor) mAdapter.getItem(i);

                                    int media_id = c.getInt(c.getColumnIndex("media_id"));
                                    String filename = c.getString(c.getColumnIndex("filename"));

                                    map.put(media_id, filename);
                                    Log.d(TAG, "Item " + i + " selected" + " Filename: " + filename);
                                }
                            }

                            // TODO: Is it necessary to use a thread to manage the downloading?
                            // TODO: does the download manager already executes separately from the main thread?
                            DownloadThread dt = new DownloadThread(getActivity(), map);
                            dt.start();

                            // Prepare the action mode to be destroyed
                            // TODO: Why do we have to destroy the action mode?
                            mode.finish();
                            return true;
                        default:
                            return false;
                    }
                }

                // Called when the ActionMode is about to be destroyed.
                // NOTE: Default implementation results in the view(s) being unselected
                public void onDestroyActionMode(ActionMode mode) {
                    mAdapter.clearSelection();
                }
            });

        }

        return v;
    }

    class DownloadThread extends Thread {
        private Map<Integer, String> mMap;
        // TODO: if the server IP address is wrong, there is no error message to warn the user about that
        private String mUriString;
        private String mSongQuery;
        private String mParam;

        public DownloadThread(Context context, Map<Integer, String> map) {
            mMap = map;
            AssetsPropertyReader propertiesReader = new AssetsPropertyReader(context);
            Properties properties = propertiesReader.getProperties("app.properties");
            mUriString = properties.getProperty("server_url");
            mSongQuery = properties.getProperty("song_query");
            mParam = properties.getProperty("song_param");
        }

        @Override
        public void run() {

            for (Map.Entry<Integer, String> entry : mMap.entrySet()) {
                int media_id = entry.getKey();
                String filename = entry.getValue();

                Uri uri = Uri.parse(mUriString).buildUpon()
                        .appendPath(mSongQuery)
                        .appendQueryParameter(mParam, Integer.toString(media_id))
                        .build();
                DownloadManager.Request request = new DownloadManager.Request(uri);

                // Make file visible and manageable by system's download app
                request.setVisibleInDownloadsUi(true);

                // Select which network, etc
                request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);

                // Set whether this download may proceed over a roaming connection.
                request.setAllowedOverRoaming(false);

                // Set the notification
                request.setDescription("Saved in /MUSIC")
                        .setTitle(new File(filename).getName());
                // TODO:  not going to work if downloading a file that is not an MP3 (e.g. ogg or mp4)
                request.setMimeType("audio/MP3");
                request.allowScanningByMediaScanner();
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

                // Set the path to where to save the file
                // Save in app package directory
                request.setDestinationInExternalPublicDir(
                        Environment.DIRECTORY_MUSIC, new File(filename).getName());

                // Queue the download
                mMyDownloadReference = mDownloadManager.enqueue(request);
            }
        }
    }

    @Override
    public void onPause(){
        super.onPause();
        getActivity().unregisterReceiver(mReceiverDownloadComplete);
        getActivity().unregisterReceiver(mReceiverNotificationClicked);
    }

    @Override
    // Reloads the list to make sure the fragment's view is up-to-date
    public void onResume() {
        super.onResume();

        // Filter for notifications - only acts on notification while download busy
        IntentFilter filter = new IntentFilter(DownloadManager
                .ACTION_NOTIFICATION_CLICKED);

        // TODO: to be tested
        mReceiverNotificationClicked = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String extraId = DownloadManager
                        .EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS;
                long[] references = intent.getLongArrayExtra(extraId);
                for (long reference : references) {
                    if (reference == mMyDownloadReference) {
                        // TODO: Do something with the download file
                    }
                }
            }
        };
        getActivity().registerReceiver(mReceiverNotificationClicked, filter);

        // Filter for download - on completion
        IntentFilter intentFilter = new IntentFilter(DownloadManager
                .ACTION_DOWNLOAD_COMPLETE);

        mReceiverDownloadComplete = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                long reference = intent.getLongExtra(DownloadManager
                        .EXTRA_DOWNLOAD_ID, -1);
                if (mMyDownloadReference == reference) {
                    // Do something with the download file
                    DownloadManager.Query query = new DownloadManager.Query();
                    query.setFilterById(reference);
                    Cursor cursor = mDownloadManager.query(query);

                    cursor.moveToFirst();
                    // Get the status of the download
                    int columnIndex = cursor.getColumnIndex(DownloadManager
                            .COLUMN_STATUS);
                    int status = cursor.getInt(columnIndex);

                    int fileNameIndex = cursor.getColumnIndex(DownloadManager
                            .COLUMN_LOCAL_FILENAME);
                    String savedFilePath = cursor.getString(fileNameIndex);

                    // Get the reason - more details on the status
                    int columnReason = cursor.getColumnIndex(DownloadManager
                            .COLUMN_REASON);
                    int reason = cursor.getInt(columnReason);

                    // TODO: give better error messages
                    switch (status) {
                        case DownloadManager.STATUS_SUCCESSFUL:
                            Toast.makeText(getActivity(),
                                    "SUCCESS: Download complete. \nSaved in " + savedFilePath,
                                    Toast.LENGTH_LONG).show();
                            break;
                        case DownloadManager.STATUS_FAILED:
                            Toast.makeText(getActivity(),
                                    "FAILED: " + reason + "\nFile: " + savedFilePath,
                                    Toast.LENGTH_LONG).show();
                            break;
                        case DownloadManager.STATUS_PAUSED:
                            Toast.makeText(getActivity(),
                                    "PAUSED: " + reason + "\nFile: " + savedFilePath,
                                    Toast.LENGTH_LONG).show();
                            break;
                        case DownloadManager.STATUS_PENDING:
                            Toast.makeText(getActivity(),
                                    "PENDING!" + "\nFile: " + savedFilePath,
                                    Toast.LENGTH_LONG).show();
                            break;
                        case DownloadManager.STATUS_RUNNING:
                            Toast.makeText(getActivity(),
                                    "RUNNING!" + "\nFile: " + savedFilePath,
                                    Toast.LENGTH_LONG).show();
                            break;
                    }
                    cursor.close();
                }
            }
        };
        getActivity().registerReceiver(mReceiverDownloadComplete, intentFilter);
    }

    @Override
    // Responds to the user touching a list item by starting MediaActivity with the _id of the
    // selected media
    // NOTE 1: It is called by CursorAdapter
    // NOTE 2: Because you named the ID column in the media table _id, CursorAdapter has detected it
    // and passed it as the id argument to onListItemClick()
    // TODO: id refers to the _id column, not the media_id column. As explained in MediaDatabaseHelper,
    // TODO: we will in the near future use the media_id the server send as to populate the _id
    // TODO: column. The media_id column will be removed.
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Start a MediaActivity with an extra
        // TODO: make it work with MediaPagerActivity
        Intent i = new Intent(getActivity(), MediaActivity.class);
        // Put the media ID of the selected Media on the intent that starts MediaActivity so that
        // MediaFragment knows which Media to display
        // NOTE: CursorAdapter gives us media ID for free
        i.putExtra(MediaActivity.EXTRA_MEDIA_ID, id);
        startActivity(i);
        // TODO: explain what the following does
        l.setItemChecked(position, ((MediaCursorAdapter) getListAdapter()).isPositionChecked(position));
    }

    @Override
    // Creates the options menu
    // It is called by the MediaListActivity's FragmentManager when the activity receives its
    // onCreateOptionsMenu() callback from the OS
    // TODO: the Options Menu is not used
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    // Creates the FLOATING context menu (with the Download Media menu item) when a view is
    // long-pressed (any list item is clicked)
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        // Inflate the menu resource and use it to populate the context menu
        getActivity().getMenuInflater().inflate(R.menu.media_list_item_context, menu);
    }

    // Very simple base subclass of SQLiteCursorLoader
    // NOTE 1: It is an inner class
    // NOTE 2: MediaListFragment must also implement the LoaderCallbacks interface for a Cursor
    // which consists in the three methods: onCreateLoader(), onLoadFinished(), and onLoaderReset()
    private static class MediaListCursorLoader extends SQLiteCursorLoader {

        public MediaListCursorLoader(Context context) {
            super(context);
        }

        @Override
        protected Cursor loadCursor() {
            // Query the list of media
            return MediaManager.get(getContext()).queryMediaList();
        }
    }

    // NOTE 1: the next three methods are for implementing the LoaderCallbacks interface for a
    // Cursor
    // NOTE 2: the class declaration was updated to declare that it implements the callbacks
    // NOTE 3: with these callbacks in place, you can tell LoaderManager to do its thing, i.e. to
    // start and restart the loader (which will load data in the background from the database).

    @Override
    // Called by the LoaderManager when it needs you to create the loader
    // NOTE 1: the id argument is useful if you have more than one loader of the same type and you
    // need to distinguish them
    // NOTE 2: Bundle holds any arguments that were passed in
    // NOTE 3: this implementation does not use either argument, and simply creates a new
    // MediaListCursorLoader pointing at the current Activity for context
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // You only ever load the list of media, so assume this is the case
        return new MediaListCursorLoader(getActivity());
    }

    @Override
    // Called on the main thread once the data has been loaded in the background
    // NOTE: In this version, we reset the adapter on the ListView to a MediaCursorAdapter pointing
    // at the new cursor TODO: Understand what is going on here
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        // Create an adapter to point at this cursor
        mAdapter =
                new MediaCursorAdapter(getActivity(), (MediaDatabaseHelper.MediaCursor)cursor);
        setListAdapter(mAdapter);
    }

    @Override
    // Called in the event that the data is no longer available
    public void onLoaderReset(Loader<Cursor> loader) {
        // Stop using the cursor (via the adapter)
        // NOTE: to be on the safe side, stop using the cursor by setting the list adapter to null
        setListAdapter(null);
    }

    // Provides a way for a MediaCursor to provide its data to the ListView associated with
    // MediaListFragment
    // NOTE: A CursorAdapter handles the logic of creating and reusing views
    private static class MediaCursorAdapter extends CursorAdapter {

        private MediaDatabaseHelper.MediaCursor mMediaCursor;
        private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();

        public MediaCursorAdapter(Context context, MediaDatabaseHelper.MediaCursor cursor) {
            // NOTE: The third param of the constructor for CursorAdapter is an integer of flags
            // that are now deprecated or questionable in favor of using loaders, so we pass 0
            super(context, cursor, 0);
            // Stash the MediaCursor in an instance variable to avoid having to cast it later
            mMediaCursor = cursor;
        }

        // TODO: explain the following methods
        public void setNewSelection(int position, boolean value) {
            mSelection.put(position, value);
            notifyDataSetChanged();
        }

        public boolean isPositionChecked(int position) {
            Boolean result = mSelection.get(position);
            return result == null ? false : result;
        }

        public Set<Integer> getCurrentCheckedPosition() {
            return mSelection.keySet();
        }

        public void removeSelection(int position) {
            mSelection.remove(position);
            notifyDataSetChanged();
        }

        public void clearSelection() {
            mSelection = new HashMap<Integer, Boolean>();
            notifyDataSetChanged();
        }

        @Override
        // Returns a View to represent the current row in the cursor
        // NOTE: this View is then given as argument to bindView()
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            // TODO: test
            // Inflate a view from the custom layout defined in list_item_media.xml
            LayoutInflater inflater = (LayoutInflater)context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View convertView = inflater.inflate(R.layout.list_item_media, null);

            return convertView;
        }

        @Override
        // Called by CursorAdapter when it wants to you to configure a view to hold data for a row
        // in the cursor
        // NOTE: it will always be called with a View that has been previously returned from
        // newView()
        public void bindView(View view, Context context, Cursor cursor) {
            if (cursor.getPosition()%2 == 0) {
                // TODO: use Color.RED instead of rgb values
                view.setBackgroundColor(Color.rgb(238, 233, 233));
            } else {
                view.setBackgroundColor(Color.rgb(255, 255, 255));
            }
            if (mSelection.get(cursor.getPosition()) != null) {
                view.setBackgroundColor(Color.rgb(6, 87, 249)); // this is a selected position so make it blue
            }

            // Get the media for the current row (the cursor will have already been positioned by
            // CursorAdapter)
            Media media = mMediaCursor.getMedia();

            // Get a reference to each widget in the view object and configure it with the Media's
            // data
            TextView titleTextView =
                    (TextView)view.findViewById(R.id.list_item_media_titleTextView);
            titleTextView.setText(media.getTitle());
            TextView infoTextView =
                    (TextView)view.findViewById(R.id.list_item_media_infoTextView);
            //  TODO: it is better to have a method in Media that outputs all the required info about the media
            String info = String.format("by %s <br> %s:%s",
                    media.getArtist(), media.getMins(), media.getSecs());
            infoTextView.setText(Html.fromHtml(info));
        }
    }
}




Java Source Code List

com.example.streaming.streaming.ApplicationTest.java
com.example.streaming.streaming.AssetsPropertyReader.java
com.example.streaming.streaming.DataFetcher.java
com.example.streaming.streaming.DataLoader.java
com.example.streaming.streaming.MainActivity.java
com.example.streaming.streaming.MediaActivity.java
com.example.streaming.streaming.MediaDatabaseHelper.java
com.example.streaming.streaming.MediaFragment.java
com.example.streaming.streaming.MediaListActivity.java
com.example.streaming.streaming.MediaListFragment.java
com.example.streaming.streaming.MediaLoader.java
com.example.streaming.streaming.MediaManager.java
com.example.streaming.streaming.MediaProvider.java
com.example.streaming.streaming.Media.java
com.example.streaming.streaming.SQLiteCursorLoader.java
com.example.streaming.streaming.SingleFragmentActivity.java