Android Open Source - BBC-News-Reader Reader Activity






From Project

Back to project page BBC-News-Reader.

License

The source code is released under:

Copyright (c) 2011, 2012, Digital Lizard (Oscar Key, Thomas Boby) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the...

If you think the Android project BBC-News-Reader 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

/*******************************************************************************
 * BBC News Reader//from w  w  w.  ja  va  2 s .c om
 * Released under the BSD License. See README or LICENSE.
 * Copyright (c) 2011, Digital Lizard (Oscar Key, Thomas Boby)
 * All rights reserved.
 ******************************************************************************/
package com.digitallizard.bbcnewsreader;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.View;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.digitallizard.bbcnewsreader.ServiceManager.MessageReceiver;
import com.digitallizard.bbcnewsreader.data.DatabaseHandler;
import com.digitallizard.bbcnewsreader.fragments.ArticleFragment;
import com.digitallizard.bbcnewsreader.fragments.FrontpageFragment;
import com.digitallizard.bbcnewsreader.fragments.FrontpageFragment.FrontPageClickHandler;

public class ReaderActivity extends SherlockFragmentActivity implements MessageReceiver, FrontPageClickHandler {
  
  /* constants */
  public static final String AD_PUB_ID = "a14f0749c716805";
  
  static final int ACTIVITY_CHOOSE_CATEGORIES = 1;
  
  public static final int DISPLAY_MODE_HANDSET = 0;
  public static final int DISPLAY_MODE_TABLET_LANDSCAPE = 1;
  
  public static final String PREFS_FILE_NAME = "com.digitallizard.bbcnewsreader_preferences";
  public static final int DEFAULT_ITEM_LOAD_LIMIT = 4;
  public static final int DEFAULT_CLEAR_OUT_AGE = 4;
  public static final boolean DEFAULT_LOAD_IN_BACKGROUND = true;
  public static final boolean DEFAULT_RTC_WAKEUP = true;
  public static final String DEFAULT_LOAD_INTERVAL = "1_hour";
  public static final boolean DEFAULT_DISPLAY_FULL_ERROR = false;
  public static final int DEFAULT_CATEGORY_UPDATE_VERSION = 0;
  public static final int CURRENT_CATEGORY_UPDATE_VERSION = 0;
  
  public static final String PREFKEY_LOAD_IN_BACKGROUND = "loadInBackground";
  public static final String PREFKEY_RTC_WAKEUP = "rtcWakeup";
  public static final String PREFKEY_LOAD_INTERVAL = "loadInterval";
  public static final String PREFKEY_CATEGORY_UPDATE_VERSION = "categoryUpdateVersion";
  
  public static final int ERROR_TYPE_GENERAL = 0;
  public static final int ERROR_TYPE_INTERNET = 1;
  public static final int ERROR_TYPE_FATAL = 2;
  
  public static final byte[] NO_THUMBNAIL_URL_CODE = new byte[] { 127 };
  
  /* variables */
  private DatabaseHandler database;
  private ServiceManager service;
  private SharedPreferences settings;
  
  private int currentDisplayMode;
  
  private boolean loadInProgress;
  private long lastLoadTime;
  private boolean errorWasFatal;
  private boolean errorDuringThisLoad;
  private boolean firstRun;
  
  private Dialog errorDialog;
  private Dialog firstRunDialog;
  private Dialog backgroundLoadDialog;
  
  private Menu menu;
  
