/* * Copyright 2012 Brendan McCarthy (brendan@oddsoftware.net) * * This file is part of Feedscribe. * * Feedscribe is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * Feedscribe is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Feedscribe. If not, see <http://www.gnu.org/licenses/>. */ package net.oddsoftware.android.feedscribe.ui; import java.io.File; import net.oddsoftware.android.feedscribe.AudioPlayer; import net.oddsoftware.android.feedscribe.Globals; import net.oddsoftware.android.feedscribe.R; import net.oddsoftware.android.feedscribe.data.Enclosure; import net.oddsoftware.android.feedscribe.data.Feed; import net.oddsoftware.android.feedscribe.data.FeedItem; import net.oddsoftware.android.feedscribe.data.FeedManager; import net.oddsoftware.android.feedscribe.service.FeedService; import net.oddsoftware.android.utils.Utilities; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.webkit.WebView; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import android.widget.ViewFlipper; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; public class PodcastsActivity extends Activity { ViewFlipper mViewFlipper = null; ListView mPodcastsListView = null; PodcastFeedsListAdaptor mPodcastsAdapter = null; ListView mItemsListView = null; PodcastItemsListAdapter mItemsAdapter = null; WebView mShownotesView = null; String mContentType; int mOverrideContextMenuPosition; private static String KEY_CURRENT_VIEW = "current_view"; private static String KEY_CURRENT_FEED = "current_feed"; private static String KEY_CURRENT_ITEM = "current_item"; private static String KEY_FIRST_FEED = "first_feed"; private static String KEY_FIRST_ITEM = "first_item"; private static int MENU_ITEM_PLAY = 1; private static int MENU_ITEM_ADD_PLAYLIST = 2; private static int MENU_ITEM_DELETE = 3; private static int MENU_ITEM_DOWNLOAD = 4; private static int MENU_ITEM_SHOWNOTES = 5; private static int MENU_ITEM_MARK_READ = 6; private static int MENU_ITEM_MARK_UNREAD = 7; private static int MENU_ITEM_ADD_PLAYLIST_STREAM = 8; private static int MENU_ITEM_STREAM = 9; private static int MENU_ITEM_PLAY_EXTERNAL = 10; private static int MENU_ITEM_DELETE_FILE = 11; private static int MENU_ITEM_ITEMS_MAX = 11; private static int MENU_ITEM_DELETE_FEED = 12; private static int MENU_ITEM_RENAME_FEED = 13; private static int MENU_ITEM_REFRESH_FEED = 14; private static final int DIALOG_CONFIRM_DELETE_ID = 10; private static final int DIALOG_RENAME_FEED_ID = 11; private long mSelectedFeedId; private long mCurrentItem; private Handler mHandler = null; private Runnable mUpdateTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPodcastsListView = new ListView(this); mPodcastsListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) { onPodcastClicked(position); } }); mContentType = getIntent().getStringExtra("type"); mPodcastsAdapter = new PodcastFeedsListAdaptor( this, mContentType ); mPodcastsListView.setAdapter( mPodcastsAdapter ); if( mHandler == null ) { mHandler = new Handler(); } if( mUpdateTask == null ) { mUpdateTask = new Runnable() { @Override public void run() { onTimer(); } }; } if( mItemsListView == null) { mItemsListView = new ListView(this); mItemsListView.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { onItemClicked(position); } }); } mShownotesView = new WebView(this); mItemsAdapter = new PodcastItemsListAdapter( this, mContentType ); mItemsListView.setAdapter( mItemsAdapter ); registerForContextMenu( mItemsListView ); registerForContextMenu( mPodcastsListView ); mOverrideContextMenuPosition = -1; // now set up the view flipper mViewFlipper = new MyViewFlipper(this); setContentView(mViewFlipper); mViewFlipper.addView(mPodcastsListView, 0); mViewFlipper.addView(mItemsListView, 1); mViewFlipper.addView(mShownotesView, 2); if( savedInstanceState != null ) { long currentFeed = savedInstanceState.getLong(KEY_CURRENT_FEED, 0); if( currentFeed > 0 ) { mItemsAdapter.setFeedId(currentFeed); } mCurrentItem = savedInstanceState.getLong(KEY_CURRENT_ITEM, 0); if( mCurrentItem > 0) { FeedManager feedManager = FeedManager.getInstance(this); FeedItem feedItem = feedManager.getItemById(mCurrentItem); if( feedItem != null ) { mShownotesView.loadDataWithBaseURL(null, feedItem.mDescription, "text/html", "utf-8", null); } } int currentView = savedInstanceState.getInt(KEY_CURRENT_VIEW, 0); if( currentView >= 0 && currentView <= 2) { mViewFlipper.setDisplayedChild(currentView); } int firstItem = savedInstanceState.getInt(KEY_FIRST_ITEM, 0); if( firstItem >= 0 && firstItem < mItemsAdapter.getCount() ) { mItemsListView.setSelection(firstItem); } int firstFeed = savedInstanceState.getInt(KEY_FIRST_FEED, 0); if( firstFeed >= 0 && firstFeed < mPodcastsAdapter.getCount() ) { mPodcastsListView.setSelection(firstFeed); } } } private void onItemClicked(int position) { if( Globals.LOGGING) Log.d(Globals.LOG_TAG, "onItemClicked"); long itemId = mItemsAdapter.getFeedItemId(position); long enclosureId = mItemsAdapter.getEnclosureId(position); FeedManager feedManager = FeedManager.getInstance(this); FeedItem item = feedManager.getItemById(itemId); Enclosure enclosure = feedManager.getEnclosure(enclosureId); feedManager.verifyEnclosure(enclosure); if( enclosure.mDownloadTime <= 0 ) { // not downloaded, show menu asking what to do mOverrideContextMenuPosition = position; this.openContextMenu(mItemsListView); } else if( mContentType.equals("audio") ) { playAudio(item, enclosure); } else // if( mContentType.equals("video") ) { playVideo(item, enclosure); } } private void onPodcastClicked(int position) { if( Globals.LOGGING) Log.d(Globals.LOG_TAG, "onPodcastClicked"); long feedId = mPodcastsAdapter.getFeedId(position); mItemsAdapter.setFeedId( feedId ); showNext(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "onKeyDown"); if ((keyCode == KeyEvent.KEYCODE_BACK) && mViewFlipper.getDisplayedChild() > 0 ) { if( mViewFlipper.getDisplayedChild() == 2 ) { mItemsAdapter.update(); } if( mViewFlipper.getDisplayedChild() == 1 ) { mPodcastsAdapter.update(); } showPrevious(); return true; } return super.onKeyDown(keyCode, event); } private void showNext() { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "showNext"); mViewFlipper.setInAnimation(this, R.anim.view_transition_in_left); mViewFlipper.setOutAnimation(this, R.anim.view_transition_out_left); mViewFlipper.showNext(); updateTitle(); } private void showPrevious() { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "showPrevious"); mViewFlipper.setInAnimation(this, R.anim.view_transition_in_right); mViewFlipper.setOutAnimation(this, R.anim.view_transition_out_right); mViewFlipper.showPrevious(); updateTitle(); } @Override protected void onPause() { mHandler.removeCallbacks(mUpdateTask); super.onPause(); } @Override protected void onResume() { super.onResume(); AudioPlayer player = AudioPlayer.getInstance(this); if(player.isPlaying()) { long playingItemId = AudioPlayer.getInstance(this).getItemId(); mItemsAdapter.setPlayingItemId(playingItemId); } else { mItemsAdapter.setPlayingItemId(-1); } mItemsAdapter.update(); mPodcastsAdapter.update(); updateTitle(); onTimer(); } @Override public boolean onContextItemSelected(MenuItem menuItem) { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "onContextItemSelected " + menuItem); boolean handled = false; if( menuItem.getItemId() <= MENU_ITEM_ITEMS_MAX ) { handled = onItemContextItemSelected( menuItem ); } else { handled = onPodcastContextItemSelected( menuItem ); } if( handled ) { return true; } else { return super.onContextItemSelected(menuItem); } } private boolean onItemContextItemSelected(MenuItem menuItem) { int position = mOverrideContextMenuPosition; if( position < 0 ) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo(); position = info.position; } if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "onPodcastContextItemSelected " + position); FeedManager feedManager = FeedManager.getInstance(this); FeedItem item = null; Enclosure enclosure = null; if( position >= 0 ) { long itemId = mItemsAdapter.getFeedItemId(position); long enclosureId = mItemsAdapter.getEnclosureId(position); item = feedManager.getItemById(itemId); enclosure = feedManager.getEnclosure(enclosureId); } if( menuItem.getItemId() == MENU_ITEM_PLAY ) { if( mContentType.equals("audio")) { playAudio(item, enclosure); } else { playVideo(item, enclosure); } return true; } else if( menuItem.getItemId() == MENU_ITEM_PLAY_EXTERNAL && enclosure != null ) { Uri uri = null; if( enclosure.mDownloadTime > 0 ) { uri = Uri.fromFile( new File( enclosure.mDownloadPath ) ); } else { uri = Uri.parse( enclosure.mURL ); } try { Intent intent = new Intent( Intent.ACTION_VIEW, uri ); intent.setDataAndType( uri, enclosure.mContentType.toLowerCase() ); startActivity( intent ); item.mFlags |= FeedItem.FLAG_READ; feedManager.updateItemFlags(item); mItemsAdapter.update(); } catch( ActivityNotFoundException exc ) { Toast.makeText(this, R.string.error_play_external, Toast.LENGTH_LONG).show(); } return true; } else if( menuItem.getItemId() == MENU_ITEM_STREAM ) { if( mContentType.equals("audio")) { playAudio(item, enclosure); } else { playVideo(item, enclosure); } return true; } else if( menuItem.getItemId() == MENU_ITEM_MARK_READ ) { item.mFlags |= FeedItem.FLAG_READ; feedManager.updateItemFlags(item); mItemsAdapter.update(); return true; } else if( menuItem.getItemId() == MENU_ITEM_MARK_UNREAD ) { item.mFlags &= ~FeedItem.FLAG_READ; feedManager.updateItemFlags(item); mItemsAdapter.update(); return true; } else if( menuItem.getItemId() == MENU_ITEM_DELETE ) { feedManager.deleteFeedItem(item); mItemsAdapter.update(); return true; } else if( menuItem.getItemId() == MENU_ITEM_DELETE_FILE ) { feedManager.deleteDownloadedEnclosure(enclosure); mItemsAdapter.update(); return true; } else if( menuItem.getItemId() == MENU_ITEM_SHOWNOTES ) { mCurrentItem = item.mId; mShownotesView.loadDataWithBaseURL(null, item.mDescription, "text/html", "utf-8", null); showNext(); return true; } else if( menuItem.getItemId() == MENU_ITEM_DOWNLOAD ) { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "podcasts activity - adding download"); feedManager.addDownload(item, enclosure); FeedService.downloadAdded(this); return true; } else { return false; // not handled } } private boolean onPodcastContextItemSelected(MenuItem menuItem) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo(); int position = info.position; long feedId = mPodcastsAdapter.getFeedId(position); if( menuItem.getItemId() == MENU_ITEM_DELETE_FEED ) { mSelectedFeedId = feedId; showDialog(DIALOG_CONFIRM_DELETE_ID); return true; } else if( menuItem.getItemId() == MENU_ITEM_RENAME_FEED ) { mSelectedFeedId = feedId; showDialog(DIALOG_RENAME_FEED_ID); return true; } else if( menuItem.getItemId() == MENU_ITEM_REFRESH_FEED ) { FeedService.updateFeed(this, feedId); return true; } else { return false; // not handled } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); if( v == mItemsListView ) { int position = mOverrideContextMenuPosition; if( position < 0 ) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; position = info.position; } long enclosureId = mItemsAdapter.getEnclosureId(position); long itemId = mItemsAdapter.getFeedItemId(position); FeedManager feedManager = FeedManager.getInstance(this); FeedItem item = feedManager.getItemById(itemId); Enclosure enclosure = feedManager.getEnclosure(enclosureId); feedManager.verifyEnclosure(enclosure); menu.setHeaderTitle(item.mCleanTitle); if( enclosure.mDownloadTime > 0 ) { menu.add(0, MENU_ITEM_PLAY, 0, R.string.menu_play); menu.add(0, MENU_ITEM_ADD_PLAYLIST, 1, R.string.menu_add_playlist); menu.add(0, MENU_ITEM_DELETE_FILE, 2, R.string.menu_delete_download); } else { String downloadString; if( enclosure.mLength > 0 ) { downloadString = getResources().getString(R.string.menu_download_size, Utilities.formatFileSize( enclosure.mLength ) ); } else { downloadString = getResources().getString(R.string.menu_download); } menu.add(0, MENU_ITEM_DOWNLOAD, 0, downloadString ); menu.getItem(menu.size() - 1).setEnabled( ! feedManager.isDownloading( enclosure ) ); menu.add(0, MENU_ITEM_STREAM, 1, R.string.menu_stream); menu.add(0, MENU_ITEM_ADD_PLAYLIST_STREAM, 2, R.string.menu_add_playlist_stream); } menu.add(0, MENU_ITEM_PLAY_EXTERNAL, 9, R.string.menu_play_external); menu.add(0, MENU_ITEM_DELETE, 10, R.string.menu_delete); menu.add(0, MENU_ITEM_SHOWNOTES, 12, R.string.menu_shownotes); if( (item.mFlags & FeedItem.FLAG_READ) == 0 ) { menu.add(0, MENU_ITEM_MARK_READ, 13, R.string.menu_mark_read); } else { menu.add(0, MENU_ITEM_MARK_UNREAD, 13, R.string.menu_mark_unread); } } else if( v == mPodcastsListView ) { menu.add(0, MENU_ITEM_REFRESH_FEED, 0, R.string.menu_refresh_feed); menu.add(0, MENU_ITEM_DELETE_FEED, 0, R.string.menu_delete_feed); menu.add(0, MENU_ITEM_RENAME_FEED, 0, R.string.menu_rename_feed); } } @Override protected Dialog onCreateDialog(int id) { Dialog dialog = null; AlertDialog.Builder builder = new AlertDialog.Builder(this); switch(id) { case DIALOG_CONFIRM_DELETE_ID: builder.setMessage(R.string.dialog_confirm_delete_feed) .setCancelable(false) .setPositiveButton(R.string.dialog_yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { deleteFeed(); } }) .setNegativeButton(R.string.dialog_no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); dialog = builder.create(); break; case DIALOG_RENAME_FEED_ID: { LayoutInflater factory = LayoutInflater.from(this); final View textEntryView = factory.inflate(R.layout.dialog_rename_feed, null); builder.setTitle(R.string.dialog_rename_feed) .setView(textEntryView) .setCancelable(true) .setPositiveButton(R.string.dialog_rename, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { EditText text = (EditText) textEntryView.findViewById(R.id.txt_name); renameFeed(text.getText().toString()); } }) .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); dialog = builder.create(); } break; } return dialog; } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch(id) { case DIALOG_RENAME_FEED_ID: { EditText text = (EditText) dialog.findViewById(R.id.txt_name); text.setText(mPodcastsAdapter.getFeedNameById(mSelectedFeedId)); text.selectAll(); } break; } super.onPrepareDialog(id, dialog); } private void deleteFeed() { FeedManager feedManager = FeedManager.getInstance(this); Feed feed = feedManager.getFeed(mSelectedFeedId); feedManager.deleteFeed( feed, true ); mItemsAdapter.update(); mPodcastsAdapter.update(); } private void renameFeed(String newName) { if(! newName.equals("")) { FeedManager feedManager = FeedManager.getInstance(this); feedManager.setFeedName(mSelectedFeedId, newName); mItemsAdapter.update(); mPodcastsAdapter.update(); } } @Override public void onContextMenuClosed(Menu menu) { super.onContextMenuClosed(menu); mOverrideContextMenuPosition = -1; } private void playVideo(FeedItem item, Enclosure enclosure) { if( Globals.LOGGING ) { Log.d(Globals.LOG_TAG, "playing video enclosure - itemId: " + item.mId + " enclosure id: " + enclosure.mId); Log.d(Globals.LOG_TAG, "playing video enclosure - path: " + enclosure.mDownloadPath + " enclosure download: " + enclosure.mDownloadTime); Log.d(Globals.LOG_TAG, "playing video enclosure - item name: " + item.mCleanTitle + " enclosure download: " + enclosure.mDownloadTime); } Intent intent = new Intent(this, PlayVideoActivity.class); if( enclosure.mDownloadTime > 0 ) { intent.putExtra(PlayVideoActivity.EXTRA_PATH, enclosure.mDownloadPath); } else { intent.putExtra(PlayVideoActivity.EXTRA_URL, enclosure.mURL); } intent.putExtra(PlayVideoActivity.EXTRA_SEEK_TO, enclosure.mPosition); intent.putExtra(PlayVideoActivity.EXTRA_ENCLOSURE_ID, enclosure.mId); intent.putExtra(PlayVideoActivity.EXTRA_ITEM_ID, item.mId); startActivity( intent ); } private void playAudio(FeedItem item, Enclosure enclosure) { if( Globals.LOG.d() ) { Globals.LOG.d("playing audio enclosure - itemId: " + item.mId + " enclosure id: " + enclosure.mId); Globals.LOG.d("playing audio enclosure - path: " + enclosure.mDownloadPath + " enclosure download: " + enclosure.mDownloadTime); Globals.LOG.d("playing audio enclosure - item name: " + item.mCleanTitle + " enclosure download: " + enclosure.mDownloadTime); } AudioPlayer player = AudioPlayer.getInstance(this); Intent intent = new Intent(this, PlayAudioActivity.class); if( player.isPlaying() && player.getItemId() == item.mId ) { intent.putExtra(PlayAudioActivity.EXTRA_FROM_NOTIFICATION, true); } else { if( enclosure.mDownloadTime > 0 ) { intent.putExtra(PlayAudioActivity.EXTRA_PATH, enclosure.mDownloadPath); } else { intent.putExtra(PlayAudioActivity.EXTRA_URL, enclosure.mURL); } intent.putExtra(PlayAudioActivity.EXTRA_SEEK_TO, enclosure.mPosition); intent.putExtra(PlayAudioActivity.EXTRA_ENCLOSURE_ID, enclosure.mId); intent.putExtra(PlayAudioActivity.EXTRA_ITEM_ID, item.mId); } startActivity( intent ); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(KEY_CURRENT_VIEW, mViewFlipper.getDisplayedChild() ); outState.putLong(KEY_CURRENT_FEED, mItemsAdapter.getFeedId()); outState.putLong(KEY_CURRENT_ITEM, mCurrentItem); outState.putInt(KEY_FIRST_ITEM, mItemsListView.getFirstVisiblePosition()); outState.putInt(KEY_FIRST_FEED, mPodcastsListView.getFirstVisiblePosition()); } private void updateTitle() { int child = mViewFlipper.getDisplayedChild(); String newTitle = getResources().getString(R.string.app_name); if (child == 1 ) { newTitle += ": " + mItemsAdapter.getFeedName(); } Activity parent = getParent(); if( parent != null ) { parent.getWindow().setTitle( newTitle ); } else { getWindow().setTitle( newTitle ); } } private void onTimer() { int timeout = updateDisplay(); Globals.LOG.v("PodcastsActivity updateDisplay: timeout: " + timeout); mHandler.removeCallbacks(mUpdateTask); if(timeout > 0 ) { mHandler.postDelayed(mUpdateTask, timeout); } } private int updateDisplay() { AudioPlayer player = AudioPlayer.getInstance(this); if( ! player.hasStarted() ) { return -1; } if( ! player.isPaused() ) { int position = player.getCurrentPosition(); long itemId = player.getItemId(); mItemsAdapter.setItemProgress(itemId, position); int remaining = 1000 - (position % 1000); return remaining; } else { mItemsAdapter.setPlayingItemId(-1); return 500; } } }