/* * 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.IOException; import java.io.InputStream; import net.oddsoftware.android.utils.WorkerThread; import net.oddsoftware.android.utils.WorkerThread.Task; import net.oddsoftware.android.feedscribe.Globals; import net.oddsoftware.android.feedscribe.R; import net.oddsoftware.android.feedscribe.data.Feed; import net.oddsoftware.android.feedscribe.data.FeedConfig; import net.oddsoftware.android.feedscribe.data.FeedItem; import net.oddsoftware.android.feedscribe.data.FeedManager; import net.oddsoftware.android.feedscribe.data.FeedSettings; import net.oddsoftware.android.feedscribe.service.FeedService; import net.oddsoftware.android.html.HttpCache; import net.oddsoftware.android.html.Textify; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import java.text.DateFormat; import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; public class NewsActivity extends Activity { protected MyViewFlipper mViewFlipper; protected ListView mNewsFeedsListView; protected NewsFeedsListAdapter mNewsFeedsListAdapter; protected ListView mNewsItemsListView; protected NewsItemsListAdapter mNewsItemsListAdapter; protected View mNewsView; private boolean mLoading; private long mItemId; private String mItemDescription; private String mItemTitle; private String mItemURL; private long mItemFlags; private String mProcessedData; private WorkerThread mWorkerThread; private Handler mHandler; private static final String KEY_FEED_ID = "key_feed_id"; private static final String KEY_ITEM_ID = "key_item_id"; private static final String KEY_FEEDS_POSITION = "key_feeds_pos"; private static final String KEY_ITEMS_POSITION = "key_items_pos"; private static final String KEY_SUBVIEW = "key_subview"; private static final String KEY_ITEM_URL = "key_item_url"; private static final String KEY_PROCESSED_DATA = "processed_data"; private static final String KEY_SCROLL_PERCENT = "scroll_percent"; private static final int DIALOG_CONFIRM_DELETE_ID = 10; private static final int DIALOG_RENAME_FEED_ID = 11; private static int MENU_ITEM_DELETE_FEED = 10; private static int MENU_ITEM_FEED_SETTINGS = 11; private static int MENU_ITEM_RENAME_FEED = 12; private HttpCache mHttpCache; protected boolean mForceRefresh; private StatusReceiver mStatusReceiver; private long mSelectedFeedId; private WebView mWebView; private int mScrollPercent = -1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler = new Handler(); mHttpCache = new HttpCache(this); // there can be only one if( mWorkerThread == null ) { mWorkerThread = new WorkerThread(); mWorkerThread.start(); } mNewsFeedsListView = new ListView(this); mNewsFeedsListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) { onNewsFeedClicked(position); } }); mNewsFeedsListAdapter = new NewsFeedsListAdapter( this ); mNewsFeedsListView.setAdapter( mNewsFeedsListAdapter ); mNewsItemsListView = new ListView(this); mNewsItemsListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) { onNewsItemClicked(position); } }); mNewsItemsListAdapter = new NewsItemsListAdapter( this ); mNewsItemsListView.setAdapter(mNewsItemsListAdapter); // now set up the viewflipper mViewFlipper = new MyViewFlipper(this); setContentView(mViewFlipper); registerForContextMenu(mNewsFeedsListView); mViewFlipper.addView(mNewsFeedsListView, 0); mViewFlipper.addView(mNewsItemsListView, 1); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); mNewsView = inflater.inflate(R.layout.news_view_activity, null, false); mViewFlipper.addView( mNewsView, 2); mWebView = (WebView) mNewsView.findViewById(R.id.web_view); /* for column view webview.getSettings().setJavaScriptEnabled(true); webview.getSettings().setUseWideViewPort(true); */ mWebView.setWebChromeClient( new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { setPageLoadProgress(newProgress); } }); /* for column view webview.setWebViewClient( new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // Column Count is just the number of 'screens' of text. Add one for partial 'screens' int columnCount = (int) (view.getContentHeight() * 1.5 / view.getWidth() ); if( columnCount <= 0 ) { columnCount = 1; } Log.d(Globals.LOG_TAG, "height " + view.getContentHeight() + " width " + view.getWidth() + " column count " + columnCount); // Must be expressed as a percentage. If not set then the WebView will not stretch to give the desired effect. int columnWidth = columnCount * 100; String js = "var d = document.getElementsByTagName('body')[0];"; js += "d.style.WebkitColumnCount=" + columnCount + ";"; js += "d.style.WebkitColumnWidth='" + (columnWidth) + "%';"; js += "d.style.width='" + (columnWidth) + "%';"; Log.d(Globals.LOG_TAG, "js is " + js); view.loadUrl("javascript:(function(){" + js + "})()"); } }); */ mScrollPercent = -1; if( savedInstanceState != null ) { int subView = savedInstanceState.getInt(KEY_SUBVIEW, 0); int feedsPosition = savedInstanceState.getInt(KEY_FEEDS_POSITION, 0); int itemsPosition = savedInstanceState.getInt(KEY_ITEMS_POSITION, 0); long feedId = savedInstanceState.getLong(KEY_FEED_ID, 0); long itemId = savedInstanceState.getLong(KEY_ITEM_ID, 0); if( subView == 0 ) { mNewsFeedsListView.setSelection(feedsPosition); } if( subView >= 1 ) { int pos = mNewsFeedsListAdapter.getPosition(feedId); mNewsFeedsListView.setSelection(pos); mNewsItemsListAdapter.setFeedId( feedId ); mNewsItemsListView.setSelection( itemsPosition ); } mItemURL = savedInstanceState.getString(KEY_ITEM_URL); mProcessedData = savedInstanceState.getString(KEY_PROCESSED_DATA); FeedItem item = FeedManager.getInstance(this).getItemById(itemId); if( item != null ) { mItemFlags = item.mFlags; } int scrollPercent = savedInstanceState.getInt(KEY_SCROLL_PERCENT, -1); if( subView >= 2 ) { showNewsItem(itemId, scrollPercent); } if( subView >= 0 && subView <= 2 ) { mViewFlipper.setDisplayedChild( subView ); } } } protected void setPageLoadProgress(int newProgress) { setProgressBarVisibility(true); setProgress(newProgress * 100); if(newProgress == 100 && mScrollPercent > 0) { mHandler.postDelayed(new Runnable(){ @Override public void run() { int scrollPos = (int)( mWebView.getContentHeight() * mWebView.getScale() * mScrollPercent / 1000); Globals.LOG.d("scrolling to " + scrollPos + " of " + mWebView.getContentHeight() * mWebView.getScale() ); mWebView.scrollTo(0, scrollPos); }}, 500); // 500ms delay } Activity parent = getParent(); if( parent != null) { setProgressBarVisibility(true); parent.setProgress(newProgress * 100); } } @Override protected void onPause() { unregisterReceiver(mStatusReceiver); super.onPause(); } @Override protected void onResume() { IntentFilter statusFilter = new IntentFilter(FeedService.STATUS_UPDATE); mStatusReceiver = new StatusReceiver(); registerReceiver(mStatusReceiver, statusFilter); mNewsFeedsListAdapter.update(); mNewsItemsListAdapter.update(); updateTitle(); super.onResume(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(KEY_SUBVIEW, mViewFlipper.getDisplayedChild() ); outState.putLong(KEY_ITEM_ID, mItemId); outState.putLong(KEY_FEED_ID, mNewsItemsListAdapter.getFeedId() ); outState.putInt(KEY_FEEDS_POSITION, mNewsFeedsListView.getFirstVisiblePosition() ); outState.putInt(KEY_ITEMS_POSITION, mNewsItemsListView.getFirstVisiblePosition() ); outState.putString(KEY_ITEM_URL, mItemURL); outState.putString(KEY_PROCESSED_DATA, mProcessedData); if(mViewFlipper.getDisplayedChild() == 2) { int height = ((int)(mWebView.getContentHeight() * mWebView.getScale())); if( height > 0 ) { outState.putInt(KEY_SCROLL_PERCENT, mWebView.getScrollY()* 1000 / height); } } } private void onNewsFeedClicked(int position) { if( Globals.LOGGING) Log.d(Globals.LOG_TAG, "onNewsFeedClicked"); mNewsItemsListAdapter.setFeedId( mNewsFeedsListAdapter.getItemId(position) ); showNext(); } private void onNewsItemClicked(int position) { if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "onNewsItemClicked"); long itemId = mNewsItemsListAdapter.getItemId(position); showNewsItem( itemId, -1 ); 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 ) { saveNewsPosition(mItemId); mNewsItemsListAdapter.update(); } if( mViewFlipper.getDisplayedChild() == 1 ) { mNewsFeedsListAdapter.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(); } private void showNewsItem(long itemId, int overrideScrollPosition) { FeedManager feedManager = FeedManager.getInstance(this); FeedItem item = feedManager.getItemById(itemId); if( item != null ) { mItemId = item.mId; mItemDescription = item.mCleanDescription; mItemTitle = item.mCleanTitle; // see if mProcessedData may be valid based on matching urls if( ! item.mLink.equals(mItemURL) ) { // not valid mProcessedData = null; } mItemURL = item.mLink; if( (item.mFlags & FeedItem.FLAG_READ) == 0) { item.mFlags |= FeedItem.FLAG_READ; feedManager.updateItemFlags(item); } mItemFlags = item.mFlags; if(overrideScrollPosition < 0) { mScrollPercent = (int) item.mPosition; } else { mScrollPercent = overrideScrollPosition; } loadURL(); updateTitle( ); } } private void saveNewsPosition(long itemId) { FeedManager feedManager = FeedManager.getInstance(this); FeedItem item = feedManager.getItemById(itemId); if( item != null ) { int height = ((int)(mWebView.getContentHeight() * mWebView.getScale())); if( height > 0 ) { item.mPosition = mWebView.getScrollY() * 1000 / height; Globals.LOG.d("saving news item " + itemId + " position as " + item.mPosition + " scrollY " + mWebView.getScrollY() + " height " + mWebView.getContentHeight() + " height " + mWebView.getHeight() + " scale " + mWebView.getScale() ); feedManager.updateItem(item); } } } private void loadURL() { setPageLoadProgress(1); FeedManager feedManager = FeedManager.getInstance(this); final long itemId = mItemId; final FeedItem item = feedManager.getItemById(mItemId); long feedId = 0; if( item != null ) { feedId = item.mFeedId; } final FeedSettings feedSettings = feedManager.getFeedSettings(feedId); final Feed feed = feedManager.getFeed(feedId); final boolean textifyEnabled = (feedSettings == null || feedSettings.mTextify == true); final boolean displayFullArticle = (feedSettings != null && feedSettings.mDisplayFullArticle == true); mLoading = true; mWorkerThread.addTask(new Task(){ String processedData = null; String processedTitle = null; String finalUrl = null; @Override public void doInBackground() { super.doInBackground(); // try using cached processed data if( mProcessedData != null ) { processedData = mProcessedData; return; } try { // j if( displayFullArticle && ! textifyEnabled ) { InputStream is = mHttpCache.getResource(mItemURL, mForceRefresh); mForceRefresh = false; if( is != null ) { java.io.Reader reader = new java.io.InputStreamReader(is); StringBuilder builder = new StringBuilder(4096); int count = 0; char[] buf = new char[1024]; while( (count = reader.read(buf)) != -1 ) { builder.append(buf, 0, count); if( builder.length() > (1024 * 1024) ) { if(Globals.LOGGING)Log.e(Globals.LOG_TAG, "error downloading full article, more than 1mb"); break; } } processedData = builder.toString(); } else if( item != null) { if(Globals.LOGGING) Log.e(Globals.LOG_TAG, "error retrieving article from cache, using rss content instead"); processedData = item.mDescription; } if(Globals.LOGGING)Log.d(Globals.LOG_TAG, "download article size is " + processedData.length() ); return; } Textify textify = new Textify(); // for column view //textify.setViewport("width=400, user-scalable=no"); if( mItemTitle.length() > 0 && !mItemTitle.equals(mItemURL)) { textify.setTitle(mItemTitle); } if( item != null ) { String author = item.mAuthor; if( author.length() == 0 ) { author = feed.mName; } textify.setAuthor(author); textify.setPubDate( DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(item.mPubDate) ); } if( displayFullArticle ) { InputStream is = mHttpCache.getResource(mItemURL, mForceRefresh); mForceRefresh = false; if( is != null ) { textify.process(is); } else if( item != null ) { if(Globals.LOGGING) Log.e(Globals.LOG_TAG, "error retrieving article from cache, using rss content instead"); textify.process(item.mDescription); } } else if( item != null ) { if( ! textifyEnabled ) { textify.setProcessingEnabled( false ); } textify.process(item.mDescription); } processedTitle = textify.getTitle(); processedData = textify.getProcessedArticle(); if( Globals.LOGGING ) Log.d(Globals.LOG_TAG, "displaying article: " + processedData); // this stuff should really go somewhere else, but this is the 2-stage article filter // if there was nothing interesting in the first pass, disable gross biases and try again if( processedData.length() < 1000 || textify.getArticleScore() < 10 ) { textify.setStripUnlikelyCandidates( false ); if( displayFullArticle ) { InputStream is = mHttpCache.getResource(mItemURL, mForceRefresh); mForceRefresh = false; if( is != null ) { textify.process(is); } else { if(Globals.LOGGING) Log.e(Globals.LOG_TAG, "error retrieving article from cache, using rss content instead"); textify.process(item.mDescription); } } else { textify.process(item.mDescription); } processedData = textify.getProcessedArticle(); } // this time there was still nothing, so just output the whole article if( processedData.length() < 1000 ) { textify.setProcessingEnabled( false ); if( displayFullArticle ) { InputStream is = mHttpCache.getResource(mItemURL, mForceRefresh); mForceRefresh = false; if( is != null ) { textify.process(is); } else { if(Globals.LOGGING) Log.e(Globals.LOG_TAG, "error retrieving article from cache, using rss content instead"); textify.process(item.mDescription); } } else { textify.process(item.mDescription); } processedData = textify.getProcessedArticle(); } finalUrl = mHttpCache.getLastUrl(mItemURL); } catch(IOException exc) { if( Globals.LOGGING) Log.e(Globals.LOG_TAG, "Error loading url", exc); } } @Override public void onPostExecute() { super.onPostExecute(); mWebView.getSettings().setBuiltInZoomControls( FeedConfig.getInstance(NewsActivity.this).getShowZoomControls() ); if( itemId != mItemId ) { if( Globals.LOGGING ) Log.w(Globals.LOG_TAG, "NewsViewActivity - loadURL complete but id has changed"); return; } if( processedData == null ) { if( Globals.LOGGING ) Log.w(Globals.LOG_TAG, "NewsViewActivity - loadURL complete - no data, using original url"); // show original mWebView.loadUrl(mItemURL); mProcessedData = ""; } else { mProcessedData = processedData; String baseUrl = mItemURL; if( finalUrl != null && finalUrl.length() > 0 ) { baseUrl = finalUrl; } mWebView.loadDataWithBaseURL(baseUrl, processedData, "text/html", "utf-8", null); } if( processedTitle != null && processedTitle.length() > 0) { FeedManager feedManager = FeedManager.getInstance(NewsActivity.this); FeedItem item = feedManager.getItemById(mItemId); if( item.mCleanTitle.equals(item.mOriginalLink) || item.mCleanTitle.length() == 0 ) { item.mTitle = processedTitle; StringBuilder titleBuilder = new StringBuilder(processedTitle.length()); feedManager.htmlUnescapeInto( new StringBuilder(processedTitle), titleBuilder); processedTitle = titleBuilder.toString(); item.mCleanTitle = processedTitle; feedManager.updateItem(item); } mItemTitle = processedTitle; updateTitle(); } mLoading = false; } }); } public void onPrev(View view) { if( mLoading ) { return; } long itemId = mNewsItemsListAdapter.getPreviousItem(mItemId); if( itemId > 0) { showNewsItem(itemId, -1); } } public void onNext(View view) { if( mLoading ) { return; } long itemId = mNewsItemsListAdapter.getNextItem(mItemId); if( itemId > 0) { showNewsItem(itemId, -1); } } public void launchNewsItem(long itemId) { mViewFlipper.setDisplayedChild(2); showNewsItem(itemId, -1); FeedManager feedManager = FeedManager.getInstance(this); Feed feed = feedManager.getFeedByItemId( itemId ); if( feed != null ) { mNewsItemsListAdapter.setFeedId(feed.mId); } } private void updateTitle() { int child = mViewFlipper.getDisplayedChild(); String newTitle = getResources().getString(R.string.app_name); if( child == 2 ) { newTitle += ": " + mItemTitle; } else if (child == 1 ) { newTitle += ": " + mNewsItemsListAdapter.getFeedName(); } Activity parent = getParent(); if( parent != null ) { parent.getWindow().setTitle( newTitle ); } else { getWindow().setTitle( newTitle ); } } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); Log.e(Globals.LOG_TAG, "NewsActivity.onPrepareOptionsMenu"); menu.removeItem(R.id.open_original); menu.removeItem(R.id.share_original); menu.removeItem(R.id.add_star); menu.removeItem(R.id.remove_star); menu.removeItem(R.id.delete); menu.removeItem(R.id.keep_unread); menu.removeItem(R.id.refresh_article); menu.removeItem(R.id.mark_all_read); menu.removeItem(R.id.delete_all_read); menu.removeItem(R.id.refresh_feed); if( mViewFlipper.getDisplayedChild() == 2) { menu.removeItem(R.id.add); menu.removeItem(R.id.refresh); menu.removeItem(R.id.info); menu.removeItem(R.id.preferences); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.news_item_view_context_menu, menu); if( (mItemFlags & FeedItem.FLAG_STARRED) == 0 ) { menu.removeItem(R.id.remove_star); } else { menu.removeItem(R.id.add_star); } } else if( mViewFlipper.getDisplayedChild() == 1) { menu.removeItem(R.id.add); menu.removeItem(R.id.refresh); menu.removeItem(R.id.info); menu.removeItem(R.id.preferences); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.news_item_list_options_menu, menu); } return true; } @Override protected void onDestroy() { super.onDestroy(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch( item.getItemId() ) { case R.id.open_original: { Uri uri = Uri.parse( mItemURL ); try { startActivity( new Intent( Intent.ACTION_VIEW, uri )); } catch( ActivityNotFoundException exc ) { Toast.makeText(this, R.string.error_view_url, Toast.LENGTH_LONG).show(); } return true; } case R.id.share_original: { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_SUBJECT, getResources().getString(R.string.app_name) + " - " + mItemTitle); intent.putExtra(Intent.EXTRA_TEXT, mItemDescription + " " + mItemURL ); try { startActivity( Intent.createChooser( intent, getResources().getString(R.string.share_original) ) ); } catch( ActivityNotFoundException exc ) { Toast.makeText(this, R.string.error_share, Toast.LENGTH_LONG).show(); } return true; } case R.id.add_star: { FeedItem feedItem = FeedManager.getInstance(this).getItemById(mItemId); if( feedItem != null ) { feedItem.mFlags |= FeedItem.FLAG_STARRED; FeedManager.getInstance(this).updateItemFlags(feedItem); mItemFlags = feedItem.mFlags; Toast.makeText(this, R.string.star_added, Toast.LENGTH_LONG).show(); } return true; } case R.id.remove_star: { FeedItem feedItem = FeedManager.getInstance(this).getItemById(mItemId); if( feedItem != null ) { feedItem.mFlags &= ~(FeedItem.FLAG_STARRED); FeedManager.getInstance(this).updateItemFlags(feedItem); mItemFlags = feedItem.mFlags; Toast.makeText(this, R.string.star_removed, Toast.LENGTH_LONG).show(); } return true; } case R.id.delete: { // in news view if( mViewFlipper.getDisplayedChild() == 2 ) { FeedItem feedItem = FeedManager.getInstance(this).getItemById(mItemId); if( feedItem != null ) { FeedManager.getInstance(this).deleteFeedItem(feedItem); mNewsItemsListAdapter.update(); showPrevious(); Toast.makeText(this, R.string.item_deleted, Toast.LENGTH_LONG).show(); } return true; } break; } case R.id.keep_unread: { FeedItem feedItem = FeedManager.getInstance(this).getItemById(mItemId); if( feedItem != null ) { feedItem.mFlags &= ~(FeedItem.FLAG_READ); FeedManager.getInstance(this).updateItemFlags(feedItem); mItemFlags = feedItem.mFlags; Toast.makeText(this, R.string.kept_unread, Toast.LENGTH_LONG).show(); } return true; } case R.id.refresh_article: { mForceRefresh = true; mProcessedData = null; showNewsItem(mItemId, 0); return true; } case R.id.mark_all_read: { FeedManager feedManager = FeedManager.getInstance(this); long feedId = mNewsItemsListAdapter.getFeedId(); if( feedId > 0 ) { feedManager.setFeedItemsRead(feedId); mNewsItemsListAdapter.update(); } return true; } case R.id.delete_all_read: { FeedManager feedManager = FeedManager.getInstance(this); long feedId = mNewsItemsListAdapter.getFeedId(); if( feedId > 0 ) { feedManager.deleteFeedItemsRead(feedId); mNewsItemsListAdapter.update(); } return true; } case R.id.refresh_feed: { long feedId = mNewsItemsListAdapter.getFeedId(); if( feedId > 0 ) { FeedService.updateFeed(this, feedId); } return true; } } return super.onOptionsItemSelected(item); } @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(); } } 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(mNewsFeedsListAdapter.getFeedNameById(mSelectedFeedId)); text.selectAll(); } break; } super.onPrepareDialog(id, dialog); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); if( v == mNewsFeedsListView ) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; menu.setHeaderTitle(mNewsFeedsListAdapter.getFeedName( info.position ) ); 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); menu.add(0, MENU_ITEM_FEED_SETTINGS, 0, R.string.menu_feed_settings); } } @Override public boolean onContextItemSelected(MenuItem menuItem) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo(); int position = info.position; long feedId = mNewsFeedsListAdapter.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_FEED_SETTINGS ) { Intent intent = new Intent(this, FeedSettingsActivity.class); intent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID, feedId); startActivity(intent); return true; } else { return super.onContextItemSelected(menuItem); } } private void deleteFeed() { FeedManager feedManager = FeedManager.getInstance(this); Feed feed = feedManager.getFeed(mSelectedFeedId); feedManager.deleteFeed( feed, true ); mNewsFeedsListAdapter.update(); mNewsItemsListAdapter.update(); } private void renameFeed(String newName) { if(! newName.equals("")) { FeedManager feedManager = FeedManager.getInstance(this); feedManager.setFeedName(mSelectedFeedId, newName); mNewsFeedsListAdapter.update(); } } private class StatusReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra("status", FeedService.STATUS_NONE); if (Globals.LOGGING) Log.d(Globals.LOG_TAG, "NewsActivity.StatusReceiver.onReceive: " + status); if( status == FeedService.STATUS_UPDATING ) { if (Globals.LOGGING) Log.d(Globals.LOG_TAG, "NewsActivity.StatusReceiver.onReceive: updating"); } else if ( status == FeedService.STATUS_UPDATE_COMPLETE ) { if (Globals.LOGGING) Log.d(Globals.LOG_TAG, "NewsActivity.StatusReceiver.onReceive: update complete"); mNewsFeedsListAdapter.update(); mNewsItemsListAdapter.update(); if( intent.getIntExtra(FeedService.EXTRA_ERROR, FeedService.ERROR_NONE) == FeedService.ERROR_NETWORK) { Toast.makeText(NewsActivity.this, R.string.error_network, Toast.LENGTH_LONG).show(); } } } } }