Merge pull request #720 from dosiecki/master

Android: Bugfixes and Feed List UI Update
This commit is contained in:
Samuel Clay 2015-08-03 14:35:48 -07:00
commit e08db415ca
16 changed files with 373 additions and 161 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,50 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
android:id="@+id/main_top_bar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentTop="true"
style="?actionbarBackground" >
<fragment
android:id="@+id/fragment_feedintelligenceselector"
android:name="com.newsblur.fragment.FeedIntelligenceSelectorFragment"
android:layout_width="fill_parent"
<ImageView
android:id="@+id/main_user_image"
android:src="@drawable/logo"
android:layout_height="41dp"
android:layout_width="41dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="4dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<TextView
android:id="@+id/main_user_name"
android:layout_height="22dp"
android:layout_width="match_parent"
android:layout_marginTop="4dp"
android:layout_marginLeft="6dp"
android:layout_marginBottom="1dp"
android:layout_toRightOf="@id/main_user_image"
android:layout_alignParentTop="true"
android:textStyle="bold"
android:textSize="16dp" />
<ImageView
android:id="@+id/main_unread_count_neut_icon"
android:layout_width="11dp"
android:layout_height="11dp"
android:layout_below="@+id/main_user_name"
android:layout_toRightOf="@id/main_user_image"
android:layout_marginLeft="6dp"
android:layout_marginTop="5dp"
android:src="@drawable/g_icn_unread" />
<TextView
android:id="@+id/main_unread_count_neut_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/main_user_name"
android:layout_toRightOf="@id/main_unread_count_neut_icon"
android:layout_marginLeft="3dp"
android:textSize="14sp" />
<ImageView
android:id="@+id/main_unread_count_posi_icon"
android:layout_width="11dp"
android:layout_height="11dp"
android:layout_below="@+id/main_user_name"
android:layout_toRightOf="@id/main_unread_count_neut_text"
android:layout_marginLeft="8dp"
android:layout_marginTop="5dp"
android:src="@drawable/g_icn_focus" />
<TextView
android:id="@+id/main_unread_count_posi_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/main_user_name"
android:layout_toRightOf="@id/main_unread_count_posi_icon"
android:layout_marginLeft="3dp"
android:textSize="14sp" />
<Button
android:id="@+id/main_profile_button"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_margin="8dp"
android:background="@drawable/ic_menu_profile"
android:contentDescription="@string/menu_profile"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
<!-- The all/unread/focus toggle. Despite being at the bottom, this needs
to be defined first so that other things can be placed above it. -->
<fragment
android:id="@+id/fragment_feedintelligenceselector"
android:name="com.newsblur.fragment.FeedIntelligenceSelectorFragment"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_alignParentBottom="true"
android:tag="feedIntelligenceSelector" />
<Button
android:id="@+id/main_menu_button"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_margin="5dp"
android:background="@drawable/ic_menu_moreoverflow"
android:contentDescription="@string/description_menu"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" />
<Button
android:id="@+id/main_add_button"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_margin="4dp"
android:background="@drawable/ic_menu_add"
android:contentDescription="@string/menu_add_feed"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true" />
<!-- A pane that sits behind the feed list, acting as a background, except
when the list is empty, and it shows through as an explainer. -->
<RelativeLayout
android:id="@+id/folderfeedlist_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/main_top_bar"
android:layout_above="@id/fragment_feedintelligenceselector"
style="?listBackground">
<TextView
android:id="@+id/empty_view_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:tag="feedIntelligenceSelector" />
android:layout_marginBottom="15dp"
android:gravity="center_horizontal"
style="?explainerText"
android:visibility="invisible"
android:textSize="16dp" />
<ImageView
android:id="@+id/empty_view_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="@string/description_empty_list_image"
android:src="@drawable/world_big"
android:layout_above="@id/empty_view_text"
android:layout_marginBottom="15dp"
android:layout_centerHorizontal="true"
android:scaleType="fitCenter"
android:visibility="invisible" />
</RelativeLayout>
<!-- The scrollable and pull-able feed list. -->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/main_top_bar"
android:layout_above="@id/fragment_feedintelligenceselector" >
<fragment
android:id="@+id/fragment_folderfeedlist"
android:name="com.newsblur.fragment.FolderListFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/fragment_feedintelligenceselector"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="folderFeedListFragment" />
<View
android:id="@+id/feedintelligenceselector_border"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/midgray"
android:layout_below="@id/fragment_folderfeedlist" />
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/main_sync_status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/fragment_feedintelligenceselector"
android:padding="2dp"
android:textSize="14sp"
android:gravity="center"
android:textColor="@color/status_overlay_text"
android:background="@color/status_overlay_background"
android:text="SYNC STATUS" />
<View
android:id="@+id/top_bar_border"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/midgray"
android:layout_above="@id/swipe_container" />
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>
<View
android:id="@+id/feedintelligenceselector_border"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/midgray"
android:layout_below="@id/swipe_container" />
<TextView
android:id="@+id/main_sync_status"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/fragment_feedintelligenceselector"
android:padding="2dp"
android:textSize="14sp"
android:gravity="center"
android:textColor="@color/status_overlay_text"
android:background="@color/status_overlay_background"
android:text="SYNC STATUS" />
</RelativeLayout>

