diff --git a/clients/android/NewsBlur/res/menu/main.xml b/clients/android/NewsBlur/res/menu/main.xml index 5d56c1687..b806f4dc6 100644 --- a/clients/android/NewsBlur/res/menu/main.xml +++ b/clients/android/NewsBlur/res/menu/main.xml @@ -18,6 +18,10 @@ + + Search Mark all as read Log out + Send app feedback Loading… No stories to read diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java b/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java index 1c173e680..e900a02f8 100644 --- a/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java +++ b/clients/android/NewsBlur/src/com/newsblur/activity/ItemsList.java @@ -179,7 +179,7 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen @Override public void storyOrderChanged(StoryOrder newValue) { updateStoryOrderPreference(newValue); - NBSyncService.resetFeed(fs); + FeedUtils.clearReadingSession(this); itemListFragment.resetEmptyState(); itemListFragment.hasUpdated(); itemListFragment.scrollToTop(); @@ -191,7 +191,7 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen @Override public void readFilterChanged(ReadFilter newValue) { updateReadFilterPreference(newValue); - NBSyncService.resetFeed(fs); + FeedUtils.clearReadingSession(this); itemListFragment.resetEmptyState(); itemListFragment.hasUpdated(); itemListFragment.scrollToTop(); diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java index 2e6d25d25..d4139641f 100644 --- a/clients/android/NewsBlur/src/com/newsblur/activity/Main.java +++ b/clients/android/NewsBlur/src/com/newsblur/activity/Main.java @@ -6,7 +6,9 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.app.DialogFragment; import android.app.FragmentManager; +import android.net.Uri; import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -20,6 +22,7 @@ import com.newsblur.fragment.FolderListFragment; import com.newsblur.fragment.LogoutDialogFragment; import com.newsblur.service.BootReceiver; import com.newsblur.service.NBSyncService; +import com.newsblur.util.AppConstants; import com.newsblur.util.FeedUtils; import com.newsblur.util.PrefsUtils; import com.newsblur.util.UIUtils; @@ -30,7 +33,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre private ActionBar actionBar; private FolderListFragment folderFeedList; private FragmentManager fragmentManager; - private Menu menu; private TextView overlayStatusText; private boolean isLightTheme; private SwipeRefreshLayout swipeLayout; @@ -91,7 +93,14 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); - this.menu = menu; + + MenuItem feedbackItem = menu.findItem(R.id.menu_feedback); + if (AppConstants.ENABLE_FEEDBACK) { + feedbackItem.setTitle(feedbackItem.getTitle() + " (v" + PrefsUtils.getVersion(this) + ")"); + } else { + feedbackItem.setVisible(false); + } + return true; } @@ -116,6 +125,15 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre Intent settingsIntent = new Intent(this, Settings.class); startActivity(settingsIntent); return true; + } else if (item.getItemId() == R.id.menu_feedback) { + try { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this))); + startActivity(i); + } catch (Exception e) { + Log.wtf(this.getClass().getName(), "device cannot even open URLs to report feedback"); + } + return true; } return super.onOptionsItemSelected(item); } diff --git a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java index b3436aef0..5e4d07111 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java @@ -292,16 +292,16 @@ public class BlurDatabaseHelper { } public void markFeedsRead(FeedSet fs, Long olderThan, Long newerThan) { - // TODO: impl at least the older/newer than cases + // split this into two steps, since the double-check feature needs to + // redo the second step separately + markFeedsRead_feedCounts(fs, olderThan, newerThan); + markFeedsRead_storyCounts(fs, olderThan, newerThan); + } - // TODO: stories - - // feed counts + public void markFeedsRead_feedCounts(FeedSet fs, Long olderThan, Long newerThan) { if (fs.isAllNormal()) { setFeedUnreadCount(0, null, null); - } else if (fs.isAllSocial()) { - // TODO: oddly, the client never supported this before, so there is no button to invoke it. The API call - // works, though, so adding an impl. here should let us enable the button. + setSocialFeedUnreadCount(0, null, null); } else if (fs.getMultipleFeeds() != null) { for (String feedId : fs.getMultipleFeeds()) { setFeedUnreadCount(0, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId}); @@ -311,10 +311,38 @@ public class BlurDatabaseHelper { } else if (fs.getSingleSocialFeed() != null) { setSocialFeedUnreadCount(0, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[]{fs.getSingleSocialFeed().getKey()}); } else { - throw new IllegalStateException("Asked to get stories for FeedSet of unknown type."); + // TODO: fs.isAllSocial() was never supported by the UI, but the API has it, and it would be + // easy enough to add here. Should we? + throw new IllegalStateException("Asked to mark stories for FeedSet of unknown type."); } } + public void markFeedsRead_storyCounts(FeedSet fs, Long olderThan, Long newerThan) { + ContentValues values = new ContentValues(); + values.put(DatabaseConstants.STORY_READ, true); + String rangeSelection = null; + if (olderThan != null) rangeSelection = DatabaseConstants.STORY_TIMESTAMP + " <= " + olderThan.toString(); + if (newerThan != null) rangeSelection = DatabaseConstants.STORY_TIMESTAMP + " >= " + newerThan.toString(); + StringBuilder feedSelection = null; + if (fs.isAllNormal()) { + // a null selection is fine for all stories + } else if (fs.getMultipleFeeds() != null) { + feedSelection = new StringBuilder(DatabaseConstants.STORY_FEED_ID + " IN ( "); + feedSelection.append(TextUtils.join(",", fs.getMultipleFeeds())); + feedSelection.append(")"); + } else if (fs.getSingleFeed() != null) { + feedSelection= new StringBuilder(DatabaseConstants.STORY_FEED_ID + " = "); + feedSelection.append(fs.getSingleFeed()); + } else if (fs.getSingleSocialFeed() != null) { + feedSelection= new StringBuilder(DatabaseConstants.STORY_SOCIAL_USER_ID + " = "); + feedSelection.append(fs.getSingleSocialFeed().getKey()); + } else { + throw new IllegalStateException("Asked to mark stories for FeedSet of unknown type."); + } + dbRW.update(DatabaseConstants.STORY_TABLE, values, conjoinSelections(feedSelection, rangeSelection), null); + + } + private void setFeedUnreadCount(int count, String whereClause, String[] whereArgs) { ContentValues values = new ContentValues(); values.put(DatabaseConstants.FEED_NEGATIVE_COUNT, count); @@ -485,4 +513,19 @@ public class BlurDatabaseHelper { try {c.close();} catch (Exception e) {;} } + private static String conjoinSelections(CharSequence... args) { + StringBuilder s = null; + for (CharSequence c : args) { + if (c == null) continue; + if (s == null) { + s = new StringBuilder(c); + } else { + s.append(" AND "); + s.append(c); + } + } + if (s == null) return null; + return s.toString(); + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java index 5080361b2..faf801534 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java @@ -468,4 +468,10 @@ public class DatabaseConstants { } } + public static Long nullIfZero(Long l) { + if (l == null) return null; + if (l.longValue() == 0L) return null; + return l; + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java b/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java index 92aac2de9..5ece211a6 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/FeedProvider.java @@ -309,24 +309,24 @@ public class FeedProvider extends ContentProvider { mdb = db; } public Cursor rawQuery(String sql, String[] selectionArgs) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "rawQuery: " + sql); Log.d(LoggingDatabase.class.getName(), "selArgs : " + Arrays.toString(selectionArgs)); } Cursor cursor = mdb.rawQuery(sql, selectionArgs); - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "result rows: " + cursor.getCount()); } return cursor; } public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "selection: " + selection); } return mdb.query(table, columns, selection, selectionArgs, groupBy, having, orderBy); } public void execSQL(String sql) { - if (AppConstants.VERBOSE_LOG) { + if (AppConstants.VERBOSE_LOG_DB) { Log.d(LoggingDatabase.class.getName(), "execSQL: " + sql); } mdb.execSQL(sql); diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java b/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java index 04bed9c98..7f0258bdc 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIConstants.java @@ -68,12 +68,15 @@ public class APIConstants { public static final String PARAMETER_URL = "url"; public static final String PARAMETER_DAYS = "days"; public static final String PARAMETER_UPDATE_COUNTS = "update_counts"; - + public static final String PARAMETER_CUTOFF_TIME = "cutoff_timestamp"; + public static final String PARAMETER_DIRECTION = "direction"; public static final String PARAMETER_PAGE_NUMBER = "page"; public static final String PARAMETER_ORDER = "order"; public static final String PARAMETER_READ_FILTER = "read_filter"; public static final String VALUE_ALLSOCIAL = "river:blurblogs"; // the magic value passed to the mark-read API for all social feeds + public static final String VALUE_OLDER = "older"; + public static final String VALUE_NEWER = "newer"; public static final String URL_CONNECT_FACEBOOK = NEWSBLUR_URL + "/oauth/facebook_connect/"; public static final String URL_CONNECT_TWITTER = NEWSBLUR_URL + "/oauth/twitter_connect/"; diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index 22e4c175b..67f8e09ba 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -122,7 +122,18 @@ public class APIManager { for (String feedId : feedIds) { values.put(APIConstants.PARAMETER_FEEDID, feedId); } - // TODO: handle older/newer + if (includeOlder != null) { + // the app uses milliseconds but the API wants seconds + long cut = includeOlder.longValue(); + values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); + values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_OLDER); + } + if (includeNewer != null) { + // the app uses milliseconds but the API wants seconds + long cut = includeNewer.longValue(); + values.put(APIConstants.PARAMETER_CUTOFF_TIME, Long.toString(cut/1000L)); + values.put(APIConstants.PARAMETER_DIRECTION, APIConstants.VALUE_NEWER); + } APIResponse response = post(APIConstants.URL_MARK_FEED_AS_READ, values, false); // TODO: these calls use a different return format than others: the errors field is an array, not an object diff --git a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java index 8bfe6a548..cef29f092 100644 --- a/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java +++ b/clients/android/NewsBlur/src/com/newsblur/service/NBSyncService.java @@ -69,6 +69,11 @@ public class NBSyncService extends Service { would annoy a user who is on the story list or paging through stories. */ private volatile static boolean HoldStories = false; private volatile static boolean DoFeedsFolders = false; + private volatile static boolean isMemoryLow = false; + private volatile static boolean HaltNow = false; + + private static long lastFeedCount = 0L; + private static long lastFFWriteMillis = 0L; /** Feed sets that we need to sync and how many stories the UI wants for them. */ private static Map PendingFeeds; @@ -84,8 +89,6 @@ public class NBSyncService extends Service { private static Set ImageQueue; static { ImageQueue = new HashSet(); } - private volatile static boolean HaltNow; - private PowerManager.WakeLock wl = null; private ExecutorService executor; private APIManager apiManager; @@ -138,7 +141,12 @@ public class NBSyncService extends Service { */ private synchronized void doSync(int startId) { try { - if (HaltNow) return; + if (HaltNow) { + if (AppConstants.VERBOSE_LOG) { + Log.d(this.getClass().getName(), "skipping sync, soft interrupt set."); + } + return; + } Log.d(this.getClass().getName(), "starting sync . . ."); @@ -171,6 +179,10 @@ public class NBSyncService extends Service { if (wl != null) wl.release(); Log.d(this.getClass().getName(), " . . . sync done"); } + + if (isMemoryLow && (NbActivity.getActiveActivityCount() < 1)) { + stopSelf(startId); + } } /** @@ -195,8 +207,8 @@ public class NBSyncService extends Service { if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_READ)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); String feedIds = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID)); - Long includeOlder = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); - Long includeNewer = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); + Long includeOlder = DatabaseConstants.nullIfZero(c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_INCLUDE_OLDER))); + Long includeNewer = DatabaseConstants.nullIfZero(c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_INCLUDE_NEWER))); if (hash != null) { response = apiManager.markStoryAsRead(hash); } else if (feedIds != null) { @@ -253,7 +265,10 @@ public class NBSyncService extends Service { String id = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_ID)); if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_READ)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); - dbHelper.setStoryReadState(hash, true); + if (hash != null ) { + dbHelper.setStoryReadState(hash, true); + } + // TODO: double-check stories from feed-marks } else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_MARK_UNREAD)) == 1) { String hash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH)); dbHelper.setStoryReadState(hash, false); @@ -324,6 +339,8 @@ public class NBSyncService extends Service { return; } + long startTime = System.currentTimeMillis(); + isPremium = feedResponse.isPremium; // clean out the feed / folder tables @@ -375,6 +392,9 @@ public class NBSyncService extends Service { // populate the starred stories count table dbHelper.updateStarredStoriesCount(feedResponse.starredCount); + lastFFWriteMillis = System.currentTimeMillis() - startTime; + lastFeedCount = feedValues.size(); + } finally { FFSyncRunning = false; NbActivity.updateAllActivities(); @@ -561,12 +581,14 @@ public class NBSyncService extends Service { } public void onTrimMemory (int level) { - // if the UI is still active, definitely don't stop - if (NbActivity.getActiveActivityCount() > 0) return; - // be nice and stop if memory is even a tiny bit pressured and we aren't visible; // the OS penalises long-running processes, and it is reasonably cheap to re-create ourself. if (level > ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { + isMemoryLow = true; + + // if the UI is still active, definitely don't stop + if (NbActivity.getActiveActivityCount() > 0) return; + if (lastStartIdCompleted != -1) { stopSelf(lastStartIdCompleted); } @@ -642,14 +664,10 @@ public class NBSyncService extends Service { return true; } - /** - * Resets pagination and exhaustion flags for the given feedset, so that it can be requested fresh - * from the beginning with new parameters. - */ - public static void resetFeed(FeedSet fs) { - ExhaustedFeeds.remove(fs); - FeedPagesSeen.put(fs, 0); - FeedStoriesSeen.put(fs, 0); + public static void resetFeeds() { + ExhaustedFeeds.clear(); + FeedPagesSeen.clear(); + FeedStoriesSeen.clear(); } public static void softInterrupt() { @@ -657,6 +675,10 @@ public class NBSyncService extends Service { HaltNow = true; } + public static void resumeFromInterrupt() { + HaltNow = false; + } + @Override public void onDestroy() { Log.d(this.getClass().getName(), "onDestroy"); @@ -671,4 +693,14 @@ public class NBSyncService extends Service { return null; } + public static boolean isMemoryLow() { + return isMemoryLow; + } + + public static String getSpeedInfo() { + StringBuilder s = new StringBuilder(); + s.append(lastFeedCount).append(" in ").append(lastFFWriteMillis); + return s.toString(); + } + } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java index 5fcd746d4..e755a2f20 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/AppConstants.java @@ -5,7 +5,8 @@ public class AppConstants { // Enables high-volume logging that may be useful for debugging. This should // never be enabled for releases, as it not only slows down the app considerably, // it will log sensitive info such as passwords! - public static final boolean VERBOSE_LOG = false; + public static final boolean VERBOSE_LOG = true; + public static final boolean VERBOSE_LOG_DB = false; public static final int STATE_ALL = 0; public static final int STATE_SOME = 1; @@ -58,4 +59,10 @@ public class AppConstants { // how many images to prefetch before updating the countdown UI public static final int IMAGE_PREFETCH_BATCH_SIZE = 10; + // should the feedback link be enabled (read: is this a beta?) + public static final boolean ENABLE_FEEDBACK = true; + + // link to app feedback page + public static final String FEEDBACK_URL = "https://getsatisfaction.com/newsblur/topics/new?topic[style]=question&from=company&product=NewsBlur+Android+App&topic[additional_detail]="; + } diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index 3690c9fb9..f6a341782 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -117,6 +117,7 @@ public class FeedUtils { } catch (Exception e) { ; // this one call can evade the on-upgrade DB wipe and throw exceptions } + NBSyncService.resetFeeds(); return null; } }.execute(); diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java index 4f9214eda..686542363 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java @@ -19,6 +19,7 @@ import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.Build; import android.util.Log; import com.newsblur.R; @@ -30,6 +31,7 @@ import com.newsblur.service.NBSyncService; public class PrefsUtils { public static void saveLogin(final Context context, final String userName, final String cookie) { + NBSyncService.resumeFromInterrupt(); final SharedPreferences preferences = context.getSharedPreferences(PrefConstants.PREFERENCES, 0); final Editor edit = preferences.edit(); edit.putString(PrefConstants.PREF_COOKIE, cookie); @@ -45,10 +47,8 @@ public class PrefsUtils { SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0); - String version; - try { - version = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; - } catch (NameNotFoundException nnfe) { + String version = getVersion(context); + if (version == null) { Log.w(PrefsUtils.class.getName(), "could not determine app version"); return; } @@ -72,6 +72,26 @@ public class PrefsUtils { } + public static String getVersion(Context context) { + try { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; + } catch (NameNotFoundException nnfe) { + Log.w(PrefsUtils.class.getName(), "could not determine app version"); + return null; + } + } + + public static String createFeedbackLink(Context context) { + StringBuilder s = new StringBuilder(AppConstants.FEEDBACK_URL); + s.append("%0A%0A"); + s.append("%0Aapp version: ").append(getVersion(context)); + s.append("%0Aandroid version: ").append(Build.VERSION.RELEASE); + s.append("%0Adevice: ").append(Build.MANUFACTURER + "+" + Build.MODEL + "+(" + Build.BOARD + ")"); + s.append("%0Amemory: ").append(NBSyncService.isMemoryLow() ? "low" : "normal"); + s.append("%0Aspeed: ").append(NBSyncService.getSpeedInfo()); + return s.toString(); + } + public static void logout(Context context) { NBSyncService.softInterrupt(); diff --git a/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java b/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java index 12cc89054..6699772c9 100644 --- a/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java +++ b/clients/android/NewsBlur/src/com/newsblur/view/SocialItemViewBinder.java @@ -70,9 +70,12 @@ public class SocialItemViewBinder implements ViewBinder { return true; } else if (TextUtils.equals(columnName, DatabaseConstants.STORY_AUTHORS)) { String authors = cursor.getString(columnIndex); - if (!TextUtils.isEmpty(authors)) { - ((TextView) view).setText(authors.toUpperCase()); - } + if (TextUtils.isEmpty(authors)) { + view.setVisibility(View.GONE); + } else { + ((TextView) view).setText(authors.toUpperCase()); + view.setVisibility(View.VISIBLE); + } return true; } else if (TextUtils.equals(columnName, DatabaseConstants.STORY_TITLE)) { ((TextView) view).setText(Html.fromHtml(cursor.getString(columnIndex)));