  public void handleMessage(Message msg) {
    // decide what to do with the message
    switch (msg.what) {
    case ResourceService.MSG_CLIENT_REGISTERED:
      // start a load if we haven't loaded within half an hour
      // TODO make the load time configurable
      long difference = System.currentTimeMillis() - (lastLoadTime * 1000); // the time since the last load
      if (lastLoadTime == 0 || difference > (60 * 60 * 1000)) {
        // don't load if this is the first run
        if(!firstRun) {
          loadData(); // trigger a load
        }
      }
      break;
    case ResourceService.MSG_ERROR:
      Bundle bundle = msg.getData(); // retrieve the data
      errorOccured(bundle.getInt(ResourceService.KEY_ERROR_TYPE), bundle.getString(ResourceService.KEY_ERROR_MESSAGE),
          bundle.getString(ResourceService.KEY_ERROR_ERROR));
      break;
    case ResourceService.MSG_NOW_LOADING:
      loadBegun();
      break;
    case ResourceService.MSG_FULL_LOAD_COMPLETE:
      fullLoadComplete();
      break;
    case ResourceService.MSG_RSS_LOAD_COMPLETE:
      rssLoadComplete();
      break;
    case ResourceService.MSG_UPDATE_LOAD_PROGRESS:
      int totalItems = msg.getData().getInt("totalItems");
      int itemsLoaded = msg.getData().getInt("itemsDownloaded");
      updateLoadProgress(totalItems, itemsLoaded);
      break;
    }
  }
  
  public void onItemClick(int id) {
    // either transition to the article view activity or update the article fragment
    if (currentDisplayMode == DISPLAY_MODE_HANDSET) {
      // launch the article activity
      Intent intent = new Intent(this, ArticleActivity.class);
      intent.putExtra(ArticleActivity.EXTRA_KEY_ITEM_ID, id);
      startActivity(intent);
    }
    else if (currentDisplayMode == DISPLAY_MODE_TABLET_LANDSCAPE) {
      // display the article
      ArticleFragment article = (ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.articleFragment);
      article.displayArticle(id);
      // findViewById(R.id.articleFragment)
    }
  }
  
  public void onCategoryClick(String title) {
    // either transition to the article view activity or update the article fragment
    if (currentDisplayMode == DISPLAY_MODE_HANDSET) {
      // launch the category activity
      Intent intent = new Intent(this, CategoryActivity.class);
      intent.putExtra(CategoryActivity.EXTRA_CATEGORY_TITLE, title);
      startActivity(intent);
    }
  }
  
  void errorOccured(int type, String msg, String error) {
    // check if we need to fill in the error messages
    if (msg == null) {
      msg = "null";
    }
    if (error == null) {
      error = "null";
    }
    
    // check if we need to shutdown after displaying the message
    if (type == ERROR_TYPE_FATAL) {
      errorWasFatal = true;
    }
    
    // show a user friendly message or just the error
    if (settings.getBoolean("displayFullError", DEFAULT_DISPLAY_FULL_ERROR)) {
      showErrorDialog("Error: " + error);
    }
    else {
      // display a user friendly message
      if (type == ERROR_TYPE_FATAL) {
        showErrorDialog("Fatal error:\n" + msg + "\nPlease try resetting the app.");
        Log.e("BBC News Reader", "Fatal error: " + msg);
        Log.e("BBC News Reader", error);
      }
      else if (type == ERROR_TYPE_GENERAL) {
        showErrorDialog("Error:\n" + msg);
        Log.e("BBC News Reader", "Error: " + msg);
        Log.e("BBC News Reader", error);
      }
      else if (type == ERROR_TYPE_INTERNET) {
        // only allow one internet error per load
        if (!errorDuringThisLoad) {
          errorDuringThisLoad = true;
          showErrorDialog("Please check your internet connection.");
        }
        Log.e("BBC News Reader", "Error: " + msg);
        Log.e("BBC News Reader", error);
      }
    }
  }
  