View file

@ -4,4 +4,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
/>
android:background="#00000000"
android:cacheColorHint="#00000000"
/>

View file

@ -28,53 +28,6 @@
android:textSize="14dp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/row_foldersums"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="6dp" >
<TextView
android:id="@+id/row_foldersumneu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_alignParentRight="true"
android:background="@drawable/neutral_count_rect"
android:gravity="center"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:paddingTop="1dp"
android:paddingBottom="2dp"
android:shadowColor="@color/neutral_drop_shadow"
android:shadowDy="1"
android:shadowRadius="1"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/row_foldersumpos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_alignParentRight="true"
android:background="@drawable/positive_count_rect"
android:gravity="center"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:paddingTop="1dp"
android:paddingBottom="2dp"
android:shadowColor="@color/positive_drop_shadow"
android:shadowDy="1"
android:shadowRadius="1"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"

View file

@ -28,6 +28,7 @@
<attr name="itemHeaderDivider" format="string" />
<attr name="storyCommentDivider" format="string" />
<attr name="activityDetailsPager" format="string" />
<attr name="explainerText" format="string" />
<declare-styleable name="FlowLayout">
<attr name="flow" />

View file

@ -34,6 +34,7 @@
<string name="description_follow_button">Follow or unfollow a user</string>
<string name="description_comment_user">Comment user image</string>
<string name="description_empty_list_image">Empty list placeholder</string>
<string name="description_menu">Menu</string>
<string name="reading_shared_count">%s shares</string>
<string name="reading_comment_count">%s comments</string>
@ -148,6 +149,8 @@
<string name="empty_list_view_loading">Loading…</string>
<string name="empty_list_view_no_stories">No stories to read</string>
<string name="empty_list_view_no_unread_stories">You have no unread stories.</string>
<string name="empty_list_view_no_focus_stories">You have no unread stories in Focus mode.\n\nSwitch to All or Unread.</string>
<string name="login_registration_register">Register</string>

View file

@ -259,4 +259,13 @@
<style name="storyCommentDivider.dark">
<item name="android:background">@color/dark_story_comment_divider</item>
</style>
<style name="explainerText">
<item name="android:textColor">@color/midgray</item>
</style>
<style name="explainerText.dark">
<item name="android:textColor">@color/lightgray</item>
</style>
</resources>

View file

