Merge pull request #754 from dosiecki/master

Android: Beta Candidate
This commit is contained in:
Samuel Clay 2015-09-21 17:00:47 -07:00
commit 3eba1408f2
26 changed files with 209 additions and 231 deletions

View file

@ -131,7 +131,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="15dp"
android:layout_marginBottom="25dp"
android:gravity="center_horizontal"
style="?explainerText"
android:visibility="invisible"

View file

@ -70,6 +70,7 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen
@Override
protected void onResume() {
super.onResume();
if (NBSyncService.isHousekeepingRunning()) finish();
updateStatusIndicators();
// Reading activities almost certainly changed the read/unread state of some stories. Ensure
// we reflect those changes promptly.
@ -124,6 +125,9 @@ public abstract class ItemsList extends NbActivity implements StateChangedListen
@Override
public void handleUpdate(int updateType) {
if ((updateType & UPDATE_REBUILD) != 0) {
finish();
}
if ((updateType & UPDATE_STATUS) != 0) {
updateStatusIndicators();
}

View file

@ -70,6 +70,11 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
getActionBar().hide();
// set the status bar to an generic loading message when the activity is first created so
// that something is displayed while the service warms up
overlayStatusText.setText(R.string.loading);
overlayStatusText.setVisibility(View.VISIBLE);
swipeLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container);
swipeLayout.setColorScheme(R.color.refresh_1, R.color.refresh_2, R.color.refresh_3, R.color.refresh_4);
swipeLayout.setOnRefreshListener(this);
@ -102,6 +107,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
updateStatusIndicators();
folderFeedList.pushUnreadCounts();
folderFeedList.checkOpenFolderPreferences();
triggerSync();
if (PrefsUtils.isLightThemeSelected(this) != isLightTheme) {
@ -116,6 +122,9 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
@Override
public void handleUpdate(int updateType) {
if ((updateType & UPDATE_REBUILD) != 0) {
folderFeedList.reset();
}
if ((updateType & UPDATE_DB_READY) != 0) {
try {
folderFeedList.startLoaders();
@ -138,8 +147,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
if ((neutCount+posiCount) <= 0) {
if (NBSyncService.isFeedCountSyncRunning() || (!folderFeedList.firstCursorSeenYet)) {
emptyViewImage.setVisibility(View.INVISIBLE);
emptyViewText.setText(R.string.loading);
emptyViewText.setVisibility(View.VISIBLE);
emptyViewText.setVisibility(View.INVISIBLE);
} else {
emptyViewImage.setVisibility(View.VISIBLE);
if (folderFeedList.currentState == StateFilter.BEST) {

View file

@ -25,6 +25,7 @@ public class NbActivity extends Activity {
public static final int UPDATE_SOCIAL = (1<<3);
public static final int UPDATE_STATUS = (1<<5);
public static final int UPDATE_TEXT = (1<<6);
public static final int UPDATE_REBUILD = (1<<7);
private final static String UNIQUE_LOGIN_KEY = "uniqueLoginKey";
private String uniqueLoginKey;

View file

@ -70,7 +70,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
/** The minimum screen width (in DP) needed to show all the overlay controls. */
private static final int OVERLAY_MIN_WIDTH_DP = 355;
protected int passedPosition;
protected StateFilter currentState;
protected StoryOrder storyOrder;
protected ReadFilter readFilter;
@ -78,8 +77,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
// Activities navigate to a particular story by hash.
// We can find it once we have the cursor.
private String storyHash;
private boolean storyHashSearchActive = false;
private boolean storyHashSearchStarted = false;
protected final Object STORIES_MUTEX = new Object();
protected Cursor stories;
@ -127,11 +124,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
fs = (FeedSet)getIntent().getSerializableExtra(EXTRA_FEEDSET);
if ((savedInstanceBundle != null) && savedInstanceBundle.containsKey(BUNDLE_POSITION)) {
passedPosition = savedInstanceBundle.getInt(BUNDLE_POSITION);
} else {
passedPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
}
if ((savedInstanceBundle != null) && savedInstanceBundle.containsKey(BUNDLE_STARTING_UNREAD)) {
startingUnreadCount = savedInstanceBundle.getInt(BUNDLE_STARTING_UNREAD);
}
@ -140,7 +132,8 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
// recreated due to rotation etc.
if (savedInstanceBundle == null) {
storyHash = getIntent().getStringExtra(EXTRA_STORY_HASH);
storyHashSearchActive = storyHash != null;
} else {
storyHash = savedInstanceBundle.getString(EXTRA_STORY_HASH);
}
currentState = (StateFilter) getIntent().getSerializableExtra(ItemsList.EXTRA_STATE);
@ -176,7 +169,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
@Override
protected void onSaveInstanceState(Bundle outState) {
if (pager != null) {
outState.putInt(BUNDLE_POSITION, pager.getCurrentItem());
int currentItem = pager.getCurrentItem();
Story story = readingAdapter.getStory(currentItem);
outState.putString(EXTRA_STORY_HASH, story.storyHash);
}
if (startingUnreadCount != 0) {
@ -196,6 +191,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
@Override
protected void onResume() {
super.onResume();
if (NBSyncService.isHousekeepingRunning()) finish();
// this view shows stories, it is not safe to perform cleanup
this.stopLoading = false;
// onCreate() in our subclass should have called createLoader(), but sometimes the callback never makes it.
@ -232,6 +228,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
setupPager();
}
// see if we are just starting and need to jump to a target story
skipPagerToStoryHash();
try {
readingAdapter.notifyDataSetChanged();
} catch (IllegalStateException ise) {
@ -244,18 +243,37 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
// now that we have more stories, look again.
nextUnread();
}
if (storyHashSearchActive) {
findStoryByHash();
}
checkStoryCount(pager.getCurrentItem());
updateOverlayNav();
updateOverlayText();
}
}
private void skipPagerToStoryHash() {
// if we already started and found our target story, this will be unset
if (storyHash == null) return;
stories.moveToPosition(-1);
while (stories.moveToNext()) {
if (stopLoading) return;
Story story = Story.fromCursor(stories);
if (story.storyHash.equals(storyHash)) {
// now that the pager is getting the right story, make it visible
pager.setVisibility(View.VISIBLE);
pager.setCurrentItem(stories.getPosition(), false);
this.onPageSelected(stories.getPosition());
storyHash = null;
return;
}
}
// if the story wasn't found, try to get more stories into the cursor
FeedUtils.activateAllStories();
this.checkStoryCount(readingAdapter.getCount()+1);
}
private void setupPager() {
pager = (ViewPager) findViewById(R.id.reading_pager);
// since it might start on the wrong story, create the pager as invisible
pager.setVisibility(View.INVISIBLE);
pager.setPageMargin(UIUtils.dp2px(getApplicationContext(), 1));
if (PrefsUtils.isLightThemeSelected(this)) {
pager.setPageMarginDrawable(R.drawable.divider_light);
@ -266,10 +284,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
pager.setOnPageChangeListener(this);
pager.setAdapter(readingAdapter);
pager.setCurrentItem(passedPosition, false);
// setCurrentItem sometimes fails to pass the first page to the callback, so call it manually
// for the first one.
this.onPageSelected(passedPosition);
// if the first story in the list was "viewed" before the page change listener was set,
// the calback was probably missed
if (storyHash == null) {
this.onPageSelected(pager.getCurrentItem());
}
updateOverlayNav();
enableOverlays();
@ -349,6 +368,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
@Override
protected void handleUpdate(int updateType) {
if ((updateType & UPDATE_REBUILD) != 0) {
finish();
}
if ((updateType & UPDATE_STATUS) != 0) {
enableMainProgress(NBSyncService.isFeedSetSyncing(this.fs, this));
}
@ -638,15 +660,45 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
// let either finish. search will happen when the cursor is pushed.
if ((pager == null) || (readingAdapter == null)) return;
boolean unreadFound = searchForStory(new UnreadStorySearchMatcher());
if (this.stopLoading) {
// this activity was ended before we finished. just stop.
unreadSearchActive = false;
return;
boolean unreadFound = false;
// start searching just after the current story
int candidate = pager.getCurrentItem() + 1;
unreadSearch:while (!unreadFound) {
// if we've reached the end of the list, loop back to the beginning
if (candidate >= readingAdapter.getCount()) {
candidate = 0;
}
// if we have looped all the way around to the story we are on, there aren't any left
if (candidate == pager.getCurrentItem()) {
break unreadSearch;
}
Story story = readingAdapter.getStory(candidate);
if (this.stopLoading) {
// this activity was ended before we finished. just stop.
unreadSearchActive = false;
return;
}
// iterate through the stories in our cursor until we find an unread one
if (story != null) {
if (story.read) {
candidate++;
continue unreadSearch;
} else {
unreadFound = true;
}
}
// if we didn't continue or break, the cursor probably changed out from under us, so stop.
break unreadSearch;
}
if (unreadFound) {
// jump to the story we found
final int page = candidate;
runOnUiThread(new Runnable() {
public void run() {
pager.setCurrentItem(page, true);
}
});
// disable the search flag, as we are done
unreadSearchActive = false;
} else {
@ -663,87 +715,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
}
}
private boolean searchForStory(StorySearchMatcher matcher) {
boolean storyFound = false;
// start searching just after the current story
int candidate = pager.getCurrentItem() + 1;
storySearch:while (!storyFound) {
// if we've reached the end of the list, loop back to the beginning
if (candidate >= readingAdapter.getCount()) {
candidate = 0;
}
// if we have looped all the way around to the story we are on, there aren't any left
if (candidate == pager.getCurrentItem()) {
break storySearch;
}
Story story = readingAdapter.getStory(candidate);
if (this.stopLoading) {
// this activity was ended before we finished. just stop.
return false;
}
// iterate through the stories in our cursor until we find one that matches
if (story != null) {
if (matcher.matches(story)) {
storyFound = true;
} else {
candidate++;
continue storySearch;
}
}
// if we didn't continue or break, the cursor probably changed out from under us, so stop.
break storySearch;
}
if (storyFound) {
// jump to the story we found
final int page = candidate;
runOnUiThread(new Runnable() {
public void run() {
pager.setCurrentItem(page, true);
}
});
}
return storyFound;
}
private void findStoryByHash() {
// the first time an story hash search is triggered, also trigger an activation of unreads, so
// we don't search for a story that doesn't exist in the cursor
if (!storyHashSearchStarted) {
FeedUtils.activateAllStories();
storyHashSearchStarted = true;
}
// if we somehow got tapped before construction or are running during destruction, stop and
// let either finish. search will happen when the cursor is pushed.
if ((pager == null) || (readingAdapter == null)) return;
boolean storyFound = searchForStory(new StoryHashSearchMatcher(storyHash));
if (this.stopLoading) {
// this activity was ended before we finished. just stop.
storyHashSearchActive = false;
return;
}
if (storyFound) {
// disable the search flag, as we are done
storyHashSearchActive = false;
} else {
// We didn't find a story, so we should trigger a check to see if the API can load any more.
// First, though, double check that there are even any left, as there may have been a delay
// between marking an earlier one and double-checking counts.
if (getUnreadCount() <= 0) {
storyHashSearchActive = false;
} else {
// trigger a check to see if there are any more to search before proceeding. By leaving the
// unreadSearchActive flag high, this method will be called again when a new cursor is loaded
this.checkStoryCount(readingAdapter.getCount()+1);
}
}
}
/**
* Click handler for the lefthand overlay nav button.
*/
@ -853,29 +824,4 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
return super.onKeyUp(keyCode, event);
}
}
private interface StorySearchMatcher {
boolean matches(Story story);
}
private class UnreadStorySearchMatcher implements StorySearchMatcher {
@Override
public boolean matches(Story story) {
return !story.read;
}
}
private class StoryHashSearchMatcher implements StorySearchMatcher {
private String hash;
public StoryHashSearchMatcher(String hash) {
this.hash = hash;
}
@Override
public boolean matches(Story story) {
return this.hash.equals(story.storyHash);
}
}
}

View file

@ -59,6 +59,9 @@ public abstract class ReadingAdapter extends FragmentStatePagerAdapter {
public synchronized void swapCursor(Cursor cursor) {
this.stories = cursor;
if (cursor != null) {
notifyDataSetChanged();
}
}
protected abstract ReadingItemFragment getReadingItemFragment(Story story);

View file

@ -35,7 +35,7 @@ public class SocialFeedReading extends Reading {
// If we have navigated from the profile we want to ignore the StateFilter and ReadFilter settings
// for the feed to ensure we can find the story.
if (openedFromProfile) {
return FeedUtils.dbHelper.getAllStoriesLoader(fs);
return FeedUtils.dbHelper.getStoriesLoaderIgnoreFilters(fs);
} else {
return super.onCreateLoader(loaderId, bundle);
}

View file

@ -867,7 +867,7 @@ public class BlurDatabaseHelper {
* When navigating to a social story from an interaction/activity we want to ignore
* the any state so we can be sure we find the selected story.
*/
public Loader<Cursor> getAllStoriesLoader(final FeedSet fs) {
public Loader<Cursor> getStoriesLoaderIgnoreFilters(final FeedSet fs) {
return new QueryCursorLoader(context) {
protected Cursor createCursor() {return getStoriesCursor(fs, StateFilter.ALL, ReadFilter.ALL, cancellationSignal);}
};

View file

@ -1,6 +1,7 @@
package com.newsblur.database;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -258,7 +259,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
// in addition to the real folders returned by the /reader/feeds API, there are virtual folders
// for global shared stories, social feeds and saved stories
if (activeFolderNames == null) return 0;
return (activeFolderNames.size() + 4);
// two types of group (folder and All Stories are represented as folders, and don't count, so -2)
return (activeFolderNames.size() + (GroupType.values().length - 2));
}
@Override
@ -505,6 +507,30 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
notifyDataSetChanged();
}
public synchronized void reset() {
socialFeeds = Collections.emptyMap();
socialFeedsOrdered = Collections.emptyList();
totalSocialNeutCount = 0;
totalSocialPosiCount = 0;
folders = Collections.emptyMap();
flatFolders = Collections.emptyMap();
safeClear(activeFolderNames);
safeClear(activeFolderChildren);
safeClear(folderNeutCounts);
safeClear(folderPosCounts);
feeds = Collections.emptyMap();
safeClear(feedNeutCounts);
safeClear(feedPosCounts);
totalNeutCount = 0;
totalPosCount = 0;
safeClear(closedFolders);
savedStoriesCount = 0;
}
public Feed getFeed(String feedId) {
return feeds.get(feedId);
}
@ -649,4 +675,12 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
}
};
public void safeClear(Collection c) {
if (c != null) c.clear();
}
public void safeClear(Map m) {
if (m != null) m.clear();
}
}

View file

@ -5,6 +5,7 @@ import android.database.Cursor;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.newsblur.database.DatabaseConstants;
@ -64,6 +65,10 @@ public class Folder {
builder.append(name);
return builder.toString();
}
public void removeOrphanFeedIds(Collection<String> orphanFeedIds) {
feedIds.removeAll(orphanFeedIds);
}
@Override
public boolean equals(Object otherFolder) {

View file

@ -8,8 +8,6 @@ import java.util.Map;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.RequestBody;
@ -49,23 +47,6 @@ public class ValueMultimap implements Serializable {
return TextUtils.join("&", parameters);
}
public String getJsonString() {
ArrayList<String> parameters = new ArrayList<String>();
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
for (String key : multimap.keySet()) {
StringBuilder builder = new StringBuilder();
builder.append("\"" + key + "\"");
builder.append(": ");
builder.append(gson.toJson(multimap.get(key)));
parameters.add(builder.toString());
}
final StringBuilder builder = new StringBuilder();
builder.append("{");
builder.append(TextUtils.join(",", parameters));
builder.append("}");
return builder.toString();
}
public RequestBody asFormEncodedRequestBody() {
FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
for (String key : multimap.keySet()) {

View file

@ -5,7 +5,6 @@ import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.AllSharedStoriesReading;
@ -44,11 +43,10 @@ public class AllSharedStoriesItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), AllSharedStoriesReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);

View file

@ -5,7 +5,6 @@ import android.database.Cursor;
import android.os.Bundle;
import android.content.Loader;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.AllStoriesReading;
@ -44,11 +43,10 @@ public class AllStoriesItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), AllStoriesReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);

View file

@ -5,7 +5,6 @@ import android.database.Cursor;
import android.os.Bundle;
import android.content.Loader;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.FeedReading;
@ -55,12 +54,11 @@ public class FeedItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), FeedReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(Reading.EXTRA_FEED, feed);
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);

View file

@ -5,7 +5,6 @@ import android.database.Cursor;
import android.os.Bundle;
import android.content.Loader;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.FeedReading;
@ -54,11 +53,10 @@ public class FolderItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), FolderReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(FeedReading.EXTRA_FOLDERNAME, folderName);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);

View file

@ -57,7 +57,6 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
public StateFilter currentState = StateFilter.SOME;
private SharedPreferences sharedPreferences;
@FindView(R.id.folderfeed_list) ExpandableListView list;
private Main activity;
public boolean firstCursorSeenYet = false;
@Override
@ -73,7 +72,6 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
sharedPreferences = getActivity().getSharedPreferences(PrefConstants.PREFERENCES, 0);
activity = (Main) getActivity();
}
@Override
@ -154,6 +152,10 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
}
}
public void reset() {
if (adapter != null) adapter.reset();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_folderfeedlist, container);
@ -181,7 +183,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
* database. The list widget likes to default all folders to closed, so open them up
* unless expressly collapsed at some point.
*/
private void checkOpenFolderPreferences() {
public void checkOpenFolderPreferences() {
// make sure we didn't beat construction
if ((this.list == null) || (this.sharedPreferences == null)) return;
@ -275,7 +277,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
* DB model, so having Main also load it would cause some lag.
*/
public void pushUnreadCounts() {
activity.updateUnreadCounts((adapter.totalNeutCount+adapter.totalSocialNeutCount), (adapter.totalPosCount+adapter.totalSocialPosiCount));
((Main) getActivity()).updateUnreadCounts((adapter.totalNeutCount+adapter.totalSocialNeutCount), (adapter.totalPosCount+adapter.totalSocialPosiCount));
}
@OnGroupClick(R.id.folderfeed_list) boolean onGroupClick(ExpandableListView list, View group, int groupPosition, long id) {

View file

@ -7,7 +7,6 @@ import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.FeedReading;
@ -44,11 +43,10 @@ public class GlobalSharedStoriesItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), GlobalSharedStoriesReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
startActivity(i);

View file

@ -316,12 +316,14 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public synchronized void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int truePosition = position - 1;
onItemClick_(parent, view, truePosition, id);
Story story = adapter.getStory(truePosition);
if (getActivity().isFinishing()) return;
onItemClick_(story.storyHash);
}
public abstract void onItemClick_(AdapterView<?> parent, View view, int position, long id);
public abstract void onItemClick_(String storyHash);
protected void setupBezelSwipeDetector(View v) {
final GestureDetector gestureDetector = new GestureDetector(getActivity(), new BezelSwipeDetector());

View file

@ -8,7 +8,6 @@ import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.Reading;
@ -48,11 +47,10 @@ public class ReadStoriesItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), ReadStoriesReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);
}