  void showErrorDialog(String error) {
    // only show the error dialog if one isn't already visible
    if (errorDialog == null) {
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setMessage(error);
      builder.setCancelable(false);
      builder.setPositiveButton("Close", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
          closeErrorDialog();
        }
      });
      errorDialog = builder.create();
      errorDialog.show();
    }
  }
  
  void closeErrorDialog() {
    errorDialog = null; // destroy the dialog
    // see if we need to end the program
    if (errorWasFatal) {
      // crash out
      // Log.e("BBC News Reader", "Oops something broke. We'll crash now.");
      System.exit(1); // closes the app with an error code
    }
  }
  
  void showFirstRunDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    String message = "Choose the categories you are interested in. \n\n"
        + "The fewer categories enabled the lower data usage and the faster loading will be.";
    builder.setMessage(message);
    builder.setCancelable(false);
    builder.setPositiveButton("Choose", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        closeFirstRunDialog();
        // show the category chooser
        showCategoryChooser();
      }
    });
    firstRunDialog = builder.create();
    firstRunDialog.show();
  }
  
  void closeFirstRunDialog() {
    firstRunDialog = null; // destroy the dialog
  }
  
  void showBackgroundLoadDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    String message = "Load news in the background? \n\n" + "This could increase data usage but will reduce load times.\n\n"
        + "If you wish to use the widget, this should be switched on.";
    builder.setMessage(message);
    builder.setCancelable(false);
    builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        closeBackgroundLoadDialog();
        firstRun = false; // we have finished the first run
        // save the selected option
        Editor editor = settings.edit();
        editor.putBoolean("loadInBackground", true);
        editor.commit();
      }
    });
    builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        closeBackgroundLoadDialog();
        firstRun = false; // we have finished the first run
        // save the selected option
        Editor editor = settings.edit();
        editor.putBoolean("loadInBackground", false);
        editor.commit();
      }
    });
    backgroundLoadDialog = builder.create();
    backgroundLoadDialog.show();
  }
  
  void closeBackgroundLoadDialog() {
    backgroundLoadDialog = null;
  }
  
  void updateLoadProgress(int totalItems, int itemsLoaded) {
    setStatusText("Preloading " + itemsLoaded + " of " + totalItems + " items");
  }
  
  void setLastLoadTime(long time) {
    lastLoadTime = time; // store the time
    // display the new time to the user
    // check if the time is set
    if (!loadInProgress) {
      if (lastLoadTime == 0) {
        // say we have never loaded
        setStatusText("Never updated.");
      }
      else {
        // set the text to show date and time
        String status = "Updated ";
        // find out time since last load in milliseconds
        long difference = System.currentTimeMillis() - (time * 1000); // the time since the last load
        // if within 1 hour, display minutes
        if (difference < (1000 * 60 * 60)) {
          int minutesAgo = (int) Math.floor((difference / 1000) / 60);
          if (minutesAgo == 0) {
            status += "just now";
          }
          else if (minutesAgo == 1) {
            status += minutesAgo + " min ago";
          }
          else {
            status += minutesAgo + " mins ago";
          }
        }
        else {
          // if we are within 24 hours, display hours
          if (difference < (1000 * 60 * 60 * 24)) {
            int hoursAgo = (int) Math.floor(((difference / 1000) / 60) / 60);
            if (hoursAgo == 1) {
              status += hoursAgo + " hour ago";
            }
            else {
              status += hoursAgo + " hours ago";
            }
          }
          else {
            // if we are within 2 days, display yesterday
            if (difference < (1000 * 60 * 60 * 48)) {
              status += "yesterday";
            }
            else {
              // we have not updated recently
              status += "ages ago";
              // TODO more formal message?
            }
          }
        }
        setStatusText(status);
      }
    }
  }
  
  void setStatusText(String text) {
    // set the text to the action bar
    getSupportActionBar().setTitle(text);
  }
  
  void loadBegun() {
    loadInProgress = true; // flag the data as being loaded
    // show the loading image on the button
    // TODO update the load button
    // tell the user what is going on
    setStatusText("Loading feeds...");
  }
  
  void loadData() {
    // check we aren't currently loading news
    if (!loadInProgress) {
      // TODO display old news as old
      // tell the service to load the data
      service.sendMessageToService(ResourceService.MSG_LOAD_DATA);
      errorDuringThisLoad = false;
      
      // hide the refresh button and show the stop button
      menu.findItem(R.id.menuItemRefresh).setVisible(false);
      menu.findItem(R.id.menuItemStop).setVisible(true);
    }
  }
  
  void stopDataLoad() {
    // check we are actually loading news
    if (loadInProgress) {
      errorDuringThisLoad = false;
      // send a message to the service to stop it loading the data
      service.sendMessageToService(ResourceService.MSG_STOP_DATA_LOAD);
      
      // hide the stop button and show the refresh button
      menu.findItem(R.id.menuItemRefresh).setVisible(true);
      menu.findItem(R.id.menuItemStop).setVisible(false);
    }
  }
  
  void fullLoadComplete() {
    // check we are actually loading news
    if (loadInProgress) {
      loadInProgress = false;
      
      // hide the stop button and show the refresh button
      menu.findItem(R.id.menuItemRefresh).setVisible(true);
      menu.findItem(R.id.menuItemStop).setVisible(false);
      
      // report the loaded status
      setLastLoadTime(settings.getLong("lastLoadTime", 0)); // set the time as unix time
      
      // tell the database to delete old items
      database.clearOld();
    }
  }
  
  void rssLoadComplete() {
    // check we are actually loading news
    if (loadInProgress) {
      // tell the user what is going on
      setStatusText("Loading items...");
    }
  }
  
  void showCategoryChooser() {
    // launch the category chooser activity
    Intent intent = new Intent(this, CategoryChooserActivity.class);
    startActivityForResult(intent, ACTIVITY_CHOOSE_CATEGORIES);
  }
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    loadInProgress = false;
    lastLoadTime = 0;
    
    // load the preferences system
    settings = getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE);
    loadSettings();
    
    // load the database, init it if required
    database = new DatabaseHandler(this);
    firstRun = false;
    if (!database.isCreated()) {
      // add the categories and set the version to prevent this happening twice
      database.addCategoriesFromXml();
      Editor editor = settings.edit();
      editor.putInt(PREFKEY_CATEGORY_UPDATE_VERSION, CURRENT_CATEGORY_UPDATE_VERSION);
      editor.commit();
      
      // proceed with the first run
      firstRun = true;
      showFirstRunDialog();
    }
    
    // check if an update is required, if the stored category version is less than the current one
    if(settings.getInt(PREFKEY_CATEGORY_UPDATE_VERSION, DEFAULT_CATEGORY_UPDATE_VERSION) 
        < CURRENT_CATEGORY_UPDATE_VERSION) {
      // run an update
      database.updateCategoriesFromXml();
      
      // set the preference value to the current version
      Editor editor = settings.edit();
      editor.putInt(PREFKEY_CATEGORY_UPDATE_VERSION, CURRENT_CATEGORY_UPDATE_VERSION);
      editor.commit();
    }
    
    // bind the service
    service = new ServiceManager(this, this);
    service.doBindService();
    
    Eula.show(this); // show the eula
    
    // determine which display mode is currently active
    if (getResources().getBoolean(R.bool.screen_xlarge)) {
      currentDisplayMode = DISPLAY_MODE_TABLET_LANDSCAPE;
    }
    else {
      currentDisplayMode = DISPLAY_MODE_HANDSET;
    }
    
    // do specific configuration for various screen sizes
    if (currentDisplayMode == DISPLAY_MODE_TABLET_LANDSCAPE) {
      // force landscape
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }
    
    // hide the up button
    this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
    
    // create the ui
    this.setContentView(R.layout.reader_activity);
  }
  
  @Override
  public void onResume() {
    super.onResume();
    // update the last loaded display
    setLastLoadTime(lastLoadTime);
    // TODO update display more often?
  }
  
  @Override
  protected void onDestroy() {
    // unbind from the service
    service.doUnbindService();
    super.onDestroy(); // pass the destroy command to the super
  }
  
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    // inflate the menu
    getSupportMenuInflater().inflate(R.menu.main_menu, menu);
    
    this.menu = menu; // store a reference for later
    
    return true; // we have made the menu so we can return true
  }
  
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.menuItemCategories) {
      // launch the category chooser activity
      showCategoryChooser();
      return true;
    }
    else if (item.getItemId() == R.id.menuItemSettings) {
      // show the settings menu
      Intent intent = new Intent(this, SettingsActivity.class);
      startActivity(intent);
      return true;
    }
    else if (item.getItemId() == R.id.menuItemRefresh) {
      loadData();
      return true;
    }
    else if (item.getItemId() == R.id.menuItemStop) {
      stopDataLoad();
      return true;
    }
    else {
      return super.onOptionsItemSelected(item);
    }
  }
  
  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // wait for activities to send us result data
    switch (requestCode) {
    case ACTIVITY_CHOOSE_CATEGORIES:
      // check the request was a success
      if (resultCode == RESULT_OK) {
        
        // refresh the display
        loadData(); // make sure selected categories are loaded
        FrontpageFragment frontpage = (FrontpageFragment) getSupportFragmentManager().findFragmentById(R.id.frontpageFragment);
        frontpage.createNewsDisplay(getLayoutInflater(), frontpage.getView()); // reload the ui
        
        // check for a first run
        if (firstRun) {
          showBackgroundLoadDialog();
        }
      }
      break;
    }
  }
  
  void loadSettings() {
    // check the settings file exists
    if (settings != null) {
      // load values from the settings
      setLastLoadTime(settings.getLong("lastLoadTime", 0)); // sets to zero if not in preferences
    }
  }
  
  public void refreshClicked(View item) {
    // start the load if we are not loading
    if (!loadInProgress) {
      loadData();
    }
    else {
      stopDataLoad();
    }
  }
  
  
  public int getCurrentDisplayMode() {
    return currentDisplayMode;
  }
}




