Back to project page streaming_project.
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.
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)); } } }