View file

@ -532,9 +532,7 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
sniffAltTexts(storyText);
if (PrefsUtils.isImagePrefetchEnabled(getActivity())) {
storyText = swapInOfflineImages(storyText);
}
storyText = swapInOfflineImages(storyText);
float currentSize = PrefsUtils.getTextSize(getActivity());

View file

@ -8,7 +8,6 @@ import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.Reading;
@ -48,11 +47,10 @@ public class SavedStoriesItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), SavedStoriesReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(FeedReading.EXTRA_POSITION, position);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);
}

View file

@ -5,7 +5,6 @@ import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import com.newsblur.R;
import com.newsblur.activity.ItemsList;
@ -53,12 +52,11 @@ public class SocialFeedItemListFragment extends ItemListFragment {
}
@Override
public void onItemClick_(AdapterView<?> parent, View view, int position, long id) {
if (getActivity().isFinishing()) return;
public void onItemClick_(String storyHash) {
Intent i = new Intent(getActivity(), SocialFeedReading.class);
i.putExtra(Reading.EXTRA_STORY_HASH, storyHash);
i.putExtra(Reading.EXTRA_FEEDSET, getFeedSet());
i.putExtra(Reading.EXTRA_SOCIAL_FEED, socialFeed);
i.putExtra(Reading.EXTRA_POSITION, position);
i.putExtra(ItemsList.EXTRA_STATE, currentState);
i.putExtra(Reading.EXTRA_DEFAULT_FEED_VIEW, defaultFeedView);
startActivity(i);

View file

@ -615,9 +615,17 @@ public class APIManager {
return new APIResponse(context);
}
if (AppConstants.VERBOSE_LOG) {
if (AppConstants.VERBOSE_LOG_NET) {
Log.d(this.getClass().getName(), "API POST " + urlString);
Log.d(this.getClass().getName(), "post body: " + formBody.toString());
String body = "";
try {
okio.Buffer buffer = new okio.Buffer();
formBody.writeTo(buffer);
body = buffer.readUtf8();
} catch (Exception e) {
; // this is debug code, do not raise
}
Log.d(this.getClass().getName(), "post body: " + body);
}
Request.Builder requestBuilder = new Request.Builder().url(urlString);

View file

@ -20,8 +20,6 @@ public class NewsBlurResponse {
public String[] errors;
public long readTime;
public static final Pattern KnownUserErrors = Pattern.compile("cannot mark as unread");
public boolean isError() {
if (isProtocolError) return true;
if ((message != null) && (!message.equals(""))) {
@ -35,18 +33,6 @@ public class NewsBlurResponse {
return false;
}
// TODO: can we add a canonical flag of some sort to 100% of API responses that differentiates
// between user and server errors? Until then, we have to sniff known bad ones, since all
// user errors have a 200 response rather than a 4xx.
public boolean isUserError() {
String err = getErrorMessage(null);
if (err != null) {
Matcher m = KnownUserErrors.matcher(err);
if (m.find()) return true;
}
return false;
}
/**
* Gets the error message returned by the API, or defaultMessage if none was found.
*/

View file

@ -270,7 +270,7 @@ public class NBSyncService extends Service {
boolean upgraded = PrefsUtils.checkForUpgrade(this);
if (upgraded) {
HousekeepingRunning = true;
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS | NbActivity.UPDATE_REBUILD);
// wipe the local DB
dbHelper.dropAndRecreateTables();
NbActivity.updateAllActivities(NbActivity.UPDATE_METADATA);
@ -350,10 +350,8 @@ public class NBSyncService extends Service {
}
} finally {
closeQuietly(c);
if (ActionsRunning) {
ActionsRunning = false;
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
}
ActionsRunning = false;
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
}
}
@ -371,7 +369,6 @@ public class NBSyncService extends Service {
if (PendingFeed == null) {
FollowupActions.clear();
}
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
}
/**
@ -393,8 +390,9 @@ public class NBSyncService extends Service {
FFSyncRunning = true;
NbActivity.updateAllActivities(NbActivity.UPDATE_STATUS);
// there is a rare issue with feeds that have no folder. capture them for workarounds.
Set<String> debugFeedIds = new HashSet<String>();
// there is an issue with feeds that have no folder or folders that list feeds that do not exist. capture them for workarounds.
Set<String> debugFeedIdsFromFolders = new HashSet<String>();
Set<String> debugFeedIdsFromFeeds = new HashSet<String>();
orphanFeedIds = new HashSet<String>();
try {
@ -430,21 +428,20 @@ public class NBSyncService extends Service {
// clean out the feed / folder tables
dbHelper.cleanupFeedsFolders();
// data for the folder and folder-feed-mapping tables
List<ContentValues> folderValues = new ArrayList<ContentValues>();
// note all feeds that belong to some folder so we can find orphans
for (Folder folder : feedResponse.folders) {
folderValues.add(folder.getValues());
// note all feeds that belong to some folder
debugFeedIds.addAll(folder.feedIds);
debugFeedIdsFromFolders.addAll(folder.feedIds);
}
// data for the feeds table
List<ContentValues> feedValues = new ArrayList<ContentValues>();
feedaddloop: for (Feed feed : feedResponse.feeds) {
// note all feeds for which the API returned data
debugFeedIdsFromFeeds.add(feed.feedId);
// sanity-check that the returned feeds actually exist in a folder or at the root
// if they do not, they should neither display nor count towards unread numbers
if (! debugFeedIds.contains(feed.feedId)) {
Log.w(this.getClass().getName(), "Found and ignoring un-foldered feed: " + feed.feedId );
if (! debugFeedIdsFromFolders.contains(feed.feedId)) {
Log.w(this.getClass().getName(), "Found and ignoring orphan feed (in feeds but not folders): " + feed.feedId );
orphanFeedIds.add(feed.feedId);
continue feedaddloop;
}
@ -454,7 +451,23 @@ public class NBSyncService extends Service {
}
feedValues.add(feed.getValues());
}
// prune out missiong feed IDs from folders
for (String id : debugFeedIdsFromFolders) {
if (! debugFeedIdsFromFeeds.contains(id)) {
Log.w(this.getClass().getName(), "Found and ignoring orphan feed (in folders but not feeds): " + id );
orphanFeedIds.add(id);
}
}
// data for the folder table
List<ContentValues> folderValues = new ArrayList<ContentValues>();
for (Folder folder : feedResponse.folders) {
// prune out orphans before pushing to the DB
folder.removeOrphanFeedIds(orphanFeedIds);
folderValues.add(folder.getValues());
}
// data for the the social feeds table
List<ContentValues> socialFeedValues = new ArrayList<ContentValues>();
for (SocialFeed feed : feedResponse.socialFeeds) {
@ -542,7 +555,7 @@ public class NBSyncService extends Service {
} finally {
if (RecountsRunning) {
RecountsRunning = false;
NbActivity.updateAllActivities(NbActivity.UPDATE_METADATA);
NbActivity.updateAllActivities(NbActivity.UPDATE_METADATA | NbActivity.UPDATE_STATUS);
}
FlushRecounts = false;
}
@ -744,6 +757,10 @@ public class NBSyncService extends Service {
return (HousekeepingRunning || RecountsRunning || FFSyncRunning);
}
public static boolean isHousekeepingRunning() {
return HousekeepingRunning;
}
/**
* Is there a sync for a given FeedSet running?
*/

View file

@ -40,8 +40,7 @@ public class NewsblurWebview extends WebView {
getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
getSettings().setDomStorageEnabled(true);
getSettings().setSupportZoom(true);
getSettings().setAppCacheMaxSize(1024*1024*8);
getSettings().setAppCachePath("/data/data/com.newsblur/cache");
getSettings().setAppCachePath(context.getCacheDir().getAbsolutePath());
getSettings().setAllowFileAccess(true);
getSettings().setAppCacheEnabled(true);