@ -29,6 +29,7 @@
<item name="itemHeaderDivider">@style/itemHeaderDivider</item>
<item name="storyCommentDivider">@style/storyCommentDivider</item>
<item name="activityDetailsPager">@style/activityDetailsPager</item>
<item name="explainerText">@style/explainerText</item>
</style>
<style name="NewsBlurDarkTheme" parent="@android:style/Theme.Holo" >
@ -60,5 +61,6 @@
<item name="itemHeaderDivider">@style/itemHeaderDivider.dark</item>
<item name="storyCommentDivider">@style/storyCommentDivider.dark</item>
<item name="activityDetailsPager">@style/activityDetailsPager.dark</item>
<item name="explainerText">@style/explainerText.dark</item>
</style>
</resources>

View file

@ -1,7 +1,7 @@
package com.newsblur.activity;
import android.app.ActionBar;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.app.DialogFragment;
@ -15,10 +15,14 @@ import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.FindView;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.fragment.FeedIntelligenceSelectorFragment;
@ -34,7 +38,7 @@ import com.newsblur.util.StateFilter;
import com.newsblur.util.UIUtils;
import com.newsblur.view.StateToggleButton.StateChangedListener;
public class Main extends NbActivity implements StateChangedListener, SwipeRefreshLayout.OnRefreshListener, AbsListView.OnScrollListener {
public class Main extends NbActivity implements StateChangedListener, SwipeRefreshLayout.OnRefreshListener, AbsListView.OnScrollListener, PopupMenu.OnMenuItemClickListener {
private FolderListFragment folderFeedList;
private FragmentManager fragmentManager;
@ -42,6 +46,13 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
private SwipeRefreshLayout swipeLayout;
private boolean wasSwipeEnabled = false;
@FindView(R.id.main_sync_status) TextView overlayStatusText;
@FindView(R.id.empty_view_image) ImageView emptyViewImage;
@FindView(R.id.empty_view_text) TextView emptyViewText;
@FindView(R.id.main_menu_button) Button menuButton;
@FindView(R.id.main_user_image) ImageView userImage;
@FindView(R.id.main_user_name) TextView userName;
@FindView(R.id.main_unread_count_neut_text) TextView unreadCountNeutText;
@FindView(R.id.main_unread_count_posi_text) TextView unreadCountPosiText;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -57,7 +68,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
getActionBar().hide();
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);
@ -70,6 +81,13 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
// make sure the interval sync is scheduled, since we are the root Activity
BootReceiver.scheduleSyncService(this);
Bitmap userPicture = PrefsUtils.getUserImage(this);
if (userPicture != null) {
userPicture = UIUtils.roundCorners(userPicture, 5);
userImage.setImageBitmap(userPicture);
}
userName.setText(PrefsUtils.getUserDetails(this).username);
}
@Override
@ -83,6 +101,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
FeedUtils.clearReadingSession();
updateStatusIndicators();
folderFeedList.pushUnreadCounts();
triggerSync();
if (PrefsUtils.isLightThemeSelected(this) != isLightTheme) {
@ -90,70 +109,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem loginAsItem = menu.findItem(R.id.menu_loginas);
if (NBSyncService.isStaff == Boolean.TRUE) {
loginAsItem.setVisible(true);
} else {
loginAsItem.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
MenuItem feedbackItem = menu.findItem(R.id.menu_feedback);
if (AppConstants.ENABLE_FEEDBACK) {
feedbackItem.setTitle(feedbackItem.getTitle() + " (v" + PrefsUtils.getVersion(this) + ")");
} else {
feedbackItem.setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_profile) {
Intent profileIntent = new Intent(this, Profile.class);
startActivity(profileIntent);
return true;
} else if (item.getItemId() == R.id.menu_refresh) {
NBSyncService.forceFeedsFolders();
triggerSync();
return true;
} else if (item.getItemId() == R.id.menu_add_feed) {
Intent intent = new Intent(this, SearchForFeeds.class);
startActivityForResult(intent, 0);
return true;
} else if (item.getItemId() == R.id.menu_logout) {
DialogFragment newFragment = new LogoutDialogFragment();
newFragment.show(getFragmentManager(), "dialog");
} else if (item.getItemId() == R.id.menu_settings) {
Intent settingsIntent = new Intent(this, Settings.class);
startActivity(settingsIntent);
return true;
} else if (item.getItemId() == R.id.menu_feedback) {
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this)));
startActivity(i);
} catch (Exception e) {
Log.wtf(this.getClass().getName(), "device cannot even open URLs to report feedback");
}
return true;
} else if (item.getItemId() == R.id.menu_loginas) {
DialogFragment newFragment = new LoginAsDialogFragment();
newFragment.show(getFragmentManager(), "dialog");
}
return super.onOptionsItemSelected(item);
}
@Override
public void changedState(StateFilter state) {
folderFeedList.changeState(state);
@ -170,6 +125,30 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
folderFeedList.startLoaders();
}
public void updateUnreadCounts(int neutCount, int posiCount) {
unreadCountNeutText.setText(Integer.toString(neutCount));
unreadCountPosiText.setText(Integer.toString(posiCount));
if ((neutCount+posiCount) <= 0) {
if (NBSyncService.isFeedCountSyncRunning()) {
emptyViewImage.setVisibility(View.INVISIBLE);
emptyViewText.setText(R.string.loading);
emptyViewText.setVisibility(View.VISIBLE);
} else {
emptyViewImage.setVisibility(View.VISIBLE);
if (folderFeedList.currentState == StateFilter.BEST) {
emptyViewText.setText(R.string.empty_list_view_no_focus_stories);
} else {
emptyViewText.setText(R.string.empty_list_view_no_unread_stories);
}
emptyViewText.setVisibility(View.VISIBLE);
}
} else {
emptyViewImage.setVisibility(View.INVISIBLE);
emptyViewText.setVisibility(View.INVISIBLE);
}
}
private void updateStatusIndicators() {
if (NBSyncService.isFeedFolderSyncRunning()) {
swipeLayout.setRefreshing(true);
@ -194,6 +173,77 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
triggerSync();
}
@OnClick(R.id.main_menu_button) void onClickMenuButton() {
PopupMenu pm = new PopupMenu(this, menuButton);
Menu menu = pm.getMenu();
pm.getMenuInflater().inflate(R.menu.main, menu);
MenuItem loginAsItem = menu.findItem(R.id.menu_loginas);
if (NBSyncService.isStaff == Boolean.TRUE) {
loginAsItem.setVisible(true);
} else {
loginAsItem.setVisible(false);
}
MenuItem feedbackItem = menu.findItem(R.id.menu_feedback);
if (AppConstants.ENABLE_FEEDBACK) {
feedbackItem.setTitle(feedbackItem.getTitle() + " (v" + PrefsUtils.getVersion(this) + ")");
} else {
feedbackItem.setVisible(false);
}
pm.setOnMenuItemClickListener(this);
pm.show();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.menu_profile) {
Intent i = new Intent(this, Profile.class);
startActivity(i);
return true;
} else if (item.getItemId() == R.id.menu_refresh) {
NBSyncService.forceFeedsFolders();
triggerSync();
return true;
} else if (item.getItemId() == R.id.menu_add_feed) {
Intent i = new Intent(this, SearchForFeeds.class);
startActivity(i);
return true;
} else if (item.getItemId() == R.id.menu_logout) {
DialogFragment newFragment = new LogoutDialogFragment();
newFragment.show(getFragmentManager(), "dialog");
} else if (item.getItemId() == R.id.menu_settings) {
Intent settingsIntent = new Intent(this, Settings.class);
startActivity(settingsIntent);
return true;
} else if (item.getItemId() == R.id.menu_feedback) {
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(PrefsUtils.createFeedbackLink(this)));
startActivity(i);
} catch (Exception e) {
Log.wtf(this.getClass().getName(), "device cannot even open URLs to report feedback");
}
return true;
} else if (item.getItemId() == R.id.menu_loginas) {
DialogFragment newFragment = new LoginAsDialogFragment();
newFragment.show(getFragmentManager(), "dialog");
return true;
}
return false;
}
@OnClick(R.id.main_add_button) void onClickAddButton() {
Intent i = new Intent(this, SearchForFeeds.class);
startActivity(i);
}
@OnClick(R.id.main_profile_button) void onClickProfileButton() {
Intent i = new Intent(this, Profile.class);
startActivity(i);
}
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
// not required