Java Source Code List

com.digitallizard.bbcnewsreader.ArticleActivity.java
com.digitallizard.bbcnewsreader.CategoryActivity.java
com.digitallizard.bbcnewsreader.CategoryChooserActivity.java
com.digitallizard.bbcnewsreader.CategoryChooserAdapter.java
com.digitallizard.bbcnewsreader.CategoryPagerAdapter.java
com.digitallizard.bbcnewsreader.Eula.java
com.digitallizard.bbcnewsreader.ItemAdapter.java
com.digitallizard.bbcnewsreader.ItemLayout.java
com.digitallizard.bbcnewsreader.Item.java
com.digitallizard.bbcnewsreader.RSSManager.java
com.digitallizard.bbcnewsreader.ReaderActivity.java
com.digitallizard.bbcnewsreader.ResourceInterface.java
com.digitallizard.bbcnewsreader.ResourceService.java
com.digitallizard.bbcnewsreader.ServiceManager.java
com.digitallizard.bbcnewsreader.SettingsActivity.java
com.digitallizard.bbcnewsreader.data.DatabaseHandler.java
com.digitallizard.bbcnewsreader.data.DatabaseHelper.java
com.digitallizard.bbcnewsreader.data.DatabaseProvider.java
com.digitallizard.bbcnewsreader.data.ItemClearer.java
com.digitallizard.bbcnewsreader.data.WrapBackwards.java
com.digitallizard.bbcnewsreader.fragments.ArticleFragment.java
com.digitallizard.bbcnewsreader.fragments.CategoryChooserFragment.java
com.digitallizard.bbcnewsreader.fragments.CategoryFragment.java
com.digitallizard.bbcnewsreader.fragments.FrontpageFragment.java
com.digitallizard.bbcnewsreader.resource.web.HtmlParser.java
com.digitallizard.bbcnewsreader.resource.web.ImageDownloader.java
com.digitallizard.bbcnewsreader.resource.web.QueueItem.java
com.digitallizard.bbcnewsreader.resource.web.WebManager.java
com.digitallizard.bbcnewsreader.widget.ReaderWidget.java
com.digitallizard.bbcnewsreader.widget.WidgetConfigActivity.java
com.hlidskialf.android.preference.SeekBarPreference.java