Clear DB on upgrade. Fix DB query for all folders.

This commit is contained in:
ojiikun 2013-04-26 07:17:09 +00:00
parent 4c711fd094
commit d38a9d5275
7 changed files with 95 additions and 17 deletions

View file

@ -16,6 +16,7 @@ import com.newsblur.fragment.FolderListFragment;
import com.newsblur.fragment.LogoutDialogFragment;
import com.newsblur.fragment.SyncUpdateFragment;
import com.newsblur.service.SyncService;
import com.newsblur.util.PrefsUtils;
import com.newsblur.view.StateToggleButton.StateChangedListener;
public class Main extends NbFragmentActivity implements StateChangedListener, SyncUpdateFragment.SyncUpdateFragmentInterface {
@ -29,6 +30,9 @@ public class Main extends NbFragmentActivity implements StateChangedListener, Sy
@Override
public void onCreate(Bundle savedInstanceState) {
PrefsUtils.checkForUpgrade(this);
requestWindowFeature(Window.FEATURE_PROGRESS);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);

View file

@ -3,6 +3,7 @@ package com.newsblur.database;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.Cursor;
import android.util.Log;
public class BlurDatabase extends SQLiteOpenHelper {

View file

@ -136,8 +136,17 @@ public class DatabaseConstants {
FOLDER_TABLE + "." + FOLDER_ID, FOLDER_TABLE + "." + FOLDER_NAME, " SUM(" + FEED_POSITIVE_COUNT + ") AS " + SUM_POS, " SUM(" + FEED_NEUTRAL_COUNT + ") AS " + SUM_NEUT, " SUM(" + FEED_NEGATIVE_COUNT + ") AS " + SUM_NEG
};
// this union clause lets folder queries also select the "root" folder that should appear whether or not it has unread stories
private static final String FOLDER_UNION_ROOT = " OR " + DatabaseConstants.FOLDER_TABLE + "." + DatabaseConstants.FOLDER_NAME + "='" + AppConstants.ROOT_FOLDER + "'";
// this union clause lets folder queries also select the "root" folder that should appear whether or not
// it has unread stories. Note that this goes *before* the normal ALL_FOLDERS select statement. The zero-valued
// pseudo-columns are safe because said columns are ignored for the root folder.
public static final String FOLDER_UNION_ROOT = "SELECT " +
FOLDER_ID + ", " +
FOLDER_NAME +
", 0 AS " + SUM_POS +
", 0 AS " + SUM_NEUT +
", 0 AS " + SUM_NEG +
" FROM " + FOLDER_TABLE +
" WHERE " + FOLDER_NAME + "='" + AppConstants.ROOT_FOLDER + "' UNION ";
private static final String FOLDER_INTELLIGENCE_ALL = " HAVING SUM(" + DatabaseConstants.FEED_NEGATIVE_COUNT + " + " + DatabaseConstants.FEED_NEUTRAL_COUNT + " + " + DatabaseConstants.FEED_POSITIVE_COUNT + ") >= 0";
private static final String FOLDER_INTELLIGENCE_SOME = " HAVING SUM(" + DatabaseConstants.FEED_NEUTRAL_COUNT + " + " + DatabaseConstants.FEED_POSITIVE_COUNT + ") > 0";
@ -186,19 +195,19 @@ public class DatabaseConstants {
}
/**
* Selection args to filter folders. This always additionally includes the root folder and assumes folders are joined with feed counts.
* Selection args to filter folders.
*/
public static String getFolderSelectionFromState(int state) {
String selection = null;
switch (state) {
case (AppConstants.STATE_ALL):
selection = FOLDER_INTELLIGENCE_ALL + FOLDER_UNION_ROOT;
selection = FOLDER_INTELLIGENCE_ALL;
break;
case (AppConstants.STATE_SOME):
selection = FOLDER_INTELLIGENCE_SOME + FOLDER_UNION_ROOT;
selection = FOLDER_INTELLIGENCE_SOME;
break;
case (AppConstants.STATE_BEST):
selection = FOLDER_INTELLIGENCE_BEST + FOLDER_UNION_ROOT;
selection = FOLDER_INTELLIGENCE_BEST;
break;
}
return selection;

View file

@ -12,10 +12,18 @@ import android.util.Log;
import com.newsblur.util.AppConstants;
/**
* A magic subclass of ContentProvider that enhances calls to the DB for presumably more simple caller syntax.
*
* TODO: the fact that most of the app uses this subclass of ContentProvider cast as such may
* deepy confuse future maintainers as to why the methods within magically do far, far more
* than suggested by the normal contract and provided args. When time and resources permit,
* this paradigm could be replaced with a much more straightforward if slightly more verbose
* use of Plain Old Raw Queries. Alternatively, the DB could be renormalized so that it is not
* necessary to use queries of such intense complexity.
*/
public class FeedProvider extends ContentProvider {
private static final String TAG = "FeedProvider";
public static final String AUTHORITY = "com.newsblur";
public static final String VERSION = "v1";
@ -67,7 +75,8 @@ public class FeedProvider extends ContentProvider {
private static UriMatcher uriMatcher;
static {
// TODO: Tidy this url-structure. It's not forward-facing but it's kind of a mess.
// TODO: get rid of the hard-coded URL paths and replace then with the constant values in DatabaseConstants
// that they actually represent.
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, VERSION + "/feeds/", ALL_FEEDS);
uriMatcher.addURI(AUTHORITY, VERSION + "/social_feeds/", ALL_SOCIAL_FEEDS);
@ -259,7 +268,7 @@ public class FeedProvider extends ContentProvider {
break;
case UriMatcher.NO_MATCH:
Log.e(TAG, "No match found for URI: " + uri.toString());
Log.e(this.getClass().getName(), "No match found for URI: " + uri.toString());
break;
}
return resultUri;
@ -271,15 +280,28 @@ public class FeedProvider extends ContentProvider {
return true;
}
/**
* A simple utility wrapper that lets us log the insanely complex queries used below for debugging.
*/
class LoggingDatabase {
SQLiteDatabase mdb;
public LoggingDatabase(SQLiteDatabase db) {
mdb = db;
}
public Cursor rawQuery(String sql, String[] selectionArgs) {
//Log.d(LoggingDatabase.class.getName(), "rawQuery: " + sql);
return mdb.rawQuery(sql, selectionArgs);
}
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
return mdb.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// TODO: the fact that most of the app uses this subclass of ContentProvider cast as such may
// deepy confuse future maintainers as to why the .query() method magically does far, far more
// than suggested by the normal contract and provided args. This method should be renamed
// to make it painfully obvious that it expands upon the normal ContentProvider.query contract.
final SQLiteDatabase db = databaseHelper.getReadableDatabase();
final SQLiteDatabase rdb = databaseHelper.getReadableDatabase();
final LoggingDatabase db = new LoggingDatabase(rdb);
switch (uriMatcher.match(uri)) {
// Query for all feeds (by default only return those that have unread items in them)
@ -417,7 +439,9 @@ public class FeedProvider extends ContentProvider {
// Querying for all folders with unread items
case ALL_FOLDERS:
String folderQuery = "SELECT " + TextUtils.join(",", DatabaseConstants.FOLDER_COLUMNS) + " FROM " + DatabaseConstants.FEED_FOLDER_MAP_TABLE +
// Note the extra special pre-select UNION clause here!
String folderQuery = DatabaseConstants.FOLDER_UNION_ROOT +
"SELECT " + TextUtils.join(",", DatabaseConstants.FOLDER_COLUMNS) + " FROM " + DatabaseConstants.FEED_FOLDER_MAP_TABLE +
" INNER JOIN " + DatabaseConstants.FOLDER_TABLE +
" ON " + DatabaseConstants.FEED_FOLDER_MAP_TABLE + "." + DatabaseConstants.FEED_FOLDER_FOLDER_NAME + " = " + DatabaseConstants.FOLDER_TABLE + "." + DatabaseConstants.FOLDER_NAME +
" INNER JOIN " + DatabaseConstants.FEED_TABLE +

View file

@ -83,6 +83,13 @@ public class FeedFolderResponse {
}
socialFeeds = socialFeedsList.toArray(new SocialFeed[socialFeedsArray.size()]);
}
// sometimes the API won't declare the top-level/root folder, but most of the
// codebase expects it to exist. Declare it as empty if missing.
if (!folders.containsKey(AppConstants.ROOT_FOLDER)) {
folders.put(AppConstants.ROOT_FOLDER, new ArrayList<Long>());
Log.d( this.getClass().getName(), "root folder was missing. added it.");
}
}
/**

View file

@ -17,4 +17,6 @@ public class AppConstants {
// the name to give the "root" folder in the local DB since the API does not assign it one.
// this name should be unique and such that it will sort to the beginning of a list, ideally.
public static final String ROOT_FOLDER = "0000_TOP_LEVEL_";
public static final String LAST_APP_VERSION = "LAST_APP_VERSION";
}

View file

@ -11,9 +11,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.util.Log;
import com.newsblur.activity.Login;
import com.newsblur.database.BlurDatabase;
@ -29,6 +31,35 @@ public class PrefsUtils {
edit.commit();
}
/**
* Check to see if this is the first launch of the app after an upgrade, in which case
* we clear the DB to prevent bugs associated with non-forward-compatibility.
*/
public static void checkForUpgrade(Context context) {
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
String version;
try {
version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
} catch (NameNotFoundException nnfe) {
Log.w(PrefsUtils.class.getName(), "could not determine app version");
return;
}
Log.i(PrefsUtils.class.getName(), "launching version: " + version);
String oldVersion = prefs.getString(AppConstants.LAST_APP_VERSION, null);
if ( (oldVersion == null) || (!oldVersion.equals(version)) ) {
Log.i(PrefsUtils.class.getName(), "detected new version of app, clearing local data");
// wipe the local DB
BlurDatabase databaseHelper = new BlurDatabase(context.getApplicationContext());
databaseHelper.dropAndRecreateTables();
// store the current version
prefs.edit().putString(AppConstants.LAST_APP_VERSION, version).commit();
}
}
public static void logout(Context context) {
// TODO: stop or wait for any BG processes