View file

@ -678,6 +678,16 @@ public class BlurDatabaseHelper {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.STORY_STARRED, starred);
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
// since local star/unstar operations are, so far, never retried or done automatically,
// we don't do the complex transactional accounting done with counts as with read/unread
// operations. Though this could get off by 1 in very loaded circumstances, the count will
// get refreshed on the next feed/folder sync. Unfortunately, we also can't do local
// counting like with unreads, since we never get a full set of starred stories.
String operator = (starred ? " + 1" : " - 1");
StringBuilder q = new StringBuilder("UPDATE " + DatabaseConstants.STARRED_STORY_COUNT_TABLE);
q.append(" SET ").append(DatabaseConstants.STARRED_STORY_COUNT_COUNT).append(" = ").append(DatabaseConstants.STARRED_STORY_COUNT_COUNT).append(operator);
synchronized (RW_MUTEX) {dbRW.execSQL(q.toString());}
}
public void setStoryShared(String hash) {

View file

@ -59,9 +59,9 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
/** Positive counts for active feeds, indexed by feed ID. */
private Map<String,Integer> feedPosCounts;
/** Total neutral unreads for all feeds. */
private int totalNeutCount = 0;
public int totalNeutCount = 0;
/** Total positive unreads for all feeds. */
private int totalPosCount = 0;
public int totalPosCount = 0;
/** Folders, indexed by canonical name. */
private Map<String,Folder> folders = Collections.emptyMap();
@ -140,7 +140,6 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
((ImageView) v.findViewById(R.id.row_folder_indicator)).setImageResource(isExpanded ? R.drawable.indicator_expanded : R.drawable.indicator_collapsed);
} else if (isFolderRoot(groupPosition)) {
v = inflater.inflate(R.layout.row_all_stories, null, false);
bindCountViews(v, totalNeutCount, totalPosCount, true);
} else if (isRowReadStories(groupPosition)) {
if (convertView == null) {
v = inflater.inflate(R.layout.row_read_stories, null, false);

View file

@ -29,6 +29,7 @@ import butterknife.OnGroupExpand;
import com.newsblur.R;
import com.newsblur.activity.AllStoriesItemsList;
import com.newsblur.activity.FeedItemsList;
import com.newsblur.activity.Main;
import com.newsblur.activity.ItemsList;
import com.newsblur.activity.ReadStoriesItemsList;
import com.newsblur.activity.SavedStoriesItemsList;
@ -56,6 +57,7 @@ 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;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -70,6 +72,7 @@ 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
@ -109,6 +112,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
throw new IllegalArgumentException("unknown loader created");
}
checkOpenFolderPreferences();
pushUnreadCounts();
} catch (Exception e) {
// for complex folder sets, these ops can take so long that they butt heads
// with the destruction of the fragment and adapter. crashes can ensue.
@ -263,6 +267,15 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
hasUpdated();
}
/**
* Every time unread counts are updated in the adapter, ping the Main activity with
* the new data. It is, unfortunately, quite expensive to compute given the current
* DB model, so having Main also load it would cause some lag.
*/
public void pushUnreadCounts() {
activity.updateUnreadCounts(adapter.totalNeutCount, adapter.totalPosCount);
}
@OnGroupClick(R.id.folderfeed_list) boolean onGroupClick(ExpandableListView list, View group, int groupPosition, long id) {
if (adapter.isFolderRoot(groupPosition)) {
Intent i = new Intent(getActivity(), AllStoriesItemsList.class);

View file

@ -542,21 +542,39 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
}
}
private static final Pattern altSniff1 = Pattern.compile("<img[^>]*src=(['\"])((?:(?!\\1).)*)\\1[^>]*alt=(['\"])((?:(?!\\3).)*)\\3[^>]*>", Pattern.CASE_INSENSITIVE);
private static final Pattern altSniff2 = Pattern.compile("<img[^>]*alt=(['\"])((?:(?!\\1).)*)\\1[^>]*src=(['\"])((?:(?!\\3).)*)\\3[^>]*>", Pattern.CASE_INSENSITIVE);
private static final Pattern altSniff3 = Pattern.compile("<img[^>]*src=(['\"])((?:(?!\\1).)*)\\1[^>]*title=(['\"])((?:(?!\\3).)*)\\3[^>]*>", Pattern.CASE_INSENSITIVE);
private static final Pattern altSniff4 = Pattern.compile("<img[^>]*title=(['\"])((?:(?!\\1).)*)\\1[^>]*src=(['\"])((?:(?!\\3).)*)\\3[^>]*>", Pattern.CASE_INSENSITIVE);
private void sniffAltTexts(String html) {
// Find images with alt tags and cache the text for use on long-press
// NOTE: if doing this via regex has a smell, you have a good nose! This method is far from perfect
// and may miss valid cases or trucate tags, but it works for popular feeds (read: XKCD) and doesn't
// require us to import a proper parser lib of hundreds of kilobytes just for this one feature.
imageAltTexts = new HashMap<String,String>();
// sniff for alts first
Matcher imgTagMatcher = altSniff1.matcher(html);
while (imgTagMatcher.find()) {
imageAltTexts.put(imgTagMatcher.group(2), imgTagMatcher.group(4));
}
imgTagMatcher = altSniff2.matcher(html);
while (imgTagMatcher.find()) {
imageAltTexts.put(imgTagMatcher.group(4), imgTagMatcher.group(2));
}
// then sniff for 'title' tags, so they will overwrite alts and take precedence
imgTagMatcher = altSniff3.matcher(html);
while (imgTagMatcher.find()) {
imageAltTexts.put(imgTagMatcher.group(2), imgTagMatcher.group(4));
}
imgTagMatcher = altSniff4.matcher(html);
while (imgTagMatcher.find()) {
imageAltTexts.put(imgTagMatcher.group(4), imgTagMatcher.group(2));
}
// while were are at it, create a place where we can later cache offline image remaps so that when
// we do an alt-text lookup, we can search for the right URL key.
imageUrlRemaps = new HashMap<String,String>();
Matcher imgTagMatcher1 = Pattern.compile("<img[^>]*src=\"([^\"]*)\"[^>]*alt=\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(html);
while (imgTagMatcher1.find()) {
imageAltTexts.put(imgTagMatcher1.group(1), imgTagMatcher1.group(2));
}
Matcher imgTagMatcher2 = Pattern.compile("<img[^>]*alt=\"([^\"]*)\"[^>]*src=\"([^\"]*)\"[^>]*>", Pattern.CASE_INSENSITIVE).matcher(html);
while (imgTagMatcher2.find()) {
imageAltTexts.put(imgTagMatcher2.group(2), imgTagMatcher2.group(1));
}
}
private String swapInOfflineImages(String html) {

View file

@ -718,6 +718,10 @@ public class NBSyncService extends Service {
return (HousekeepingRunning || ActionsRunning || RecountsRunning || FFSyncRunning || CleanupService.running() || UnreadsService.running() || StorySyncRunning || OriginalTextService.running() || ImagePrefetchService.running());
}
public static boolean isFeedCountSyncRunning() {
return (HousekeepingRunning || RecountsRunning || FFSyncRunning);
}
/**
* Is there a sync for a given FeedSet running?
*/