mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
Added basic reading-item carousel for feed-specific reading lists, including ability to load and save items, view originals, cache content, and load custom CSS stylesheet for the content.
This commit is contained in:
parent
e20d58a949
commit
7212604e7a
31 changed files with 475 additions and 84 deletions
|
@ -34,6 +34,9 @@
|
|||
<activity
|
||||
android:name=".activity.Profile"
|
||||
android:label="@string/profile"/>
|
||||
|
||||
<activity
|
||||
android:name=".activity.Reading"/>
|
||||
|
||||
<service android:name=".service.SyncService" />
|
||||
|
||||
|
|
8
media/android/NewsBlur/assets/reading.css
Normal file
8
media/android/NewsBlur/assets/reading.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
body {
|
||||
font-size: 2.0em;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
BIN
media/android/NewsBlur/res/drawable-hdpi/share.png
Normal file
BIN
media/android/NewsBlur/res/drawable-hdpi/share.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
media/android/NewsBlur/res/drawable-hdpi/website.png
Normal file
BIN
media/android/NewsBlur/res/drawable-hdpi/website.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
media/android/NewsBlur/res/drawable-mdpi/share.png
Normal file
BIN
media/android/NewsBlur/res/drawable-mdpi/share.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
BIN
media/android/NewsBlur/res/drawable-mdpi/website.png
Normal file
BIN
media/android/NewsBlur/res/drawable-mdpi/website.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
media/android/NewsBlur/res/drawable-xhdpi/share.png
Normal file
BIN
media/android/NewsBlur/res/drawable-xhdpi/share.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
media/android/NewsBlur/res/drawable-xhdpi/website.png
Normal file
BIN
media/android/NewsBlur/res/drawable-xhdpi/website.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
|
||||
<solid android:color="@color/negative"/>
|
||||
<stroke android:width="0.5dp" android:color="@color/darkgray"/>
|
||||
</shape>
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
|
||||
<solid android:color="@color/neutral" />
|
||||
<stroke android:width="0.5dp" android:color="@color/darkgray"/>
|
||||
</shape>
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
|
||||
<solid android:color="@color/positive"/>
|
||||
<stroke android:width="0.5dp" android:color="@color/darkgray"/>
|
||||
</shape>
|
14
media/android/NewsBlur/res/layout/activity_reading.xml
Normal file
14
media/android/NewsBlur/res/layout/activity_reading.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="4dip" >
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/reading_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
|
@ -2,7 +2,7 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@color/darkgray"
|
||||
android:background="@drawable/list_background"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
|
@ -19,7 +19,7 @@
|
|||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/list_background"
|
||||
android:divider="@drawable/divider_dark"
|
||||
android:dividerHeight="1px" />
|
||||
android:divider="@color/transparent"
|
||||
android:dividerHeight="1dp" />
|
||||
|
||||
</LinearLayout>
|
13
media/android/NewsBlur/res/layout/fragment_readingitem.xml
Normal file
13
media/android/NewsBlur/res/layout/fragment_readingitem.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/list_background"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<WebView
|
||||
android:id="@+id/reading_webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
|
@ -36,8 +36,8 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/row_foldersumneg"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@drawable/negative_count_circle"
|
||||
android:gravity="center"
|
||||
|
@ -47,8 +47,8 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/row_foldersumneu"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@drawable/neutral_count_circle"
|
||||
android:gravity="center"
|
||||
|
@ -58,8 +58,8 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/row_foldersumpos"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/positive_count_circle"
|
||||
android:gravity="center"
|
||||
android:layout_marginRight="10dp"
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@color/lightgray" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/row_folder_icon"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@string/description_row_folder_icon"
|
||||
android:src="@drawable/folder" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/row_foldername"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_toRightOf="@id/row_folder_icon"
|
||||
android:textColor="@color/darkgray"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="35dp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/row_foldersumpos"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:textColor="@color/darkgray"
|
||||
android:textSize="5dp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
14
media/android/NewsBlur/res/menu/reading.xml
Normal file
14
media/android/NewsBlur/res/menu/reading.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/menu_reading_original"
|
||||
android:icon="@drawable/website"
|
||||
android:title="@string/menu_original"
|
||||
android:showAsAction="always" />
|
||||
|
||||
<item android:id="@+id/menu_shared"
|
||||
android:icon="@drawable/share"
|
||||
android:title="@string/menu_share"
|
||||
android:showAsAction="always" />
|
||||
|
||||
</menu>
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
<string name="menu_profile">Profile</string>
|
||||
<string name="menu_refresh">Refresh</string>
|
||||
<string name="menu_original">View original</string>
|
||||
<string name="menu_share">Share</string>
|
||||
|
||||
<string name="profile">Profile</string>
|
||||
<string name="profile_location_icon">Location icon</string>
|
||||
|
@ -42,6 +44,4 @@
|
|||
<string name="profile_with_comment">with the comment</string>
|
||||
<string name="profile_shared_story">Shared the story</string>
|
||||
|
||||
|
||||
|
||||
</resources>
|
|
@ -1,15 +1,167 @@
|
|||
package com.newsblur.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.database.ReadingAdapter;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.service.DetachableResultReceiver;
|
||||
import com.newsblur.service.DetachableResultReceiver.Receiver;
|
||||
import com.newsblur.service.SyncService;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class Reading extends SherlockFragmentActivity {
|
||||
|
||||
|
||||
public static final String EXTRA_FEED = "feed_selected";
|
||||
public static final String TAG = "ReadingActivity";
|
||||
private ViewPager pager;
|
||||
private SyncReadingUpdaterFragment syncFragment;
|
||||
private FragmentManager fragmentManager;
|
||||
private ReadingAdapter readingAdapter;
|
||||
private String feedId;
|
||||
private final int READING_LOADER = 0x01;
|
||||
private ContentResolver contentResolver;
|
||||
private Feed feed;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
super.onCreate(savedInstanceBundle);
|
||||
|
||||
setContentView(R.layout.activity_reading);
|
||||
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
feedId = getIntent().getStringExtra(EXTRA_FEED);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
readingAdapter = new ReadingAdapter(fragmentManager, this, feedId);
|
||||
getSupportLoaderManager().initLoader(READING_LOADER , null, readingAdapter);
|
||||
|
||||
contentResolver = getContentResolver();
|
||||
final Uri feedUri = FeedProvider.FEEDS_URI.buildUpon().appendPath(feedId).build();
|
||||
|
||||
feed = Feed.fromCursor(contentResolver.query(feedUri, null, null, null, null));
|
||||
setTitle(feed.title);
|
||||
|
||||
if (!TextUtils.isEmpty(feed.favicon)) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
final byte[] data = Base64.decode(feed.favicon, Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
|
||||
bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
|
||||
bitmap = UIUtils.roundCorners(bitmap, 15);
|
||||
getSupportActionBar().setLogo(new BitmapDrawable(bitmap));
|
||||
}
|
||||
|
||||
syncFragment = (SyncReadingUpdaterFragment) fragmentManager.findFragmentByTag(SyncReadingUpdaterFragment.TAG);
|
||||
if (syncFragment == null) {
|
||||
syncFragment = new SyncReadingUpdaterFragment();
|
||||
fragmentManager.beginTransaction().add(syncFragment, SyncReadingUpdaterFragment.TAG).commit();
|
||||
triggerRefresh();
|
||||
}
|
||||
|
||||
pager = (ViewPager) findViewById(R.id.reading_pager);
|
||||
pager.setAdapter(readingAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
MenuInflater inflater = getSupportMenuInflater();
|
||||
inflater.inflate(R.menu.reading, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void triggerRefresh() {
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, syncFragment.receiver);
|
||||
intent.putExtra(SyncService.SYNCSERVICE_TASK, SyncService.EXTRA_TASK_FEED_UPDATE);
|
||||
intent.putExtra(SyncService.EXTRA_TASK_FEED_ID, feedId);
|
||||
startService(intent);
|
||||
}
|
||||
|
||||
public void redrawUI() {
|
||||
Log.d(TAG, "Redrawing reading pager...");
|
||||
getSupportLoaderManager().restartLoader(READING_LOADER, null, readingAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
case R.id.menu_reading_original:
|
||||
int currentItem = pager.getCurrentItem();
|
||||
Story story = readingAdapter.getStory(currentItem);
|
||||
if (story != null) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(story.permalink));
|
||||
startActivity(i);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SyncReadingUpdaterFragment extends Fragment implements Receiver {
|
||||
public static final String TAG = "SyncReadingFragment";
|
||||
private DetachableResultReceiver receiver;
|
||||
|
||||
public SyncReadingUpdaterFragment() {
|
||||
receiver = new DetachableResultReceiver(new Handler());
|
||||
receiver.setReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
Log.d(TAG, "Creating syncfragment");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
Log.d(TAG, "Attached");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceiverResult(int resultCode, Bundle resultData) {
|
||||
switch (resultCode) {
|
||||
case SyncService.STATUS_FINISHED:
|
||||
Log.d(TAG, "Synchronisation finished.");
|
||||
((Reading) getActivity()).redrawUI();
|
||||
break;
|
||||
case SyncService.STATUS_RUNNING:
|
||||
Log.d(TAG, "Synchronisation running.");
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unrecognised response attempting to get reading data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ public class FeedProvider extends ContentProvider {
|
|||
public static final String VERSION = "v1";
|
||||
public static final Uri NEWSBLUR_URI = Uri.parse("content://" + AUTHORITY + "/" + VERSION);
|
||||
public static final Uri FEEDS_URI = Uri.parse("content://" + AUTHORITY + "/" + VERSION + "/feeds/");
|
||||
public static final Uri STORIES_URI = Uri.parse("content://" + AUTHORITY + "/" + VERSION + "/stories/");
|
||||
public static final Uri FEED_FOLDER_MAP_URI = Uri.parse("content://" + AUTHORITY + "/" + VERSION + "/feedfoldermap/");
|
||||
public static final Uri FOLDERS_URI = Uri.parse("content://" + AUTHORITY + "/" + VERSION + "/folders/");
|
||||
|
||||
|
@ -25,11 +26,12 @@ public class FeedProvider extends ContentProvider {
|
|||
private static final String TAG = "FeedProvider";
|
||||
|
||||
private static final int ALL_FEEDS = 0;
|
||||
private static final int SPECIFIC_FEED = 1;
|
||||
private static final int FEED_STORIES = 1;
|
||||
private static final int ALL_FOLDERS = 2;
|
||||
private static final int SPECIFIC_FOLDER = 3;
|
||||
private static final int FEED_FOLDER_MAP = 4;
|
||||
private static final int SPECIFIC_FEED_FOLDER_MAP = 5;
|
||||
private static final int INDIVIDUAL_FEED = 6;
|
||||
|
||||
private BlurDatabase databaseHelper;
|
||||
|
||||
|
@ -37,7 +39,8 @@ public class FeedProvider extends ContentProvider {
|
|||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/feeds/", ALL_FEEDS);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/feeds/*/", SPECIFIC_FEED);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/feeds/*/", INDIVIDUAL_FEED);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/stories/#/", FEED_STORIES);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/feedfoldermap/", FEED_FOLDER_MAP);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/feedfoldermap/*/", SPECIFIC_FEED_FOLDER_MAP);
|
||||
uriMatcher.addURI(AUTHORITY, VERSION + "/folders/", ALL_FOLDERS);
|
||||
|
@ -88,12 +91,13 @@ public class FeedProvider extends ContentProvider {
|
|||
break;
|
||||
|
||||
// Inserting a story
|
||||
case SPECIFIC_FEED:
|
||||
case FEED_STORIES:
|
||||
db.beginTransaction();
|
||||
db.insertWithOnConflict(DatabaseConstants.STORY_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
break;
|
||||
break;
|
||||
|
||||
case UriMatcher.NO_MATCH:
|
||||
Log.d(TAG, "No match found for URI: " + uri.toString());
|
||||
break;
|
||||
|
@ -124,14 +128,20 @@ public class FeedProvider extends ContentProvider {
|
|||
" ORDER BY " + DatabaseConstants.FEED_TABLE + "." + DatabaseConstants.FEED_TITLE + " COLLATE NOCASE", selectionArgs);
|
||||
break;
|
||||
|
||||
// Querying for a specific
|
||||
case SPECIFIC_FEED:
|
||||
selection = DatabaseConstants.FEED_ID + " = ?";
|
||||
// Query for a specific feed
|
||||
case INDIVIDUAL_FEED:
|
||||
cursor = db.rawQuery("SELECT " + TextUtils.join(",", DatabaseConstants.FEED_COLUMNS) + " FROM " + DatabaseConstants.FEED_TABLE +
|
||||
" WHERE " + DatabaseConstants.FEED_ID + "= '" + uri.getLastPathSegment() + "'", selectionArgs);
|
||||
break;
|
||||
|
||||
// Querying for a stories from a feed
|
||||
case FEED_STORIES:
|
||||
selection = DatabaseConstants.STORY_FEED_ID + " = ?";
|
||||
selectionArgs = new String[] { uri.getLastPathSegment() };
|
||||
cursor = db.query(DatabaseConstants.FEED_TABLE, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
cursor = db.query(DatabaseConstants.STORY_TABLE, null, selection, selectionArgs, null, null, null);
|
||||
break;
|
||||
|
||||
// Query for feeds with no folder mapping
|
||||
// Query for feeds with no folder mapping
|
||||
case FEED_FOLDER_MAP:
|
||||
String nullFolderQuery = "SELECT " + TextUtils.join(",", DatabaseConstants.FEED_COLUMNS) + " FROM " + DatabaseConstants.FEED_TABLE +
|
||||
" LEFT JOIN " + DatabaseConstants.FEED_FOLDER_MAP_TABLE +
|
||||
|
@ -148,7 +158,7 @@ public class FeedProvider extends ContentProvider {
|
|||
cursor = db.rawQuery(nullFolderBuilder.toString(), null);
|
||||
break;
|
||||
|
||||
// Querying for feeds for a given folder
|
||||
// Querying for feeds for a given folder
|
||||
case SPECIFIC_FEED_FOLDER_MAP:
|
||||
selection = DatabaseConstants.FOLDER_ID + " = ?";
|
||||
String[] folderArguments = new String[] { uri.getLastPathSegment() };
|
||||
|
@ -168,7 +178,7 @@ public class FeedProvider extends ContentProvider {
|
|||
cursor = db.rawQuery(builder.toString(), folderArguments);
|
||||
break;
|
||||
|
||||
// Querying for all folders with unread items
|
||||
// Querying for all folders with unread items
|
||||
case ALL_FOLDERS:
|
||||
String folderQuery = "SELECT " + TextUtils.join(",", DatabaseConstants.FOLDER_COLUMNS) + " FROM " + DatabaseConstants.FEED_FOLDER_MAP_TABLE +
|
||||
" LEFT JOIN " + DatabaseConstants.FOLDER_TABLE +
|
||||
|
@ -192,7 +202,6 @@ public class FeedProvider extends ContentProvider {
|
|||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,4 +32,5 @@ public class FolderTreeAdapter extends SimpleCursorTreeAdapter {
|
|||
return resolver.query(uri, null, null, new String[] { currentState }, null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
package com.newsblur.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
|
||||
public class ReadingAdapter extends FragmentStatePagerAdapter implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
private Context context;
|
||||
private Cursor cursor;
|
||||
private Uri feedUri;
|
||||
private String TAG = "ReadingAdapter";
|
||||
|
||||
public ReadingAdapter(final FragmentManager fragmentManager, final Context context, final String feedId) {
|
||||
super(fragmentManager);
|
||||
this.context = context;
|
||||
feedUri = FeedProvider.STORIES_URI.buildUpon().appendPath(feedId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
} else {
|
||||
cursor.moveToPosition(position);
|
||||
return new ReadingItemFragment(Story.fromCursor(cursor));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (cursor != null) {
|
||||
return cursor.getCount();
|
||||
} else {
|
||||
Log.d(TAG , "No cursor");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Story getStory(int position) {
|
||||
if (cursor == null || position > cursor.getCount()) {
|
||||
return null;
|
||||
} else {
|
||||
cursor.moveToPosition(position);
|
||||
return Story.fromCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
CursorLoader cursorLoader = new CursorLoader(context, feedUri, null, null, null, null);
|
||||
return cursorLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.domain;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
|
@ -63,5 +64,24 @@ public class Feed {
|
|||
values.put(DatabaseConstants.FEED_UPDATED_SECONDS, lastUpdated);
|
||||
return values;
|
||||
}
|
||||
|
||||
public static Feed fromCursor(Cursor childCursor) {
|
||||
Feed feed = new Feed();
|
||||
childCursor.moveToFirst();
|
||||
feed.active = Boolean.parseBoolean(childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ACTIVE)));
|
||||
feed.address = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ADDRESS));
|
||||
feed.favicon = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON));
|
||||
feed.faviconColour = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_COLOUR));
|
||||
feed.faviconFade = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_FADE));
|
||||
feed.feedId = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ID));
|
||||
feed.feedLink = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_LINK));
|
||||
feed.lastUpdated = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_UPDATED_SECONDS));
|
||||
feed.negativeCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_NEGATIVE_COUNT));
|
||||
feed.neutralCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_NEUTRAL_COUNT));
|
||||
feed.positiveCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_POSITIVE_COUNT));
|
||||
feed.subscribers = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_SUBSCRIBERS));
|
||||
feed.title = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_TITLE));
|
||||
return feed;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.domain;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
@ -9,6 +10,8 @@ import com.newsblur.database.DatabaseConstants;
|
|||
public class Story {
|
||||
|
||||
public String id;
|
||||
|
||||
@SerializedName("story_permalink")
|
||||
public String permalink;
|
||||
|
||||
@SerializedName("share_count")
|
||||
|
@ -18,7 +21,7 @@ public class Story {
|
|||
public Integer commentCount;
|
||||
|
||||
@SerializedName("read_status")
|
||||
public Boolean read;
|
||||
public int read;
|
||||
|
||||
@SerializedName("story_tags")
|
||||
public String[] tags;
|
||||
|
@ -43,7 +46,7 @@ public class Story {
|
|||
|
||||
@SerializedName("intelligence_feed")
|
||||
public String intelligenceFeed;
|
||||
|
||||
|
||||
@SerializedName("intelligence_authors")
|
||||
public String intelligenceAuthors;
|
||||
|
||||
|
@ -66,5 +69,21 @@ public class Story {
|
|||
values.put(DatabaseConstants.STORY_FEED_ID, feedId);
|
||||
return values;
|
||||
}
|
||||
|
||||
public static Story fromCursor(final Cursor cursor) {
|
||||
Story story = new Story();
|
||||
story.authors = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_AUTHORS));
|
||||
story.content = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_CONTENT));
|
||||
story.title = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_TITLE));
|
||||
story.date = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_DATE));
|
||||
story.permalink = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_PERMALINK));
|
||||
story.intelligenceAuthors = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_AUTHORS));
|
||||
story.tags = TextUtils.split(cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_TAGS)), ",");
|
||||
story.intelligenceFeed = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_INTELLIGENCE_FEED));
|
||||
story.read = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.STORY_READ));
|
||||
story.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_FEED_ID));
|
||||
story.id = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_ID));
|
||||
return story;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
@ -9,17 +10,20 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ExpandableListView.OnChildClickListener;
|
||||
import android.widget.ExpandableListView.OnGroupClickListener;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.database.FolderTreeAdapter;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.util.AppConstants;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.view.FolderTreeViewBinder;
|
||||
|
||||
public class FolderFeedListFragment extends Fragment implements OnGroupClickListener {
|
||||
public class FolderFeedListFragment extends Fragment implements OnGroupClickListener, OnChildClickListener {
|
||||
|
||||
private ExpandableListView list;
|
||||
private ContentResolver resolver;
|
||||
|
@ -58,6 +62,7 @@ public class FolderFeedListFragment extends Fragment implements OnGroupClickList
|
|||
list.setChildDivider(getActivity().getResources().getDrawable(R.drawable.divider_light));
|
||||
list.setAdapter(folderAdapter);
|
||||
list.setOnGroupClickListener(this);
|
||||
list.setOnChildClickListener(this);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -94,4 +99,14 @@ public class FolderFeedListFragment extends Fragment implements OnGroupClickList
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChildClick(ExpandableListView list, View childView, int groupPosition, int childPosition, long id) {
|
||||
final Intent intent = new Intent(getActivity(), Reading.class);
|
||||
Cursor childCursor = folderAdapter.getChild(groupPosition, childPosition);
|
||||
String feedId = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ID));
|
||||
intent.putExtra(Reading.EXTRA_FEED, feedId);
|
||||
getActivity().startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ public class LoginFragment extends Fragment implements OnClickListener, Receiver
|
|||
Log.d(TAG, "Synchronisation finished.");
|
||||
final Intent intent = new Intent(Intent.ACTION_SYNC, null, getActivity(), SyncService.class);
|
||||
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, receiver);
|
||||
intent.putExtra(SyncService.SYNCSERVICE_TASK, SyncService.EXTRA_TASK_FOLDER_UPDATE);
|
||||
getActivity().startService(intent);
|
||||
} else {
|
||||
if (viewSwitcher != null) {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Story;
|
||||
|
||||
public class ReadingItemFragment extends Fragment {
|
||||
|
||||
final Story story;
|
||||
|
||||
public ReadingItemFragment(final Story story) {
|
||||
this.story = story;
|
||||
}
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_readingitem, null);
|
||||
WebView web = (WebView) view.findViewById(R.id.reading_webview);
|
||||
web.getSettings().setLoadWithOverviewMode(true);
|
||||
web.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
|
||||
|
||||
web.getSettings().setJavaScriptEnabled(true);
|
||||
web.getSettings().setDomStorageEnabled(true);
|
||||
web.getSettings().setSupportZoom(true);
|
||||
web.getSettings().setAppCacheMaxSize(1024*1024*8);
|
||||
web.getSettings().setAppCachePath("/data/data/com.newsblur/cache");
|
||||
web.getSettings().setAllowFileAccess(true);
|
||||
web.getSettings().setAppCacheEnabled(true);
|
||||
web.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
// TODO: Define a better strategy for rescaling the HTML across device screen sizes and storying this HTML as boilderplate somewhere
|
||||
builder.append("<html><head><meta name=\"viewport\" content=\"target-densitydpi=device-dpi\" /><link rel=\"stylesheet\" type=\"text/css\" href=\"reading.css\" /></head><body>");
|
||||
builder.append(story.content);
|
||||
builder.append("</body></html>");
|
||||
web.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import org.apache.http.HttpStatus;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
@ -16,6 +17,7 @@ import com.newsblur.database.DatabaseConstants;
|
|||
import com.newsblur.database.FeedProvider;
|
||||
import com.newsblur.domain.Feed;
|
||||
import com.newsblur.domain.FolderStructure;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.network.domain.FeedFolderResponse;
|
||||
import com.newsblur.network.domain.LoginResponse;
|
||||
import com.newsblur.network.domain.ProfileResponse;
|
||||
|
@ -80,14 +82,22 @@ public class APIManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void getStoriesForFeed(String feedId) {
|
||||
public StoriesResponse getStoriesForFeed(String feedId) {
|
||||
final APIClient client = new APIClient(context);
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_FEEDS, feedId);
|
||||
client.get(APIConstants.URL_FEEDS_STORIES, values);
|
||||
final APIResponse response = client.get(APIConstants.URL_FEEDS_STORIES, values);
|
||||
StoriesResponse storiesResponse = gson.fromJson(response.responseString, StoriesResponse.class);
|
||||
Log.d(TAG, "Response: " + response.responseString);
|
||||
if (response.responseCode == HttpStatus.SC_OK && !response.hasRedirected) {
|
||||
Uri feedUri = FeedProvider.STORIES_URI.buildUpon().appendPath(feedId).build();
|
||||
for (Story story : storiesResponse.stories) {
|
||||
contentResolver.insert(feedUri, story.getValues());
|
||||
}
|
||||
return storiesResponse;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void getFolderFeedMapping() {
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
package com.newsblur.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.newsblur.network.APIClient;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.domain.StoriesResponse;
|
||||
|
||||
/**
|
||||
* The SyncService is based on an app architecture that tries to place network calls
|
||||
|
@ -24,12 +23,19 @@ public class SyncService extends IntentService {
|
|||
|
||||
private static final String TAG = "SyncService";
|
||||
public static final String EXTRA_STATUS_RECEIVER = "resultReceiverExtra";
|
||||
public static final String EXTRA_TASK_FEED_ID = "taskFeedId";
|
||||
|
||||
public final static int STATUS_RUNNING = 0;
|
||||
public final static int STATUS_FINISHED = 1;
|
||||
public final static int STATUS_ERROR = 2;
|
||||
public static final int NOT_RUNNING = -1;
|
||||
|
||||
public static final int EXTRA_TASK_FOLDER_UPDATE = 30;
|
||||
public static final int EXTRA_TASK_FEED_UPDATE = 31;
|
||||
|
||||
public APIClient apiClient;
|
||||
private APIManager apiManager;
|
||||
public static final String SYNCSERVICE_TASK = "syncservice_task";
|
||||
|
||||
public SyncService() {
|
||||
super(TAG);
|
||||
|
@ -45,12 +51,27 @@ public class SyncService extends IntentService {
|
|||
protected void onHandleIntent(Intent intent) {
|
||||
Log.d(TAG, "Received SyncService handleIntent call.");
|
||||
final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_STATUS_RECEIVER);
|
||||
try {
|
||||
try {
|
||||
if (receiver != null) {
|
||||
receiver.send(STATUS_RUNNING, Bundle.EMPTY);
|
||||
}
|
||||
apiManager.getFolderFeedMapping();
|
||||
|
||||
switch (intent.getIntExtra(SYNCSERVICE_TASK , -1)) {
|
||||
case EXTRA_TASK_FOLDER_UPDATE:
|
||||
apiManager.getFolderFeedMapping();
|
||||
break;
|
||||
case EXTRA_TASK_FEED_UPDATE:
|
||||
if (!TextUtils.isEmpty(intent.getStringExtra(EXTRA_TASK_FEED_ID))) {
|
||||
apiManager.getStoriesForFeed(intent.getStringExtra(EXTRA_TASK_FEED_ID));
|
||||
} else {
|
||||
Log.e(TAG, "No feed to refresh included in SyncRequest");
|
||||
receiver.send(STATUS_ERROR, Bundle.EMPTY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "SyncService called without relevant task assignment");
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "Couldn't synchronise with Newsblur servers: " + e.getMessage(), e.getCause());
|
||||
|
@ -63,6 +84,8 @@ public class SyncService extends IntentService {
|
|||
|
||||
if (receiver != null) {
|
||||
receiver.send(STATUS_FINISHED, Bundle.EMPTY);
|
||||
} else {
|
||||
Log.e(TAG, "No receiver attached to Sync?");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,18 +63,19 @@ public class ActivitiesAdapter extends ArrayAdapter<ActivitiesResponse> {
|
|||
final ActivitiesResponse activity = getItem(position);
|
||||
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
|
||||
|
||||
ClickableSpan usernameClick = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, activity.id);
|
||||
context.startActivity(i);
|
||||
}
|
||||
};
|
||||
|
||||
if (TextUtils.equals(activity.category, "follow")) {
|
||||
stringBuilder.append(startedFollowing);
|
||||
stringBuilder.append(" ");
|
||||
stringBuilder.append(activity.user.username);
|
||||
ClickableSpan usernameClick = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, activity.id);
|
||||
context.startActivity(i);
|
||||
}
|
||||
};
|
||||
stringBuilder.setSpan(darkgray, 0, startedFollowing.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
stringBuilder.setSpan(usernameClick, startedFollowing.length() + 1, startedFollowing.length() + 1 + activity.user.username.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
stringBuilder.setSpan(highlight, startedFollowing.length() + 1, startedFollowing.length() + 1 + activity.user.username.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
@ -86,6 +87,7 @@ public class ActivitiesAdapter extends ArrayAdapter<ActivitiesResponse> {
|
|||
stringBuilder.append(activity.content);
|
||||
stringBuilder.append("\"");
|
||||
stringBuilder.setSpan(darkgray, 0, repliedTo.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
stringBuilder.setSpan(usernameClick, repliedTo.length() + 1, repliedTo.length() + 1 + activity.user.username.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
stringBuilder.setSpan(highlight, repliedTo.length() + 1, repliedTo.length() + 1 + activity.user.username.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
stringBuilder.setSpan(midgray, stringBuilder.length() - activity.content.length() - 2, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
} else if (TextUtils.equals(activity.category, "sharedstory")) {
|
||||
|
|
|
@ -18,7 +18,6 @@ public class FolderTreeViewBinder implements ViewBinder {
|
|||
|
||||
private int currentState = AppConstants.STATE_ALL;
|
||||
|
||||
|
||||
// TODO: Make this more efficient
|
||||
@Override
|
||||
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
|
||||
|
|
Loading…
Add table
Reference in a new issue