mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-21 05:45:13 +00:00
Several changes in one commit (bad me)
- Speed up main feed list refresh by not downloading feed icons and using nested feed list (rather than flat) - Update icons on main feed list in background now that they are no longer downloaded from sync - Cache gson builder as to not take penalty to rebuild it for each parse - Perform bulk insert of records into Sqlite DB during main feed list sync - Add initial support for shared feed button off story page (still needs a bit of work) - Add 'mark item read' and 'mark previous items read' functionality to the feed story list view
This commit is contained in:
parent
dc0507f43f
commit
60a828a914
26 changed files with 453 additions and 130 deletions
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#1e78c1"
|
||||
android:startColor="#42aaff" />
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="5dp"
|
||||
android:bottomRightRadius="5dp"
|
||||
android:topLeftRadius="5dp"
|
||||
android:topRightRadius="5dp" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:endColor="#2379bf"
|
||||
android:startColor="#1e78c1" />
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="5dp"
|
||||
android:bottomRightRadius="5dp"
|
||||
android:topLeftRadius="5dp"
|
||||
android:topRightRadius="5dp" />
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:state_pressed="true" android:drawable="@drawable/savebutton_background_pressed" />
|
||||
<item android:drawable="@drawable/savebutton_background_default" />
|
||||
</selector>
|
|
@ -13,6 +13,19 @@
|
|||
android:padding="10dp"
|
||||
android:text="@string/share_this" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/save_story_button"
|
||||
style="@style/saveButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:background="@drawable/selector_savebutton_background"
|
||||
android:padding="10dp"
|
||||
android:text="@string/save_this" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/reading_shared_container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
12
media/android/NewsBlur/res/menu/context_story.xml
Normal file
12
media/android/NewsBlur/res/menu/context_story.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/menu_mark_story_as_read"
|
||||
android:title="@string/menu_mark_story_as_read" />
|
||||
|
||||
<item android:id="@+id/menu_mark_previous_stories_as_read"
|
||||
android:title="@string/menu_mark_previous_stories_as_read" />
|
||||
|
||||
<!-- TODO: Share/Unshare, Save/Unsave, mark unread -->
|
||||
|
||||
</menu>
|
|
@ -59,6 +59,8 @@
|
|||
<string name="error_sharing">There was an problem sharing this story.</string>
|
||||
<string name="share_newsblur">Share \"%s\" to your Blurblog?</string>
|
||||
|
||||
<string name="save_this">Save this story</string>
|
||||
|
||||
<string name="reply_to">Reply to \"%s\"</string>
|
||||
|
||||
<string name="alert_dialog_ok">Okay</string>
|
||||
|
@ -93,6 +95,8 @@
|
|||
<string name="menu_sharenewsblur">Share this story</string>
|
||||
<string name="menu_textsize">Adjust text size</string>
|
||||
<string name="menu_save_story">Save this story</string>
|
||||
<string name="menu_mark_previous_stories_as_read">Mark previous as read</string>
|
||||
<string name="menu_mark_story_as_read">Mark as read</string>
|
||||
|
||||
<string name="toast_marked_folder_as_read">Folder marked as read</string>
|
||||
<string name="toast_marked_feed_as_read">Feed marked as read</string>
|
||||
|
|
|
@ -35,6 +35,18 @@
|
|||
<item name="android:padding">10dp</item>
|
||||
</style>
|
||||
|
||||
<style name="saveButton" parent="@android:style/Widget.Button">
|
||||
<item name="android:background">@drawable/selector_sharebutton_background</item>
|
||||
<item name="android:textColor">@color/button_text_selector</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:shadowDx">0</item>
|
||||
<item name="android:shadowDy">0</item>
|
||||
<item name="android:shadowRadius">5</item>
|
||||
<item name="android:shadowColor">#003366</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:padding">10dp</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="button" parent="@android:style/Widget.Button">
|
||||
<item name="android:background">@drawable/selector_button_background</item>
|
||||
|
|
|
@ -69,11 +69,10 @@ public class FeedItemsList extends ItemsList {
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (!super.onOptionsItemSelected(item)) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_delete_feed:
|
||||
if (item.getItemId() == R.id.menu_delete_feed) {
|
||||
deleteFeed();
|
||||
return true;
|
||||
default:
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -62,12 +62,10 @@ public abstract class ItemsList extends SherlockFragmentActivity implements Sync
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
|
||||
case R.id.menu_mark_all_as_read:
|
||||
} else if (item.getItemId() == R.id.menu_mark_all_as_read) {
|
||||
markItemListAsRead();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -90,19 +90,18 @@ public class Main extends SherlockFragmentActivity implements StateChangedListen
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_profile:
|
||||
if (item.getItemId() == R.id.menu_profile) {
|
||||
Intent profileIntent = new Intent(this, Profile.class);
|
||||
startActivity(profileIntent);
|
||||
return true;
|
||||
case R.id.menu_refresh:
|
||||
} else if (item.getItemId() == R.id.menu_refresh) {
|
||||
triggerRecount();
|
||||
return true;
|
||||
case R.id.menu_add_feed:
|
||||
} else if (item.getItemId() == R.id.menu_add_feed) {
|
||||
Intent intent = new Intent(this, SearchForFeeds.class);
|
||||
startActivityForResult(intent, 0);
|
||||
return true;
|
||||
case R.id.menu_logout:
|
||||
} else if (item.getItemId() == R.id.menu_logout) {
|
||||
DialogFragment newFragment = new LogoutDialogFragment();
|
||||
newFragment.show(getSupportFragmentManager(), "dialog");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import com.newsblur.fragment.SyncUpdateFragment;
|
|||
import com.newsblur.fragment.TextSizeDialogFragment;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
@ -119,25 +120,24 @@ public abstract class Reading extends SherlockFragmentActivity implements OnPage
|
|||
Story story = readingAdapter.getStory(currentItem);
|
||||
UserDetails user = PrefsUtils.getUserDetails(this);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_reading_original:
|
||||
} else if (item.getItemId() == R.id.menu_reading_original) {
|
||||
if (story != null) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(story.permalink));
|
||||
startActivity(i);
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_reading_sharenewsblur:
|
||||
} else if (item.getItemId() == R.id.menu_reading_sharenewsblur) {
|
||||
if (story != null) {
|
||||
ReadingItemFragment currentFragment = (ReadingItemFragment) readingAdapter.instantiateItem(pager, currentItem);
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(currentFragment, story, currentFragment.previouslySavedShareText);
|
||||
newFragment.show(getSupportFragmentManager(), "dialog");
|
||||
}
|
||||
return true;
|
||||
case R.id.menu_shared:
|
||||
} else if (item.getItemId() == R.id.menu_shared) {
|
||||
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
|
||||
|
@ -146,35 +146,15 @@ public abstract class Reading extends SherlockFragmentActivity implements OnPage
|
|||
intent.putExtra(Intent.EXTRA_TEXT, String.format(shareString, new Object[] { story.title, story.permalink }));
|
||||
startActivity(Intent.createChooser(intent, "Share using"));
|
||||
return true;
|
||||
case R.id.menu_textsize:
|
||||
} else if (item.getItemId() == R.id.menu_textsize) {
|
||||
float currentValue = getSharedPreferences(PrefConstants.PREFERENCES, 0).getFloat(PrefConstants.PREFERENCE_TEXT_SIZE, 0.5f);
|
||||
TextSizeDialogFragment textSize = TextSizeDialogFragment.newInstance(currentValue);
|
||||
textSize.show(getSupportFragmentManager(), TEXT_SIZE);
|
||||
return true;
|
||||
case R.id.menu_reading_save:
|
||||
if (story != null) {
|
||||
final String feedId = story.feedId;
|
||||
final String storyId = story.id;
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... arg) {
|
||||
return apiManager.markStoryAsStarred(feedId, storyId);
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result) {
|
||||
Toast.makeText(Reading.this, R.string.toast_story_saved, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(Reading.this, R.string.toast_story_save_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
} else {
|
||||
Log.w(this.getClass().getName(), "Couldn't save story, no selection found.");
|
||||
}
|
||||
} else if (item.getItemId() == R.id.menu_reading_save) {
|
||||
FeedUtils.saveStory(story, Reading.this, apiManager);
|
||||
return true;
|
||||
|
||||
default:
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,16 +83,12 @@ public class SearchForFeeds extends SherlockFragmentActivity implements LoaderCa
|
|||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case R.id.menu_search:
|
||||
if (item.getItemId() == R.id.menu_search) {
|
||||
onSearchRequested();
|
||||
return true;
|
||||
|
||||
case android.R.id.home:
|
||||
} else if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.newsblur.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
|
@ -82,4 +85,20 @@ public class FeedItemsAdapter extends SimpleCursorAdapter {
|
|||
return v;
|
||||
}
|
||||
|
||||
public Story getStory(int position) {
|
||||
cursor.moveToPosition(position);
|
||||
return Story.fromCursor(cursor);
|
||||
}
|
||||
|
||||
public ArrayList<Story> getPreviousStories(int position) {
|
||||
ArrayList<Story> stories = new ArrayList<Story>();
|
||||
cursor.moveToPosition(0);
|
||||
for(int i=0;i<=position && position < cursor.getCount();i++) {
|
||||
Story story = Story.fromCursor(cursor);
|
||||
stories.add(story);
|
||||
cursor.moveToNext();
|
||||
}
|
||||
return stories;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.newsblur.database;
|
||||
|
||||
import com.newsblur.util.AppConstants;
|
||||
|
||||
import android.R.string;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
|
@ -11,6 +10,8 @@ import android.net.Uri;
|
|||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.newsblur.util.AppConstants;
|
||||
|
||||
public class FeedProvider extends ContentProvider {
|
||||
|
||||
private static final String TAG = "FeedProvider";
|
||||
|
@ -146,6 +147,41 @@ public class FeedProvider extends ContentProvider {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri uri, ContentValues[] valuesArray) {
|
||||
int count = 0;
|
||||
final SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case ALL_FOLDERS:
|
||||
db.beginTransaction();
|
||||
try {
|
||||
for(ContentValues values: valuesArray) {
|
||||
db.insertWithOnConflict(DatabaseConstants.FEED_FOLDER_MAP_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
count++;
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
break;
|
||||
case ALL_SOCIAL_FEEDS:
|
||||
db.beginTransaction();
|
||||
try {
|
||||
for(ContentValues values: valuesArray) {
|
||||
db.insertWithOnConflict(DatabaseConstants.SOCIALFEED_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
count++;
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
count = super.bulkInsert(uri, valuesArray);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
final SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
@ -9,9 +12,13 @@ import android.support.v4.app.LoaderManager;
|
|||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.support.v4.widget.SimpleCursorAdapter;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AbsListView.OnScrollListener;
|
||||
|
@ -27,16 +34,18 @@ import com.newsblur.database.DatabaseConstants;
|
|||
import com.newsblur.database.FeedItemsAdapter;
|
||||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.network.MarkStoryAsReadTask;
|
||||
import com.newsblur.util.NetworkUtils;
|
||||
import com.newsblur.view.FeedItemViewBinder;
|
||||
|
||||
public class FeedItemListFragment extends ItemListFragment implements LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, OnScrollListener {
|
||||
public class FeedItemListFragment extends ItemListFragment implements LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, OnScrollListener, OnCreateContextMenuListener {
|
||||
|
||||
private static final String TAG = "itemListFragment";
|
||||
public static final String FRAGMENT_TAG = "itemListFragment";
|
||||
private ContentResolver contentResolver;
|
||||
private String feedId;
|
||||
private SimpleCursorAdapter adapter;
|
||||
private FeedItemsAdapter adapter;
|
||||
private Uri storiesUri;
|
||||
private int currentState;
|
||||
private int currentPage = 1;
|
||||
|
@ -95,6 +104,7 @@ public class FeedItemListFragment extends ItemListFragment implements LoaderMana
|
|||
adapter.setViewBinder(new FeedItemViewBinder(getActivity()));
|
||||
itemList.setAdapter(adapter);
|
||||
itemList.setOnItemClickListener(this);
|
||||
itemList.setOnCreateContextMenuListener(this);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
@ -142,7 +152,11 @@ public class FeedItemListFragment extends ItemListFragment implements LoaderMana
|
|||
|
||||
public void changeState(int state) {
|
||||
currentState = state;
|
||||
final String selection = FeedProvider.getStorySelectionFromState(state);
|
||||
refreshStories();
|
||||
}
|
||||
|
||||
private void refreshStories() {
|
||||
final String selection = FeedProvider.getStorySelectionFromState(currentState);
|
||||
Cursor cursor = contentResolver.query(storiesUri, null, selection, null, DatabaseConstants.STORY_DATE + " DESC");
|
||||
adapter.swapCursor(cursor);
|
||||
}
|
||||
|
@ -160,5 +174,57 @@ public class FeedItemListFragment extends ItemListFragment implements LoaderMana
|
|||
public void onScrollStateChanged(AbsListView view, int scrollState) { }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
final AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
|
||||
if (item.getItemId() == R.id.menu_mark_story_as_read) {
|
||||
final Story story = adapter.getStory(menuInfo.position);
|
||||
ArrayList<String> storyIdsToMarkRead = new ArrayList<String>();
|
||||
storyIdsToMarkRead.add(story.id);
|
||||
new MarkStoryAsReadTask(getActivity(), storyIdsToMarkRead, feedId) {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
// TODO this isn't sufficient. We also need to update counts
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_READ, true);
|
||||
contentResolver.update(FeedProvider.STORY_URI.buildUpon().appendPath(story.id).build(), values, null, null);
|
||||
refreshStories();
|
||||
}
|
||||
|
||||
}.execute();
|
||||
} else if (item.getItemId() == R.id.menu_mark_previous_stories_as_read) {
|
||||
ArrayList<Story> previousStories = adapter.getPreviousStories(menuInfo.position);
|
||||
final ArrayList<String> storyIdsToMarkRead = new ArrayList<String>();
|
||||
for(Story story: previousStories) {
|
||||
if(story.read == 0) {
|
||||
storyIdsToMarkRead.add(story.id);
|
||||
}
|
||||
}
|
||||
new MarkStoryAsReadTask(getActivity(), storyIdsToMarkRead, feedId) {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
// TODO this isn't sufficient. We also need to update counts
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_READ, true);
|
||||
for(String storyId: storyIdsToMarkRead) {
|
||||
contentResolver.update(FeedProvider.STORY_URI.buildUpon().appendPath(storyId).build(), values, null, null);
|
||||
}
|
||||
refreshStories();
|
||||
}
|
||||
|
||||
}.execute();
|
||||
}
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
|
||||
inflater.inflate(R.menu.context_story, menu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.newsblur.activity.AllStoriesItemsList;
|
|||
import com.newsblur.activity.FeedItemsList;
|
||||
import com.newsblur.activity.ItemsList;
|
||||
import com.newsblur.activity.Main;
|
||||
import com.newsblur.activity.NewsBlurApplication;
|
||||
import com.newsblur.activity.SocialFeedItemsList;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.database.FeedProvider;
|
||||
|
@ -36,6 +37,7 @@ import com.newsblur.network.MarkFeedAsReadTask;
|
|||
import com.newsblur.network.MarkFolderAsReadTask;
|
||||
import com.newsblur.network.MarkSocialFeedAsReadTask;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.ImageLoader;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.view.FolderTreeViewBinder;
|
||||
|
@ -64,7 +66,8 @@ public class FolderListFragment extends Fragment implements OnGroupClickListener
|
|||
Cursor countCursor = resolver.query(FeedProvider.FEED_COUNT_URI, null, DatabaseConstants.SOCIAL_INTELLIGENCE_SOME, null, null);
|
||||
Cursor sharedCountCursor = resolver.query(FeedProvider.SOCIALCOUNT_URI, null, DatabaseConstants.SOCIAL_INTELLIGENCE_SOME, null, null);
|
||||
|
||||
groupViewBinder = new FolderTreeViewBinder();
|
||||
ImageLoader imageLoader = ((NewsBlurApplication) getActivity().getApplicationContext()).getImageLoader();
|
||||
groupViewBinder = new FolderTreeViewBinder(imageLoader);
|
||||
blogViewBinder = new SocialFeedViewBinder(getActivity());
|
||||
|
||||
leftBound = UIUtils.convertDPsToPixels(getActivity(), 20);
|
||||
|
@ -72,7 +75,7 @@ public class FolderListFragment extends Fragment implements OnGroupClickListener
|
|||
|
||||
final String[] groupFrom = new String[] { DatabaseConstants.FOLDER_NAME, DatabaseConstants.SUM_POS, DatabaseConstants.SUM_NEUT };
|
||||
final int[] groupTo = new int[] { R.id.row_foldername, R.id.row_foldersumpos, R.id.row_foldersumneu };
|
||||
final String[] childFrom = new String[] { DatabaseConstants.FEED_TITLE, DatabaseConstants.FEED_FAVICON, DatabaseConstants.FEED_NEUTRAL_COUNT, DatabaseConstants.FEED_POSITIVE_COUNT };
|
||||
final String[] childFrom = new String[] { DatabaseConstants.FEED_TITLE, DatabaseConstants.FEED_FAVICON_URL, DatabaseConstants.FEED_NEUTRAL_COUNT, DatabaseConstants.FEED_POSITIVE_COUNT };
|
||||
final int[] childTo = new int[] { R.id.row_feedname, R.id.row_feedfavicon, R.id.row_feedneutral, R.id.row_feedpositive };
|
||||
final String[] blogFrom = new String[] { DatabaseConstants.SOCIAL_FEED_TITLE, DatabaseConstants.SOCIAL_FEED_ICON, DatabaseConstants.SOCIAL_FEED_NEUTRAL_COUNT, DatabaseConstants.SOCIAL_FEED_POSITIVE_COUNT };
|
||||
final int[] blogTo = new int[] { R.id.row_socialfeed_name, R.id.row_socialfeed_icon, R.id.row_socialsumneu, R.id.row_socialsumpos };
|
||||
|
@ -152,9 +155,7 @@ public class FolderListFragment extends Fragment implements OnGroupClickListener
|
|||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
final ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item.getMenuInfo();
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case R.id.menu_mark_feed_as_read:
|
||||
if (item.getItemId() == R.id.menu_mark_feed_as_read) {
|
||||
new MarkFeedAsReadTask(getActivity(), apiManager) {
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
|
@ -172,13 +173,11 @@ public class FolderListFragment extends Fragment implements OnGroupClickListener
|
|||
}
|
||||
}.execute(Long.toString(info.id));
|
||||
return true;
|
||||
|
||||
case R.id.menu_delete_feed:
|
||||
} else if (item.getItemId() == R.id.menu_delete_feed) {
|
||||
Toast.makeText(getActivity(), "Deleted feed", Toast.LENGTH_SHORT).show();
|
||||
((Main) getActivity()).deleteFeed(info.id, null);
|
||||
return true;
|
||||
|
||||
case R.id.menu_mark_folder_as_read:
|
||||
} else if (item.getItemId() == R.id.menu_mark_folder_as_read) {
|
||||
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
|
||||
if (folderAdapter.isExpandable(groupPosition)) {
|
||||
final Cursor folderCursor = ((MixedExpandableListAdapter) list.getExpandableListAdapter()).getGroup(groupPosition);
|
||||
|
|
|
@ -98,13 +98,10 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
|||
|
||||
@Override
|
||||
public void onClick(View viewClicked) {
|
||||
switch (viewClicked.getId()) {
|
||||
case R.id.login_button:
|
||||
if (viewClicked.getId() == R.id.login_button) {
|
||||
logIn();
|
||||
break;
|
||||
case R.id.registration_button:
|
||||
} else if (viewClicked.getId() == R.id.registration_button) {
|
||||
signUp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,13 +179,10 @@ public class ProfileDetailsFragment extends Fragment implements OnClickListener
|
|||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.profile_follow_button:
|
||||
if (v.getId() == R.id.profile_follow_button) {
|
||||
new FollowTask().execute();
|
||||
break;
|
||||
case R.id.profile_unfollow_button:
|
||||
} else if (v.getId() == R.id.profile_unfollow_button) {
|
||||
new UnfollowTask().execute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,14 @@ import android.widget.TextView;
|
|||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.NewsBlurApplication;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.SetupCommentSectionTask;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.ImageLoader;
|
||||
import com.newsblur.util.PrefConstants;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
|
@ -119,6 +121,7 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
setupWebview(web);
|
||||
setupItemMetadata();
|
||||
setupShareButton();
|
||||
setupSaveButton();
|
||||
|
||||
if (story.sharedUserIds.length > 0 || story.commentCount > 0 ) {
|
||||
view.findViewById(R.id.reading_share_bar).setVisibility(View.VISIBLE);
|
||||
|
@ -129,6 +132,18 @@ public class ReadingItemFragment extends Fragment implements ClassifierDialogFra
|
|||
return view;
|
||||
}
|
||||
|
||||
private void setupSaveButton() {
|
||||
|
||||
Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
|
||||
saveButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FeedUtils.saveStory(story, getActivity(), apiManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupShareButton() {
|
||||
|
||||
Button shareButton = (Button) view.findViewById(R.id.share_story_button);
|
||||
|
|
|
@ -6,8 +6,7 @@ public class APIConstants {
|
|||
// they are not.
|
||||
|
||||
public static final String URL_LOGIN = "http://newsblur.com/api/login";
|
||||
public static final String URL_FEEDS = "http://newsblur.com/reader/feeds/?include_favicons=true&flat=true";
|
||||
public static final String URL_FEEDS_NO_UPDATE = "http://newsblur.com/reader/feeds/?include_favicons=true&flat=true&update_counts=false";
|
||||
public static final String URL_FEEDS = "http://newsblur.com/reader/feeds/";
|
||||
public static final String URL_USER_PROFILE = "http://newsblur.com/social/profile";
|
||||
public static final String URL_MY_PROFILE = "http://newsblur.com/social/load_user_profile";
|
||||
public static final String URL_FOLLOW = "http://newsblur.com/social/follow";
|
||||
|
|
|
@ -48,16 +48,22 @@ public class APIManager {
|
|||
|
||||
private static final String TAG = "APIManager";
|
||||
private Context context;
|
||||
private Gson gson;
|
||||
private static Gson gson;
|
||||
private ContentResolver contentResolver;
|
||||
|
||||
public APIManager(final Context context) {
|
||||
this.context = context;
|
||||
contentResolver = context.getContentResolver();
|
||||
initGsonIfNeededBuilder();
|
||||
}
|
||||
|
||||
private static synchronized void initGsonIfNeededBuilder() {
|
||||
if(gson == null) {
|
||||
final GsonBuilder builder = new GsonBuilder();
|
||||
builder.registerTypeAdapter(Date.class, new DateStringTypeAdapter());
|
||||
contentResolver = context.getContentResolver();
|
||||
gson = builder.create();
|
||||
}
|
||||
}
|
||||
|
||||
public LoginResponse login(final String username, final String password) {
|
||||
final APIClient client = new APIClient(context);
|
||||
|
@ -445,10 +451,9 @@ public class APIManager {
|
|||
|
||||
public boolean getFolderFeedMapping(boolean doUpdateCounts) {
|
||||
|
||||
|
||||
final APIClient client = new APIClient(context);
|
||||
final APIResponse response = client.get(doUpdateCounts ? APIConstants.URL_FEEDS : APIConstants.URL_FEEDS_NO_UPDATE);
|
||||
final FeedFolderResponse feedUpdate = gson.fromJson(response.responseString, FeedFolderResponse.class);
|
||||
final APIResponse response = client.get(APIConstants.URL_FEEDS);
|
||||
final FeedFolderResponse feedUpdate = new FeedFolderResponse(response.responseString, gson);
|
||||
|
||||
if (response.responseCode == HttpStatus.SC_OK && !response.hasRedirected) {
|
||||
if (feedUpdate.folders.size() == 0) {
|
||||
|
@ -457,11 +462,15 @@ public class APIManager {
|
|||
|
||||
HashMap<String, Feed> existingFeeds = getExistingFeeds();
|
||||
|
||||
List<ContentValues> feedValues = new ArrayList<ContentValues>();
|
||||
for (String newFeedId : feedUpdate.feeds.keySet()) {
|
||||
if (existingFeeds.get(newFeedId) == null || !feedUpdate.feeds.get(newFeedId).equals(existingFeeds.get(newFeedId))) {
|
||||
contentResolver.insert(FeedProvider.FEEDS_URI, feedUpdate.feeds.get(newFeedId).getValues());
|
||||
feedValues.add(feedUpdate.feeds.get(newFeedId).getValues());
|
||||
}
|
||||
}
|
||||
if(feedValues.size() > 0) {
|
||||
contentResolver.bulkInsert(FeedProvider.FEEDS_URI, feedValues.toArray(new ContentValues[feedValues.size()]));
|
||||
}
|
||||
|
||||
for (String olderFeedId : existingFeeds.keySet()) {
|
||||
if (feedUpdate.feeds.get(olderFeedId) == null) {
|
||||
|
@ -470,10 +479,13 @@ public class APIManager {
|
|||
}
|
||||
}
|
||||
|
||||
List<ContentValues> socialFeedValues = new ArrayList<ContentValues>();
|
||||
for (final SocialFeed feed : feedUpdate.socialFeeds) {
|
||||
contentResolver.insert(FeedProvider.SOCIAL_FEEDS_URI, feed.getValues());
|
||||
socialFeedValues.add(feed.getValues());
|
||||
}
|
||||
if(socialFeedValues.size() > 0) {
|
||||
contentResolver.bulkInsert(FeedProvider.SOCIAL_FEEDS_URI, socialFeedValues.toArray(new ContentValues[socialFeedValues.size()]));
|
||||
}
|
||||
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FOLDERS_URI, null, null, null, null);
|
||||
folderCursor.moveToFirst();
|
||||
|
|
|
@ -16,6 +16,10 @@ public class MarkStoryAsReadTask extends AsyncTask<Void, Void, Void> {
|
|||
private final String feedId;
|
||||
private final ArrayList<String> storyIds;
|
||||
|
||||
public MarkStoryAsReadTask(final Context context, final ArrayList<String> storyIds, final String feedId) {
|
||||
this(context, null, storyIds, feedId);
|
||||
}
|
||||
|
||||
public MarkStoryAsReadTask(final Context context, final SyncUpdateFragment fragment, final ArrayList<String> storyIds, final String feedId) {
|
||||
this.context = context;
|
||||
this.receiver = fragment;
|
||||
|
@ -26,7 +30,9 @@ public class MarkStoryAsReadTask extends AsyncTask<Void, Void, Void> {
|
|||
protected Void doInBackground(Void... stories) {
|
||||
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, context, SyncService.class);
|
||||
if(receiver != null) {
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, receiver.receiver);
|
||||
}
|
||||
intent.putExtra(SyncService.SYNCSERVICE_TASK, SyncService.EXTRA_TASK_MARK_STORY_READ);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_FEED_ID, feedId);
|
||||
intent.putStringArrayListExtra(SyncService.EXTRA_TASK_STORY_ID, storyIds);
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
package com.newsblur.network.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.SocialFeed;
|
||||
|
@ -21,5 +31,88 @@ public class FeedFolderResponse {
|
|||
@SerializedName("social_feeds")
|
||||
public SocialFeed[] socialFeeds;
|
||||
|
||||
public FeedFolderResponse() {
|
||||
}
|
||||
|
||||
public FeedFolderResponse(String json, Gson gson) {
|
||||
// This is a mess but I don't see a way to parse the mixed content in
|
||||
// folders w/o going to low level Gson API
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject asJsonObject = parser.parse(json).getAsJsonObject();
|
||||
JsonArray jsonFoldersArray = (JsonArray) asJsonObject.get("folders");
|
||||
ArrayList<String> nestedFolderList = new ArrayList<String>();
|
||||
folders = new HashMap<String, List<Long>>();
|
||||
parseFeedArray(nestedFolderList, folders, null, jsonFoldersArray);
|
||||
|
||||
JsonElement starredCountElement = asJsonObject.get("starred_count");
|
||||
if(starredCountElement != null) {
|
||||
starredCount = gson.fromJson(starredCountElement, int.class);
|
||||
}
|
||||
|
||||
JsonObject feedsElement = (JsonObject) asJsonObject.get("feeds");
|
||||
if(feedsElement != null) {
|
||||
feeds = new HashMap<String, Feed>();
|
||||
Set<Entry<String, JsonElement>> entrySet = feedsElement.entrySet();
|
||||
Iterator<Entry<String, JsonElement>> iterator = entrySet.iterator();
|
||||
while(iterator.hasNext()) {
|
||||
Entry<String, JsonElement> feedElement = iterator.next();
|
||||
Feed feed = gson.fromJson(feedElement.getValue(), Feed.class);
|
||||
feeds.put(feedElement.getKey(), feed);
|
||||
}
|
||||
}
|
||||
|
||||
JsonArray socialFeedsArray = (JsonArray) asJsonObject.get("social_feeds");
|
||||
if(socialFeedsArray != null) {
|
||||
List<SocialFeed> socialFeedsList = new ArrayList<SocialFeed>();
|
||||
for(int i=0;i<socialFeedsArray.size();i++) {
|
||||
JsonElement jsonElement = socialFeedsArray.get(i);
|
||||
SocialFeed socialFeed = gson.fromJson(jsonElement, SocialFeed.class);
|
||||
socialFeedsList.add(socialFeed);
|
||||
}
|
||||
socialFeeds = socialFeedsList.toArray(new SocialFeed[socialFeedsArray.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFeed(JsonObject asJsonObject2, List<String> parentFeedNames, Map<String, List<Long>> folders) {
|
||||
Set<Entry<String, JsonElement>> entrySet = asJsonObject2.entrySet();
|
||||
Iterator<Entry<String, JsonElement>> iterator = entrySet.iterator();
|
||||
while(iterator.hasNext()) {
|
||||
Entry<String, JsonElement> next = iterator.next();
|
||||
String key = next.getKey();
|
||||
JsonArray value = (JsonArray) next.getValue();
|
||||
parseFeedArray(parentFeedNames, folders, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFeedArray(List<String> nestedFolderList,
|
||||
Map<String, List<Long>> folders, String name, JsonArray arrayValue) {
|
||||
String fullFolderName = getFolderName(name, nestedFolderList);
|
||||
ArrayList<Long> feedIds = new ArrayList<Long>();
|
||||
for(int k=0;k<arrayValue.size();k++) {
|
||||
JsonElement jsonElement = arrayValue.get(k);
|
||||
if(jsonElement.isJsonPrimitive()) {
|
||||
feedIds.add(jsonElement.getAsLong());
|
||||
} else {
|
||||
List<String> nestedFolerListCopy = new ArrayList<String>(nestedFolderList);
|
||||
if(name != null) {
|
||||
nestedFolerListCopy.add(name);
|
||||
}
|
||||
parseFeed((JsonObject) jsonElement, nestedFolerListCopy, folders);
|
||||
}
|
||||
}
|
||||
folders.put(fullFolderName, feedIds);
|
||||
}
|
||||
|
||||
private String getFolderName(String key, List<String> parentFeedNames) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for(String parentFolder: parentFeedNames) {
|
||||
builder.append(parentFolder);
|
||||
builder.append(" - ");
|
||||
}
|
||||
if(key != null) {
|
||||
builder.append(key);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
36
media/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
Normal file
36
media/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package com.newsblur.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.network.APIManager;
|
||||
|
||||
public class FeedUtils {
|
||||
|
||||
public static void saveStory(final Story story, final Context context, final APIManager apiManager) {
|
||||
if (story != null) {
|
||||
final String feedId = story.feedId;
|
||||
final String storyId = story.id;
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... arg) {
|
||||
return apiManager.markStoryAsStarred(feedId, storyId);
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result) {
|
||||
Toast.makeText(context, R.string.toast_story_saved, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(context, R.string.toast_story_save_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
} else {
|
||||
Log.w(FeedUtils.class.getName(), "Couldn't save story, no selection found.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageView;
|
||||
|
@ -17,24 +16,28 @@ import com.newsblur.R;
|
|||
import com.newsblur.activity.FolderItemsList;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.ImageLoader;
|
||||
|
||||
public class FolderTreeViewBinder implements ViewBinder {
|
||||
|
||||
private int currentState = AppConstants.STATE_SOME;
|
||||
private int READING_RETURNED = 0x02;
|
||||
private final ImageLoader imageLoader;
|
||||
|
||||
public FolderTreeViewBinder(ImageLoader imageLoader) {
|
||||
this.imageLoader = imageLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
|
||||
if (TextUtils.equals(cursor.getColumnName(columnIndex), DatabaseConstants.FEED_FAVICON)) {
|
||||
Bitmap bitmap = null;
|
||||
if (cursor.getBlob(columnIndex) != null) {
|
||||
final byte[] data = Base64.decode(cursor.getBlob(columnIndex), Base64.DEFAULT);
|
||||
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
|
||||
}
|
||||
if (bitmap == null) {
|
||||
bitmap = BitmapFactory.decodeResource(view.getContext().getResources(), R.drawable.world);
|
||||
}
|
||||
if (TextUtils.equals(cursor.getColumnName(columnIndex), DatabaseConstants.FEED_FAVICON_URL)) {
|
||||
if (cursor.getString(columnIndex) != null) {
|
||||
String imageUrl = cursor.getString(columnIndex);
|
||||
imageLoader.displayImage(imageUrl, (ImageView)view);
|
||||
} else {
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(view.getContext().getResources(), R.drawable.world);
|
||||
((ImageView) view).setImageBitmap(bitmap);
|
||||
}
|
||||
return true;
|
||||
} else if (TextUtils.equals(cursor.getColumnName(columnIndex), DatabaseConstants.FEED_POSITIVE_COUNT) || TextUtils.equals(cursor.getColumnName(columnIndex), DatabaseConstants.SUM_POS)) {
|
||||
int feedPositive = cursor.getInt(columnIndex);
|
||||
|
|
|
@ -64,25 +64,21 @@ public class StateToggleButton extends LinearLayout implements OnClickListener {
|
|||
}
|
||||
|
||||
public void setState(final int state) {
|
||||
switch (state) {
|
||||
case R.id.toggle_all:
|
||||
if (state == R.id.toggle_all) {
|
||||
allButton.setEnabled(false);
|
||||
someButton.setEnabled(true);
|
||||
focusButton.setEnabled(true);
|
||||
CURRENT_STATE = AppConstants.STATE_ALL;
|
||||
break;
|
||||
case R.id.toggle_some:
|
||||
} else if (state == R.id.toggle_some) {
|
||||
allButton.setEnabled(true);
|
||||
someButton.setEnabled(false);
|
||||
focusButton.setEnabled(true);
|
||||
CURRENT_STATE = AppConstants.STATE_SOME;
|
||||
break;
|
||||
case R.id.toggle_focus:
|
||||
} else if (state == R.id.toggle_focus) {
|
||||
allButton.setEnabled(true);
|
||||
someButton.setEnabled(true);
|
||||
focusButton.setEnabled(false);
|
||||
CURRENT_STATE = AppConstants.STATE_BEST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue