Merge branch 'sictiru'

* sictiru:
  #1282 Rename folder option
  #1282 Delete folder option
  #1282 Add a folder when adding a feed
  Android Gradle 4.0.0
  #1339 ButterKnife to view binding part 2
  #1339 ButterKnife to view binding part 1
  #1337 Remove unsupported plugin
  #1274 (http images not loading)
  #1299 Android saved searches add option
  #1299 Android saved searches delete option
  Android folder saved searches
  #1299 Android Saved searches
  Update support libraries to api 28. Update dependencies
This commit is contained in:
Samuel Clay 2020-07-16 19:22:28 -04:00
commit 5002403902
63 changed files with 1914 additions and 811 deletions

View file

@ -1,12 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.newsblur"
android:versionCode="167"
android:versionName="10.0" >
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="28" />
package="com.newsblur">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View file

@ -8,7 +8,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
@ -22,27 +22,29 @@ repositories {
apply plugin: 'com.android.application'
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
dependencies {
compile 'com.android.support:support-core-utils:27.1.1'
compile 'com.android.support:support-fragment:27.1.1'
compile 'com.android.support:support-core-ui:27.1.1'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.android.support:support-core-utils:28.0.0'
implementation 'com.android.support:support-fragment:28.0.0'
implementation 'com.android.support:support-core-ui:28.0.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.12'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
compileSdkVersion 28
defaultConfig {
applicationId "com.newsblur"
minSdkVersion 21
targetSdkVersion 28
versionCode 166
versionName "10.0b4"
}
compileOptions.with {
sourceCompatibility = JavaVersion.VERSION_1_7
}
// force old processing behaviour that butterknife 7 depends on, until we can afford to upgrade
android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
viewBinding.enabled = true
sourceSets {
main {

View file

@ -11,16 +11,6 @@
-dontwarn okhttp3.**
-dontnote okhttp3.**
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
# these two seem to confuse ProGuard, so force keep them
-keep class com.newsblur.util.StateFilter { *; }
-keep class com.newsblur.view.StateToggleButton$StateChangedListener { *; }

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#7B7B7B"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM19,14h-3v3h-2v-3h-3v-2h3L14,9h2v3h3v2z" />
</vector>

View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<TextView
android:id="@+id/text_sync_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/status_overlay_background"
android:gravity="center"
android:padding="2dp"
android:text="@string/sync_status_feed_add"
android:textColor="@color/status_overlay_text"
android:textSize="14sp"
android:visibility="gone" />
<TextView
android:id="@+id/text_add_folder_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="14dp"
android:paddingEnd="16dp"
android:paddingBottom="14dp"
android:text="@string/add_new_folder"
android:textAllCaps="true" />
<LinearLayout
android:id="@+id/container_add_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:visibility="gone">
<EditText
android:id="@+id/input_folder_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:autofillHints="@null"
android:textSize="14sp"
android:hint="@string/new_folder_name_hint"
android:inputType="textCapCharacters"
android:maxLines="1" />
<ImageView
android:id="@+id/ic_create_folder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginEnd="12dp"
android:contentDescription="@string/description_add_new_folder_icon"
android:padding="4dp"
android:src="@drawable/ic_create_folder" />
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="bottom"
android:layout_marginStart="8dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="4dp"
android:indeterminate="true"
android:visibility="gone" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view_folders"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
</LinearLayout>

View file

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
android:paddingRight="10dp">
<EditText
android:id="@+id/feed_name_field"
android:id="@+id/input_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="5dp"
android:singleLine="true"
/>
android:layout_marginBottom="10dp"
android:autofillHints="@null"
android:hint="@string/new_folder_name_hint"
android:inputType="textCapSentences"
android:singleLine="true" />
</RelativeLayout>
</FrameLayout>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text_folder_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="42dp"
android:paddingStart="16dp"
android:paddingEnd="16dp" />

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="?selectorFeedBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/row_saved_search_icon"
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:contentDescription="@string/description_row_folder_icon"
android:src="@drawable/ic_menu_search_gray55" />
<TextView
android:id="@+id/row_saved_search_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/row_saved_search_icon"
android:ellipsize="end"
android:lines="1"
android:paddingTop="9dp"
android:paddingEnd="9dp"
android:paddingBottom="9dp" />
<View
style="?rowBorderTop"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentTop="true" />
<View
style="?rowBorderBottom"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true" />
</RelativeLayout>

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="?selectorFolderBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/row_folder_icon"
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:contentDescription="@string/description_row_folder_icon"
android:src="@drawable/ic_menu_search_gray55" />
<TextView
android:id="@+id/row_foldername"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/row_folder_icon"
android:paddingTop="9dp"
android:paddingBottom="9dp"
android:text="@string/saved_searches_row_title"
android:textStyle="bold" />
<View
style="?rowBorderTop"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentTop="true" />
<View
style="?rowBorderBottom"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true" />
</RelativeLayout>

View file

@ -44,4 +44,7 @@
<item android:id="@+id/menu_intel"
android:title="@string/menu_intel" />
<item android:id="@+id/menu_delete_saved_search"
android:title="@string/menu_delete_saved_search" />
</menu>

View file

@ -9,5 +9,11 @@
<item android:id="@+id/menu_unmute_folder"
android:title="@string/menu_unmute_folder" />
<item android:id="@+id/menu_delete_folder"
android:title="@string/menu_delete_folder" />
<item android:id="@+id/menu_rename_folder"
android:title="@string/menu_rename_folder" />
</menu>

View file

@ -72,4 +72,8 @@
<item android:id="@+id/menu_infrequent_cutoff"
android:title="@string/menu_infrequent_cutoff"
android:showAsAction="never" />
<item android:id="@+id/menu_save_search"
android:title="Save Search"
android:showAsAction="never"
android:visible="false"/>
</menu>

View file

@ -37,7 +37,8 @@
<string name="title_choose_folders">Choose Folders for Feed %s</string>
<string name="title_rename_feed">Rename Feed %s</string>
<string name="title_rename_folder">Rename Folder %s</string>
<string name="all_stories_row_title">ALL STORIES</string>
<string name="all_stories_title">All Stories</string>
<string name="infrequent_row_title">INFREQUENT SITE STORIES</string>
@ -50,6 +51,7 @@
<string name="read_stories_title">Read Stories</string>
<string name="saved_stories_row_title">SAVED STORIES</string>
<string name="saved_stories_title">Saved Stories</string>
<string name="saved_searches_row_title">SAVED SEARCHES</string>
<string name="top_level">TOP LEVEL</string>
@ -63,6 +65,7 @@
<string name="unshare">DELETE SHARE</string>
<string name="update_shared">UPDATE COMMENT</string>
<string name="feed_name_save">RENAME FEED</string>
<string name="folder_name_save">RENAME FOLDER</string>
<string name="save_this">SAVE</string>
<string name="unsave_this">REMOVE FROM SAVED</string>
@ -132,6 +135,7 @@
<string name="menu_send_story_full">Send story to…</string>
<string name="menu_mark_feed_as_read">Mark feed as read</string>
<string name="menu_delete_feed">Delete feed</string>
<string name="menu_delete_saved_search">Delete saved search</string>
<string name="menu_unfollow">Unfollow user</string>
<string name="menu_choose_folders">Choose folders</string>
<string name="menu_notifications_choose">Notifications…</string>
@ -156,6 +160,8 @@
<string name="menu_unmute_feed">Unmute feed</string>
<string name="menu_mute_folder">Mute folder</string>
<string name="menu_unmute_folder">Unmute folder</string>
<string name="menu_delete_folder">Delete folder</string>
<string name="menu_rename_folder">Rename folder</string>
<string name="menu_instafetch_feed">Insta-fetch stories</string>
<string name="menu_infrequent_cutoff">Infrequent stories per month</string>
<string name="menu_intel">Intelligence trainer</string>
@ -196,7 +202,11 @@
<string name="menu_add_feed">Add new feed</string>
<string name="menu_search">Search</string>
<string name="adding_feed_progress">Adding feed…</string>
<string name="add_folder_name">Please enter folder name</string>
<string name="add_new_folder">+ Add new folder</string>
<string name="new_folder_name_hint">Enter new folder name</string>
<string name="add_feed_error">Could not add feed</string>
<string name="add_folder_error">Could not add folder</string>
<string name="menu_mark_all_as_read">Mark all as read</string>
<string name="menu_logout">Log out</string>
<string name="menu_feedback">Send app feedback</string>
@ -204,6 +214,8 @@
<string name="menu_feedback_email">Email a bug report</string>
<string name="menu_theme_choose">Theme…</string>
<string name="description_add_new_folder_icon">Add new folder icon</string>
<string name="menu_loginas">Login as...</string>
<string name="loginas_title">Login As User</string>
@ -234,6 +246,9 @@
<string name="friends_shares_count">%d SHARES</string>
<string name="unknown_user">Unknown User</string>
<string name="delete_feed_message">Delete feed \&quot;%s\&quot;?</string>
<string name="delete_saved_search_message">Delete saved search %s?</string>
<string name="delete_folder_message">Delete folder \&quot;%s\&quot; and unsubscribe from all feeds inside?</string>
<string name="add_saved_search_message">Save search \&quot;%s\&quot;?</string>
<string name="unfollow_message">Unfollow \&quot;%s\&quot;?</string>
<string name="feed_subscribers">%s subscribers</string>
<string name="feed_opens">%d opens</string>
@ -403,6 +418,7 @@
<string name="sync_status_text">Storing text for %s stories...</string>
<string name="sync_status_images">Storing %s images...</string>
<string name="sync_status_offline">Offline</string>
<string name="sync_status_feed_add">Adding feed …</string>
<string name="volume_key_navigation">Volume Key Navigation</string>
<string name="off">Off</string>

View file

@ -5,32 +5,27 @@ import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.View;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.databinding.ActivityAddfeedexternalBinding;
import com.newsblur.fragment.AddFeedFragment;
import com.newsblur.util.UIUtils;
import com.newsblur.util.ViewUtils;
import com.newsblur.view.ProgressThrobber;
public class AddFeedExternal extends NbActivity implements AddFeedFragment.AddFeedProgressListener {
@Bind(R.id.loading_throb) ProgressThrobber progressView;
@Bind(R.id.progress_text) TextView progressText;
private ActivityAddfeedexternalBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityAddfeedexternalBinding.inflate(getLayoutInflater());
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_addfeedexternal);
ButterKnife.bind(this);
setContentView(binding.getRoot());
progressView.setEnabled(!ViewUtils.isPowerSaveMode(this));
progressView.setColors(UIUtils.getColor(this, R.color.refresh_1),
binding.loadingThrob.setEnabled(!ViewUtils.isPowerSaveMode(this));
binding.loadingThrob.setColors(UIUtils.getColor(this, R.color.refresh_1),
UIUtils.getColor(this, R.color.refresh_2),
UIUtils.getColor(this, R.color.refresh_3),
UIUtils.getColor(this, R.color.refresh_4));
@ -48,9 +43,9 @@ public class AddFeedExternal extends NbActivity implements AddFeedFragment.AddFe
public void addFeedStarted() {
runOnUiThread(new Runnable() {
public void run() {
progressText.setText(R.string.adding_feed_progress);
progressText.setVisibility(View.VISIBLE);
progressView.setVisibility(View.VISIBLE);
binding.progressText.setText(R.string.adding_feed_progress);
binding.progressText.setVisibility(View.VISIBLE);
binding.loadingThrob.setVisibility(View.VISIBLE);
}
});
}

View file

@ -14,4 +14,9 @@ public class AllSharedStoriesItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, R.drawable.ak_icon_blurblogs, getResources().getString(R.string.all_shared_stories_title));
}
@Override
String getSaveSearchFeedId() {
// doesn't have save search option
return null;
}
}

View file

@ -24,4 +24,9 @@ public class AllStoriesItemsList extends ItemsList {
UIUtils.startReadingActivity(fs, hash, this);
}
}
@Override
String getSaveSearchFeedId() {
return "river:";
}
}

View file

@ -11,7 +11,7 @@ import com.newsblur.R;
import com.newsblur.domain.Feed;
import com.newsblur.fragment.DeleteFeedFragment;
import com.newsblur.fragment.FeedIntelTrainerFragment;
import com.newsblur.fragment.RenameFeedFragment;
import com.newsblur.fragment.RenameDialogFragment;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.UIUtils;
@ -79,8 +79,8 @@ public class FeedItemsList extends ItemsList {
return true;
}
if (item.getItemId() == R.id.menu_rename_feed) {
RenameFeedFragment frag = RenameFeedFragment.newInstance(feed);
frag.show(getSupportFragmentManager(), RenameFeedFragment.class.getName());
RenameDialogFragment frag = RenameDialogFragment.newInstance(feed);
frag.show(getSupportFragmentManager(), RenameDialogFragment.class.getName());
return true;
// TODO: since this activity uses a feed object passed as an extra and doesn't query the DB,
// the name change won't be reflected until the activity finishes.
@ -118,4 +118,8 @@ public class FeedItemsList extends ItemsList {
return true;
}
@Override
String getSaveSearchFeedId() {
return "feed:" + feed.feedId;
}
}

View file

@ -19,4 +19,8 @@ public class FolderItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, R.drawable.g_icn_folder_rss, folderName);
}
@Override
String getSaveSearchFeedId() {
return "river:" + folderName;
}
}

View file

@ -14,4 +14,9 @@ public class GlobalSharedStoriesItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, R.drawable.ak_icon_global, getResources().getString(R.string.global_shared_stories_title));
}
@Override
String getSaveSearchFeedId() {
// doesn't have save search option
return null;
}
}

View file

@ -37,4 +37,8 @@ public class InfrequentItemsList extends ItemsList implements InfrequentCutoffCh
restartReadingSession();
}
@Override
String getSaveSearchFeedId() {
return "river:infrequent";
}
}

View file

@ -10,17 +10,14 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.databinding.ActivityItemslistBinding;
import com.newsblur.fragment.ItemSetFragment;
import com.newsblur.fragment.ReadFilterDialogFragment;
import com.newsblur.fragment.SaveSearchFragment;
import com.newsblur.fragment.StoryOrderDialogFragment;
import com.newsblur.fragment.TextSizeDialogFragment;
import com.newsblur.service.NBSyncService;
@ -46,10 +43,9 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
private static final String READ_FILTER = "readFilter";
private static final String DEFAULT_FEED_VIEW = "defaultFeedView";
private static final String BUNDLE_ACTIVE_SEARCH_QUERY = "activeSearchQuery";
private ActivityItemslistBinding binding;
protected ItemSetFragment itemSetFragment;
@Bind(R.id.itemlist_sync_status) TextView overlayStatusText;
@Bind(R.id.itemlist_search_query) EditText searchQueryInput;
protected StateFilter intelState;
protected FeedSet fs;
@ -79,8 +75,8 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
setContentView(R.layout.activity_itemslist);
ButterKnife.bind(this);
binding = ActivityItemslistBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
FragmentManager fragmentManager = getSupportFragmentManager();
itemSetFragment = (ItemSetFragment) fragmentManager.findFragmentByTag(ItemSetFragment.class.getName());
@ -92,19 +88,23 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
transaction.commit();
}
String activeSearchQuery;
if (bundle != null) {
String activeSearchQuery = bundle.getString(BUNDLE_ACTIVE_SEARCH_QUERY);
if (activeSearchQuery != null) {
searchQueryInput.setText(activeSearchQuery);
searchQueryInput.setVisibility(View.VISIBLE);
fs.setSearchQuery(activeSearchQuery);
}
activeSearchQuery = bundle.getString(BUNDLE_ACTIVE_SEARCH_QUERY);
} else {
activeSearchQuery = fs.getSearchQuery();
}
searchQueryInput.setOnKeyListener(new OnKeyListener() {
if (activeSearchQuery != null) {
binding.itemlistSearchQuery.setText(activeSearchQuery);
binding.itemlistSearchQuery.setVisibility(View.VISIBLE);
fs.setSearchQuery(activeSearchQuery);
}
binding.itemlistSearchQuery.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
searchQueryInput.setVisibility(View.GONE);
searchQueryInput.setText("");
binding.itemlistSearchQuery.setVisibility(View.GONE);
binding.itemlistSearchQuery.setText("");
checkSearchQuery();
return true;
}
@ -120,8 +120,8 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (searchQueryInput != null) {
String q = searchQueryInput.getText().toString().trim();
if (binding.itemlistSearchQuery != null) {
String q = binding.itemlistSearchQuery.getText().toString().trim();
if (q.length() > 0) {
outState.putString(BUNDLE_ACTIVE_SEARCH_QUERY, q);
}
@ -224,6 +224,12 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
menu.findItem(R.id.menu_theme_black).setChecked(true);
}
if (!TextUtils.isEmpty(binding.itemlistSearchQuery.getText())) {
menu.findItem(R.id.menu_save_search).setVisible(true);
} else {
menu.findItem(R.id.menu_save_search).setVisible(false);
}
return true;
}
@ -250,11 +256,11 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
textSize.show(getSupportFragmentManager(), TextSizeDialogFragment.class.getName());
return true;
} else if (item.getItemId() == R.id.menu_search_stories) {
if (searchQueryInput.getVisibility() != View.VISIBLE) {
searchQueryInput.setVisibility(View.VISIBLE);
searchQueryInput.requestFocus();
if (binding.itemlistSearchQuery.getVisibility() != View.VISIBLE) {
binding.itemlistSearchQuery.setVisibility(View.VISIBLE);
binding.itemlistSearchQuery.requestFocus();
} else {
searchQueryInput.setVisibility(View.GONE);
binding.itemlistSearchQuery.setVisibility(View.GONE);
checkSearchQuery();
}
} else if (item.getItemId() == R.id.menu_theme_light) {
@ -279,6 +285,14 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
PrefsUtils.updateStoryListStyle(this, fs, StoryListStyle.GRID_C);
itemSetFragment.updateStyle();
}
if (item.getItemId() == R.id.menu_save_search) {
String feedId = getSaveSearchFeedId();
if (feedId != null) {
String query = binding.itemlistSearchQuery.getText().toString();
SaveSearchFragment frag = SaveSearchFragment.newInstance(feedId, query);
frag.show(getSupportFragmentManager(), SaveSearchFragment.class.getName());
}
}
return false;
}
@ -315,23 +329,23 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
}
private void updateStatusIndicators() {
if (overlayStatusText != null) {
if (binding.itemlistSyncStatus != null) {
String syncStatus = NBSyncService.getSyncStatusMessage(this, true);
if (syncStatus != null) {
if (AppConstants.VERBOSE_LOG) {
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
}
overlayStatusText.setText(syncStatus);
overlayStatusText.setVisibility(View.VISIBLE);
binding.itemlistSyncStatus.setText(syncStatus);
binding.itemlistSyncStatus.setVisibility(View.VISIBLE);
} else {
overlayStatusText.setVisibility(View.GONE);
binding.itemlistSyncStatus.setVisibility(View.GONE);
}
}
}
private void checkSearchQuery() {
String oldQuery = fs.getSearchQuery();
String q = searchQueryInput.getText().toString().trim();
String q = binding.itemlistSearchQuery.getText().toString().trim();
if (q.length() < 1) {
q = null;
}
@ -395,4 +409,6 @@ public abstract class ItemsList extends NbActivity implements StoryOrderChangedL
*/
overridePendingTransition(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
}
abstract String getSaveSearchFeedId();
}

View file

@ -17,19 +17,12 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.databinding.ActivityMainBinding;
import com.newsblur.fragment.FeedIntelligenceSelectorFragment;
import com.newsblur.fragment.FolderListFragment;
import com.newsblur.fragment.LoginAsDialogFragment;
@ -54,15 +47,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
private FragmentManager fragmentManager;
private SwipeRefreshLayout swipeLayout;
private boolean wasSwipeEnabled = false;
@Bind(R.id.main_sync_status) TextView overlayStatusText;
@Bind(R.id.empty_view_image) ImageView emptyViewImage;
@Bind(R.id.empty_view_text) TextView emptyViewText;
@Bind(R.id.main_menu_button) Button menuButton;
@Bind(R.id.main_user_image) ImageView userImage;
@Bind(R.id.main_user_name) TextView userName;
@Bind(R.id.main_unread_count_neut_text) TextView unreadCountNeutText;
@Bind(R.id.main_unread_count_posi_text) TextView unreadCountPosiText;
@Bind(R.id.feedlist_search_query) EditText searchQueryInput;
private ActivityMainBinding binding;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -70,16 +55,15 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getActionBar().hide();
// set the status bar to an generic loading message when the activity is first created so
// that something is displayed while the service warms up
overlayStatusText.setText(R.string.loading);
overlayStatusText.setVisibility(View.VISIBLE);
binding.mainSyncStatus.setText(R.string.loading);
binding.mainSyncStatus.setVisibility(View.VISIBLE);
swipeLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container);
swipeLayout.setColorSchemeResources(R.color.refresh_1, R.color.refresh_2, R.color.refresh_3, R.color.refresh_4);
@ -97,25 +81,25 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
Bitmap userPicture = PrefsUtils.getUserImage(this);
if (userPicture != null) {
userPicture = UIUtils.clipAndRound(userPicture, 5, false);
userImage.setImageBitmap(userPicture);
binding.mainUserImage.setImageBitmap(userPicture);
}
userName.setText(PrefsUtils.getUserDetails(this).username);
searchQueryInput.setOnKeyListener(new OnKeyListener() {
binding.mainUserName.setText(PrefsUtils.getUserDetails(this).username);
binding.feedlistSearchQuery.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
searchQueryInput.setVisibility(View.GONE);
searchQueryInput.setText("");
binding.feedlistSearchQuery.setVisibility(View.GONE);
binding.feedlistSearchQuery.setText("");
checkSearchQuery();
return true;
}
if ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN)) {
checkSearchQuery();
return true;
}
}
return false;
}
});
searchQueryInput.addTextChangedListener(new TextWatcher() {
binding.feedlistSearchQuery.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
checkSearchQuery();
}
@ -124,6 +108,31 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
});
FeedUtils.currentFolderName = null;
binding.mainMenuButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickMenuButton();
}
});
binding.mainAddButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickAddButton();
}
});
binding.mainProfileButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickProfileButton();
}
});
binding.mainUserImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickUserButton();
}
});
}
@Override
@ -149,8 +158,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
}
if (folderFeedList.getSearchQuery() != null) {
searchQueryInput.setText(folderFeedList.getSearchQuery());
searchQueryInput.setVisibility(View.VISIBLE);
binding.feedlistSearchQuery.setText(folderFeedList.getSearchQuery());
binding.feedlistSearchQuery.setVisibility(View.VISIBLE);
}
// triggerSync() might not actually do enough to push a UI update if background sync has been
@ -172,8 +181,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
if ( !( (state == StateFilter.ALL) ||
(state == StateFilter.SOME) ||
(state == StateFilter.BEST) ) ) {
searchQueryInput.setText("");
searchQueryInput.setVisibility(View.GONE);
binding.feedlistSearchQuery.setText("");
binding.feedlistSearchQuery.setVisibility(View.GONE);
checkSearchQuery();
}
@ -201,8 +210,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
}
public void updateUnreadCounts(int neutCount, int posiCount) {
unreadCountNeutText.setText(Integer.toString(neutCount));
unreadCountPosiText.setText(Integer.toString(posiCount));
binding.mainUnreadCountNeutText.setText(Integer.toString(neutCount));
binding.mainUnreadCountPosiText.setText(Integer.toString(posiCount));
}
/**
@ -213,22 +222,22 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
public void updateFeedCount(int feedCount) {
if (feedCount < 1 ) {
if (NBSyncService.isFeedCountSyncRunning() || (!folderFeedList.firstCursorSeenYet)) {
emptyViewImage.setVisibility(View.INVISIBLE);
emptyViewText.setVisibility(View.INVISIBLE);
binding.emptyViewImage.setVisibility(View.INVISIBLE);
binding.emptyViewText.setVisibility(View.INVISIBLE);
} else {
emptyViewImage.setVisibility(View.VISIBLE);
binding.emptyViewImage.setVisibility(View.VISIBLE);
if (folderFeedList.currentState == StateFilter.BEST) {
emptyViewText.setText(R.string.empty_list_view_no_focus_stories);
binding.emptyViewText.setText(R.string.empty_list_view_no_focus_stories);
} else if (folderFeedList.currentState == StateFilter.SAVED) {
emptyViewText.setText(R.string.empty_list_view_no_saved_stories);
binding.emptyViewText.setText(R.string.empty_list_view_no_saved_stories);
} else {
emptyViewText.setText(R.string.empty_list_view_no_unread_stories);
binding.emptyViewText.setText(R.string.empty_list_view_no_unread_stories);
}
emptyViewText.setVisibility(View.VISIBLE);
binding.emptyViewText.setVisibility(View.VISIBLE);
}
} else {
emptyViewImage.setVisibility(View.INVISIBLE);
emptyViewText.setVisibility(View.INVISIBLE);
binding.emptyViewImage.setVisibility(View.INVISIBLE);
binding.emptyViewText.setVisibility(View.INVISIBLE);
}
}
@ -239,16 +248,16 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
swipeLayout.setRefreshing(false);
}
if (overlayStatusText != null) {
if (binding.mainSyncStatus != null) {
String syncStatus = NBSyncService.getSyncStatusMessage(this, false);
if (syncStatus != null) {
if (AppConstants.VERBOSE_LOG) {
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
}
overlayStatusText.setText(syncStatus);
overlayStatusText.setVisibility(View.VISIBLE);
binding.mainSyncStatus.setText(syncStatus);
binding.mainSyncStatus.setVisibility(View.VISIBLE);
} else {
overlayStatusText.setVisibility(View.GONE);
binding.mainSyncStatus.setVisibility(View.GONE);
}
}
}
@ -260,8 +269,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
folderFeedList.clearRecents();
}
@OnClick(R.id.main_menu_button) void onClickMenuButton() {
PopupMenu pm = new PopupMenu(this, menuButton);
private void onClickMenuButton() {
PopupMenu pm = new PopupMenu(this, binding.mainMenuButton);
Menu menu = pm.getMenu();
pm.getMenuInflater().inflate(R.menu.main, menu);
@ -308,12 +317,12 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
onRefresh();
return true;
} else if (item.getItemId() == R.id.menu_search_feeds) {
if (searchQueryInput.getVisibility() != View.VISIBLE) {
searchQueryInput.setVisibility(View.VISIBLE);
searchQueryInput.requestFocus();
if (binding.feedlistSearchQuery.getVisibility() != View.VISIBLE) {
binding.feedlistSearchQuery.setVisibility(View.VISIBLE);
binding.feedlistSearchQuery.requestFocus();
} else {
searchQueryInput.setText("");
searchQueryInput.setVisibility(View.GONE);
binding.feedlistSearchQuery.setText("");
binding.feedlistSearchQuery.setVisibility(View.GONE);
checkSearchQuery();
}
} else if (item.getItemId() == R.id.menu_add_feed) {
@ -364,17 +373,17 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
return false;
}
@OnClick(R.id.main_add_button) void onClickAddButton() {
private void onClickAddButton() {
Intent i = new Intent(this, SearchForFeeds.class);
startActivity(i);
}
@OnClick(R.id.main_profile_button) void onClickProfileButton() {
private void onClickProfileButton() {
Intent i = new Intent(this, Profile.class);
startActivity(i);
}
@OnClick(R.id.main_user_image) void onClickUserButton() {
private void onClickUserButton() {
Intent i = new Intent(this, Profile.class);
startActivity(i);
}
@ -404,7 +413,7 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
}
private void checkSearchQuery() {
String q = searchQueryInput.getText().toString().trim();
String q = binding.feedlistSearchQuery.getText().toString().trim();
if (q.length() < 1) {
q = null;
}

View file

@ -14,4 +14,8 @@ public class ReadStoriesItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, R.drawable.g_icn_unread_double, getResources().getString(R.string.read_stories_title));
}
@Override
String getSaveSearchFeedId() {
return "read";
}
}

View file

@ -19,18 +19,14 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.database.ReadingAdapter;
import com.newsblur.databinding.ActivityReadingBinding;
import com.newsblur.domain.Story;
import com.newsblur.fragment.ReadingItemFragment;
import com.newsblur.fragment.ReadingPagerFragment;
@ -77,16 +73,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
protected final Object STORIES_MUTEX = new Object();
protected Cursor stories;
@Bind(android.R.id.content) View contentView; // we use this a ton, so cache it
@Bind(R.id.reading_overlay_left) Button overlayLeft;
@Bind(R.id.reading_overlay_right) Button overlayRight;
@Bind(R.id.reading_overlay_progress) ProgressBar overlayProgress;
@Bind(R.id.reading_overlay_progress_right) ProgressBar overlayProgressRight;
@Bind(R.id.reading_overlay_progress_left) ProgressBar overlayProgressLeft;
@Bind(R.id.reading_overlay_text) Button overlayText;
@Bind(R.id.reading_overlay_send) Button overlaySend;
@Bind(R.id.reading_empty_view_text) View emptyViewText;
@Bind(R.id.reading_sync_status) TextView overlayStatusText;
private View contentView; // we use this a ton, so cache it
ViewPager pager;
ReadingPagerFragment readingFragment;
@ -109,6 +96,8 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
private VolumeKeyNavigation volumeKeyNavigation;
private ActivityReadingBinding binding;
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@ -119,8 +108,9 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
protected void onCreate(Bundle savedInstanceBundle) {
super.onCreate(savedInstanceBundle);
setContentView(R.layout.activity_reading);
ButterKnife.bind(this);
binding = ActivityReadingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
contentView = findViewById(android.R.id.content);
try {
fs = (FeedSet)getIntent().getSerializableExtra(EXTRA_FEEDSET);
@ -167,17 +157,17 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
this.pageHistory = new ArrayList<Story>();
ViewUtils.setViewElevation(overlayLeft, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlayRight, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlayText, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlaySend, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlayProgress, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlayProgressLeft, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(overlayProgressRight, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayLeft, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayRight, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayText, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlaySend, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayProgress, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayProgressLeft, OVERLAY_ELEVATION_DP);
ViewUtils.setViewElevation(binding.readingOverlayProgressRight, OVERLAY_ELEVATION_DP);
// this likes to default to 'on' for some platforms
enableProgressCircle(overlayProgressLeft, false);
enableProgressCircle(overlayProgressRight, false);
enableProgressCircle(binding.readingOverlayProgressLeft, false);
enableProgressCircle(binding.readingOverlayProgressRight, false);
FragmentManager fragmentManager = getSupportFragmentManager();
ReadingPagerFragment fragment = (ReadingPagerFragment) fragmentManager.findFragmentByTag(ReadingPagerFragment.class.getName());
@ -312,7 +302,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
this.onPageSelected(position);
// now that the pager is getting the right story, make it visible
pager.setVisibility(View.VISIBLE);
emptyViewText.setVisibility(View.INVISIBLE);
binding.readingEmptyViewText.setVisibility(View.INVISIBLE);
storyHash = null;
return;
}
@ -405,16 +395,16 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
}
if ((updateType & UPDATE_STATUS) != 0) {
enableMainProgress(NBSyncService.isFeedSetSyncing(this.fs, this));
if (overlayStatusText != null) {
if (binding.readingSyncStatus != null) {
String syncStatus = NBSyncService.getSyncStatusMessage(this, true);
if (syncStatus != null) {
if (AppConstants.VERBOSE_LOG) {
syncStatus = syncStatus + UIUtils.getMemoryUsageDebug(this);
}
overlayStatusText.setText(syncStatus);
overlayStatusText.setVisibility(View.VISIBLE);
binding.readingSyncStatus.setText(syncStatus);
binding.readingSyncStatus.setVisibility(View.VISIBLE);
} else {
overlayStatusText.setVisibility(View.GONE);
binding.readingSyncStatus.setVisibility(View.GONE);
}
}
}
@ -516,11 +506,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
final boolean _overflowExtras = overflowExtras;
runOnUiThread(new Runnable() {
public void run() {
UIUtils.setViewAlpha(overlayLeft, a, true);
UIUtils.setViewAlpha(overlayRight, a, true);
UIUtils.setViewAlpha(overlayProgress, a, true);
UIUtils.setViewAlpha(overlayText, a, true);
UIUtils.setViewAlpha(overlaySend, a, !_overflowExtras);
UIUtils.setViewAlpha(binding.readingOverlayLeft, a, true);
UIUtils.setViewAlpha(binding.readingOverlayRight, a, true);
UIUtils.setViewAlpha(binding.readingOverlayProgress, a, true);
UIUtils.setViewAlpha(binding.readingOverlayText, a, true);
UIUtils.setViewAlpha(binding.readingOverlaySend, a, !_overflowExtras);
}
});
}
@ -544,40 +534,40 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
if (currentUnreadCount > this.startingUnreadCount ) {
this.startingUnreadCount = currentUnreadCount;
}
this.overlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
this.overlayRight.setText((currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
this.binding.readingOverlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
this.binding.readingOverlayRight.setText((currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
if (currentUnreadCount > 0) {
this.overlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRight, android.R.attr.background));
this.binding.readingOverlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRight, android.R.attr.background));
} else {
this.overlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRightDone, android.R.attr.background));
this.binding.readingOverlayRight.setBackgroundResource(UIUtils.getThemedResource(this, R.attr.selectorOverlayBackgroundRightDone, android.R.attr.background));
}
if (this.startingUnreadCount == 0 ) {
// sessions with no unreads just show a full progress bar
this.overlayProgress.setMax(1);
this.overlayProgress.setProgress(1);
this.binding.readingOverlayProgress.setMax(1);
this.binding.readingOverlayProgress.setProgress(1);
} else {
int unreadProgress = this.startingUnreadCount - currentUnreadCount;
this.overlayProgress.setMax(this.startingUnreadCount);
this.overlayProgress.setProgress(unreadProgress);
this.binding.readingOverlayProgress.setMax(this.startingUnreadCount);
this.binding.readingOverlayProgress.setProgress(unreadProgress);
}
this.overlayProgress.invalidate();
this.binding.readingOverlayProgress.invalidate();
invalidateOptionsMenu();
}
private void updateOverlayText() {
if (overlayText == null) return;
if (binding.readingOverlayText == null) return;
runOnUiThread(new Runnable() {
public void run() {
ReadingItemFragment item = getReadingFragment();
if (item == null) return;
if (item.getSelectedViewMode() == DefaultFeedView.STORY) {
overlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundText, android.R.attr.background));
overlayText.setText(R.string.overlay_text);
binding.readingOverlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundText, android.R.attr.background));
binding.readingOverlayText.setText(R.string.overlay_text);
} else {
overlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundStory, android.R.attr.background));
overlayText.setText(R.string.overlay_story);
binding.readingOverlayText.setBackgroundResource(UIUtils.getThemedResource(Reading.this, R.attr.selectorOverlayBackgroundStory, android.R.attr.background));
binding.readingOverlayText.setText(R.string.overlay_story);
}
}
});
@ -610,11 +600,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
}
protected void enableMainProgress(boolean enabled) {
enableProgressCircle(overlayProgressRight, enabled);
enableProgressCircle(binding.readingOverlayProgressRight, enabled);
}
public void enableLeftProgressCircle(boolean enabled) {
enableProgressCircle(overlayProgressLeft, enabled);
enableProgressCircle(binding.readingOverlayProgressLeft, enabled);
}
private void enableProgressCircle(final ProgressBar view, final boolean enabled) {

View file

@ -18,4 +18,13 @@ public class SavedStoriesItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, R.drawable.clock, title);
}
@Override
String getSaveSearchFeedId() {
String feedId = "starred";
String savedTag = fs.getSingleSavedTag();
if (savedTag != null) {
feedId += ":" + savedTag;
}
return feedId;
}
}

View file

@ -19,4 +19,8 @@ public class SocialFeedItemsList extends ItemsList {
UIUtils.setCustomActionBar(this, socialFeed.photoUrl, socialFeed.feedTitle);
}
@Override
String getSaveSearchFeedId() {
return "social:" + socialFeed.userId;
}
}

View file

@ -9,10 +9,9 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.TextView;
import com.newsblur.R;
import com.newsblur.databinding.ActivityWidgetConfigBinding;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.util.FeedOrderFilter;
@ -31,28 +30,21 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import butterknife.Bind;
import butterknife.ButterKnife;
public class WidgetConfig extends NbActivity {
@Bind(R.id.list_view)
ExpandableListView listView;
@Bind(R.id.text_no_subscriptions)
TextView textNoSubscriptions;
private WidgetConfigAdapter adapter;
private ArrayList<Feed> feeds;
private ArrayList<Folder> folders;
private Map<String, Feed> feedMap = new HashMap<>();
private ArrayList<String> folderNames = new ArrayList<>();
private ArrayList<ArrayList<Feed>> folderChildren = new ArrayList<>();
private ActivityWidgetConfigBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_widget_config);
ButterKnife.bind(this);
binding = ActivityWidgetConfigBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getActionBar().setDisplayHomeAsUpEnabled(true);
setupList();
loadFeeds();
@ -164,7 +156,7 @@ public class WidgetConfig extends NbActivity {
private void setupList() {
adapter = new WidgetConfigAdapter(this);
listView.setAdapter(adapter);
binding.listView.setAdapter(adapter);
}
private void loadFeeds() {
@ -288,7 +280,7 @@ public class WidgetConfig extends NbActivity {
private void setAdapterData() {
adapter.setData(this.folderNames, this.folderChildren, this.feeds);
listView.setVisibility(this.feeds.isEmpty() ? View.GONE : View.VISIBLE);
textNoSubscriptions.setVisibility(this.feeds.isEmpty() ? View.VISIBLE : View.GONE);
binding.listView.setVisibility(this.feeds.isEmpty() ? View.GONE : View.VISIBLE);
binding.textNoSubscriptions.setVisibility(this.feeds.isEmpty() ? View.VISIBLE : View.GONE);
}
}

View file

@ -27,6 +27,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
db.execSQL(DatabaseConstants.CLASSIFIER_SQL);
db.execSQL(DatabaseConstants.SOCIALFEED_STORIES_SQL);
db.execSQL(DatabaseConstants.STARREDCOUNTS_SQL);
db.execSQL(DatabaseConstants.SAVED_SEARCH_SQL);
db.execSQL(DatabaseConstants.ACTION_SQL);
db.execSQL(DatabaseConstants.NOTIFY_DISMISS_SQL);
db.execSQL(DatabaseConstants.FEED_TAGS_SQL);
@ -49,6 +50,7 @@ public class BlurDatabase extends SQLiteOpenHelper {
db.execSQL(drop + DatabaseConstants.CLASSIFIER_TABLE);
db.execSQL(drop + DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE);
db.execSQL(drop + DatabaseConstants.STARREDCOUNTS_TABLE);
db.execSQL(drop + DatabaseConstants.SAVED_SEARCH_TABLE);
db.execSQL(drop + DatabaseConstants.ACTION_TABLE);
db.execSQL(drop + DatabaseConstants.NOTIFY_DISMISS_TABLE);
db.execSQL(drop + DatabaseConstants.FEED_TAGS_TABLE);

View file

@ -195,6 +195,13 @@ public class BlurDatabaseHelper {
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ?", selArgs);}
}
public void deleteSavedSearch(String feedId, String query) {
String q = "DELETE FROM " + DatabaseConstants.SAVED_SEARCH_TABLE +
" WHERE " + DatabaseConstants.SAVED_SEARCH_FEED_ID + " = '" + feedId + "'" +
" AND " + DatabaseConstants.SAVED_SEARCH_QUERY + " = '" + query + "'";
synchronized (RW_MUTEX) {dbRW.execSQL(q);}
}
public Feed getFeed(String feedId) {
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, DatabaseConstants.FEED_ID + " = ?", new String[] {feedId}, null, null, null);
Feed result = null;
@ -237,7 +244,8 @@ public class BlurDatabaseHelper {
public void setFeedsFolders(List<ContentValues> folderValues,
List<ContentValues> feedValues,
List<ContentValues> socialFeedValues,
List<ContentValues> starredCountValues) {
List<ContentValues> starredCountValues,
List<ContentValues> savedSearchValues) {
synchronized (RW_MUTEX) {
dbRW.beginTransaction();
try {
@ -248,10 +256,12 @@ public class BlurDatabaseHelper {
dbRW.delete(DatabaseConstants.COMMENT_TABLE, null, null);
dbRW.delete(DatabaseConstants.REPLY_TABLE, null, null);
dbRW.delete(DatabaseConstants.STARREDCOUNTS_TABLE, null, null);
dbRW.delete(DatabaseConstants.SAVED_SEARCH_TABLE, null, null);
bulkInsertValuesExtSync(DatabaseConstants.FOLDER_TABLE, folderValues);
bulkInsertValuesExtSync(DatabaseConstants.FEED_TABLE, feedValues);
bulkInsertValuesExtSync(DatabaseConstants.SOCIALFEED_TABLE, socialFeedValues);
bulkInsertValuesExtSync(DatabaseConstants.STARREDCOUNTS_TABLE, starredCountValues);
bulkInsertValuesExtSync(DatabaseConstants.SAVED_SEARCH_TABLE, savedSearchValues);
dbRW.setTransactionSuccessful();
} finally {
dbRW.endTransaction();
@ -1033,6 +1043,17 @@ public class BlurDatabaseHelper {
return result;
}
@Nullable
public StarredCount getStarredFeedByTag(String tag) {
Cursor c = dbRO.query(DatabaseConstants.STARREDCOUNTS_TABLE, null, DatabaseConstants.STARREDCOUNTS_TAG + " = ?", new String[] {tag}, null, null, null);
StarredCount result = null;
while (c.moveToNext()) {
result = StarredCount.fromCursor(c);
}
c.close();
return result;
}
public List<Folder> getFolders() {
Cursor c = getFoldersCursor(null);
List<Folder> folders = new ArrayList<Folder>(c.getCount());
@ -1069,11 +1090,21 @@ public class BlurDatabaseHelper {
};
}
public Loader<Cursor> getSavedSearchLoader() {
return new QueryCursorLoader(context) {
protected Cursor createCursor() {return getSavedSearchCursor(cancellationSignal);}
};
}
private Cursor getSavedStoryCountsCursor(CancellationSignal cancellationSignal) {
Cursor c = query(false, DatabaseConstants.STARREDCOUNTS_TABLE, null, null, null, null, null, null, null, cancellationSignal);
return c;
}
private Cursor getSavedSearchCursor(CancellationSignal cancellationSignal) {
return query(false, DatabaseConstants.SAVED_SEARCH_TABLE, null, null, null, null, null, null, null, cancellationSignal);
}
public Cursor getNotifyFocusStoriesCursor() {
return rawQuery(DatabaseConstants.NOTIFY_FOCUS_STORY_QUERY, null, null);
}

View file

@ -148,6 +148,13 @@ public class DatabaseConstants {
public static final String STARREDCOUNTS_TAG = "tag";
public static final String STARREDCOUNTS_FEEDID = "feed_id";
public static final String SAVED_SEARCH_TABLE = "saved_search";
public static final String SAVED_SEARCH_FEED_TITLE = "saved_search_title";
public static final String SAVED_SEARCH_FAVICON = "saved_search_favicon";
public static final String SAVED_SEARCH_ADDRESS = "saved_search_address";
public static final String SAVED_SEARCH_QUERY = "saved_search_query";
public static final String SAVED_SEARCH_FEED_ID = "saved_search_feed_id";
public static final String NOTIFY_DISMISS_TABLE = "notify_dimiss";
public static final String NOTIFY_DISMISS_STORY_HASH = "story_hash";
public static final String NOTIFY_DISMISS_TIME = "time";
@ -185,7 +192,7 @@ public class DatabaseConstants {
FEED_FAVICON_BORDER + TEXT + ", " +
FEED_LINK + TEXT + ", " +
FEED_SUBSCRIBERS + TEXT + ", " +
FEED_TITLE + TEXT + ", " +
FEED_TITLE + TEXT + ", " +
FEED_OPENS + INTEGER + ", " +
FEED_AVERAGE_STORIES_PER_MONTH + INTEGER + ", " +
FEED_LAST_STORY_DATE + TEXT + ", " +
@ -303,6 +310,14 @@ public class DatabaseConstants {
STARREDCOUNTS_FEEDID + TEXT +
")";
static final String SAVED_SEARCH_SQL = "CREATE TABLE " + SAVED_SEARCH_TABLE + " (" +
SAVED_SEARCH_FEED_TITLE + TEXT + ", " +
SAVED_SEARCH_FAVICON + TEXT + ", " +
SAVED_SEARCH_ADDRESS + TEXT + ", " +
SAVED_SEARCH_QUERY + TEXT + ", " +
SAVED_SEARCH_FEED_ID +
")";
static final String NOTIFY_DISMISS_SQL = "CREATE TABLE " + NOTIFY_DISMISS_TABLE + " (" +
NOTIFY_DISMISS_STORY_HASH + TEXT + ", " +
NOTIFY_DISMISS_TIME + INTEGER + " NOT NULL " +

View file

@ -27,6 +27,7 @@ import android.widget.TextView;
import com.newsblur.R;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.StarredCount;
import com.newsblur.domain.SocialFeed;
import com.newsblur.util.AppConstants;
@ -34,14 +35,15 @@ import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.StateFilter;
import com.newsblur.util.UIUtils;
/**
* Custom adapter to display a nested folder/feed list in an ExpandableListView.
*/
public class FolderListAdapter extends BaseExpandableListAdapter {
private enum GroupType { GLOBAL_SHARED_STORIES, ALL_SHARED_STORIES, INFREQUENT_STORIES, ALL_STORIES, FOLDER, READ_STORIES, SAVED_STORIES }
private enum ChildType { SOCIAL_FEED, FEED, SAVED_BY_TAG }
private enum GroupType { GLOBAL_SHARED_STORIES, ALL_SHARED_STORIES, INFREQUENT_STORIES, ALL_STORIES, FOLDER, READ_STORIES, SAVED_SEARCHES, SAVED_STORIES }
private enum ChildType { SOCIAL_FEED, FEED, SAVED_BY_TAG, SAVED_SEARCH }
// The following keys are used to mark the position of the special meta-folders within
// the folders array. Since the ExpandableListView doesn't handle collapsing of views
@ -56,6 +58,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
private static final String INFREQUENT_SITE_STORIES_GROUP_KEY = "INFREQUENT_SITE_STORIES_GROUP_KEY";
private static final String READ_STORIES_GROUP_KEY = "READ_STORIES_GROUP_KEY";
private static final String SAVED_STORIES_GROUP_KEY = "SAVED_STORIES_GROUP_KEY";
private static final String SAVED_SEARCHES_GROUP_KEY = "SAVED_SEARCHES_GROUP_KEY";
private final static float defaultTextSize_childName = 14;
private final static float defaultTextSize_groupName = 13;
@ -101,6 +104,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
/** Starred story sets in display order. */
private List<StarredCount> starredCountsByTag = Collections.emptyList();
/** Saved Searches */
private List<SavedSearch> savedSearches = Collections.emptyList();
private int savedStoriesTotalCount;
@ -162,6 +167,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
if (v == null) v = inflater.inflate(R.layout.row_infrequent_stories, null, false);
} else if (isRowReadStories(groupPosition)) {
if (v == null) v = inflater.inflate(R.layout.row_read_stories, null, false);
} else if (isRowSavedSearches(groupPosition)) {
if (v == null) v = inflater.inflate(R.layout.row_saved_searches, null, false);
} else if (isRowSavedStories(groupPosition)) {
if (v == null) v = inflater.inflate(R.layout.row_saved_stories, null, false);
TextView savedSum = ((TextView) v.findViewById(R.id.row_foldersum));
@ -268,7 +275,15 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
TextView savedCounter =((TextView) v.findViewById(R.id.row_saved_tag_sum));
savedCounter.setText(Integer.toString(checkNegativeUnreads(sc.count)));
savedCounter.setTextSize(textSize * defaultTextSize_count);
} else {
} else if (isRowSavedSearches(groupPosition)) {
if (v == null) v = inflater.inflate(R.layout.row_saved_search_child, parent, false);
SavedSearch ss = savedSearches.get(childPosition);
TextView nameView = v.findViewById(R.id.row_saved_search_title);
nameView.setText(UIUtils.fromHtml(ss.feedTitle));
ImageView iconView = v.findViewById(R.id.row_saved_search_icon);
FeedUtils.iconLoader.preCheck(ss.faviconUrl, iconView);
FeedUtils.iconLoader.displayImage(ss.faviconUrl, iconView, 0 , false);
} else {
if (v == null) v = inflater.inflate(R.layout.row_feed, parent, false);
Feed f = activeFolderChildren.get(groupPosition).get(childPosition);
TextView nameView =((TextView) v.findViewById(R.id.row_feedname));
@ -392,6 +407,11 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return folder.name;
}
public Folder getGroupFolder(int groupPosition) {
String flatFolderName = activeFolderNames.get(groupPosition);
return flatFolders.get(flatFolderName);
}
@Override
public synchronized int getGroupCount() {
if (activeFolderNames == null) return 0;
@ -409,7 +429,9 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return socialFeedsActive.size();
} else if (isRowSavedStories(groupPosition)) {
return starredCountsByTag.size();
} else {
} else if (isRowSavedSearches(groupPosition)) {
return savedSearches.size();
} else {
return activeFolderChildren.get(groupPosition).size();
}
}
@ -421,6 +443,9 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return FeedSet.singleSocialFeed(socialFeed.userId, socialFeed.username);
} else if (isRowSavedStories(groupPosition)) {
return FeedSet.singleSavedTag(starredCountsByTag.get(childPosition).tag);
} else if (isRowSavedSearches(groupPosition)) {
SavedSearch savedSearch = savedSearches.get(childPosition);
return FeedSet.singleSavedSearch(savedSearch.feedId, savedSearch.query);
} else {
Feed feed = activeFolderChildren.get(groupPosition).get(childPosition);
FeedSet fs = FeedSet.singleFeed(feed.feedId);
@ -465,6 +490,10 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return SAVED_STORIES_GROUP_KEY.equals(activeFolderNames.get(groupPosition));
}
public boolean isRowSavedSearches(int groupPosition) {
return SAVED_SEARCHES_GROUP_KEY.equals(activeFolderNames.get(groupPosition));
}
/**
* Determines if the row at the specified position is last of the special rows, under which
* un-foldered "root level" feeds are created as children. These feeds are not in any folder,
@ -564,6 +593,17 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
recountFeeds();
notifyDataSetChanged();
}
public synchronized void setSavedSearchesCursor(Cursor cursor) {
if (!cursor.isBeforeFirst()) return;
savedSearches = new ArrayList<>();
while (cursor.moveToNext()) {
SavedSearch savedSearch = SavedSearch.fromCursor(cursor);
savedSearches.add(savedSearch);
}
Collections.sort(savedSearches, SavedSearch.SavedSearchComparatorByTitle);
notifyDataSetChanged();
}
private void recountFeeds() {
if ((folders == null) || (feeds == null)) return;
@ -619,6 +659,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
}
// add the always-present (if enabled) special rows/folders that got at the bottom of the list
addSpecialRow(READ_STORIES_GROUP_KEY);
addSpecialRow(SAVED_SEARCHES_GROUP_KEY);
addSpecialRow(SAVED_STORIES_GROUP_KEY);
recountChildren();
}
@ -728,6 +769,7 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
totalNeutCount = 0;
totalPosCount = 0;
safeClear(savedSearches);
safeClear(starredCountsByTag);
safeClear(closedFolders);
@ -755,6 +797,11 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return socialFeedsActive.get(childPosition);
}
/** Get the cached SavedSearch object at the given saved search list location. */
public SavedSearch getSavedSearch(int childPosition) {
return savedSearches.get(childPosition);
}
public synchronized void changeState(StateFilter state) {
currentState = state;
lastFeedViewedId = null; // clear when changing modes
@ -804,6 +851,8 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return GroupType.INFREQUENT_STORIES.ordinal();
} else if (isRowReadStories(groupPosition)) {
return GroupType.READ_STORIES.ordinal();
} else if (isRowSavedSearches(groupPosition)) {
return GroupType.SAVED_SEARCHES.ordinal();
} else if (isRowSavedStories(groupPosition)) {
return GroupType.SAVED_STORIES.ordinal();
} else {
@ -817,7 +866,9 @@ public class FolderListAdapter extends BaseExpandableListAdapter {
return ChildType.SOCIAL_FEED.ordinal();
} else if (isRowSavedStories(groupPosition)) {
return ChildType.SAVED_BY_TAG.ordinal();
} else {
} else if (isRowSavedSearches(groupPosition)) {
return ChildType.SAVED_SEARCH.ordinal();
} else {
return ChildType.FEED.ordinal();
}
}

View file

@ -1,6 +1,5 @@
package com.newsblur.database;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Typeface;
@ -22,18 +21,13 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import butterknife.Bind;
import butterknife.ButterKnife;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.newsblur.R;
import com.newsblur.activity.FeedItemsList;
import com.newsblur.activity.ItemsList;
import com.newsblur.activity.NbActivity;
import com.newsblur.domain.Story;
import com.newsblur.domain.UserDetails;
@ -313,16 +307,16 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
MenuItem.OnMenuItemClickListener,
View.OnTouchListener {
@Bind(R.id.story_item_favicon_borderbar_1) View leftBarOne;
@Bind(R.id.story_item_favicon_borderbar_2) View leftBarTwo;
@Bind(R.id.story_item_inteldot) ImageView intelDot;
@Bind(R.id.story_item_thumbnail) ImageView thumbView;
@Bind(R.id.story_item_feedicon) ImageView feedIconView;
@Bind(R.id.story_item_feedtitle) TextView feedTitleView;
@Bind(R.id.story_item_title) TextView storyTitleView;
@Bind(R.id.story_item_date) TextView storyDate;
@Bind(R.id.story_item_saved_icon) View savedView;
@Bind(R.id.story_item_shared_icon) View sharedView;
View leftBarOne;
View leftBarTwo;
ImageView intelDot;
ImageView thumbView;
ImageView feedIconView;
TextView feedTitleView;
TextView storyTitleView;
TextView storyDate;
View savedView;
View sharedView;
Story story;
ImageLoader.PhotoToLoad thumbLoader;
@ -332,9 +326,20 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
boolean gestureL2R = false;
boolean gestureDebounce = false;
public StoryViewHolder(View view) {
super(view);
ButterKnife.bind(StoryViewHolder.this, view);
leftBarOne = view.findViewById(R.id.story_item_favicon_borderbar_1);
leftBarTwo = view.findViewById(R.id.story_item_favicon_borderbar_2);
intelDot = view.findViewById(R.id.story_item_inteldot);
thumbView = view.findViewById(R.id.story_item_thumbnail);
feedIconView = view.findViewById(R.id.story_item_feedicon);
feedTitleView = view.findViewById(R.id.story_item_feedtitle);
storyTitleView = view.findViewById(R.id.story_item_title);
storyDate = view.findViewById(R.id.story_item_date);
savedView = view.findViewById(R.id.story_item_saved_icon);
sharedView = view.findViewById(R.id.story_item_shared_icon);
view.setOnClickListener(StoryViewHolder.this);
view.setOnCreateContextMenuListener(StoryViewHolder.this);
view.setOnTouchListener(StoryViewHolder.this);
@ -472,10 +477,12 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
}
public class StoryRowViewHolder extends StoryViewHolder {
@Bind(R.id.story_item_author) TextView storyAuthor;
@Bind(R.id.story_item_content) TextView storySnippet;
TextView storyAuthor;
TextView storySnippet;
public StoryRowViewHolder(View view) {
super(view);
storyAuthor = view.findViewById(R.id.story_item_author);
storySnippet = view.findViewById(R.id.story_item_content);
}
}
@ -660,12 +667,13 @@ public class StoryViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
}
}
public class FooterViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.footer_view_inner) FrameLayout innerView;
public static class FooterViewHolder extends RecyclerView.ViewHolder {
FrameLayout innerView;
public FooterViewHolder(View view) {
super(view);
ButterKnife.bind(FooterViewHolder.this, view);
innerView = view.findViewById(R.id.footer_view_inner);
}
}

View file

@ -2,6 +2,7 @@ package com.newsblur.domain;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.util.Collection;
@ -61,6 +62,15 @@ public class Folder {
public void removeOrphanFeedIds(Collection<String> orphanFeedIds) {
feedIds.removeAll(orphanFeedIds);
}
@Nullable
public String getFirstParentName() {
String folderParentName = null;
if (!parents.isEmpty()) {
folderParentName = parents.get(0);
}
return folderParentName;
}
@Override
public boolean equals(Object otherFolder) {

View file

@ -0,0 +1,122 @@
package com.newsblur.domain;
import android.content.ContentValues;
import android.database.Cursor;
import com.google.gson.annotations.SerializedName;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import java.util.Comparator;
public class SavedSearch {
@SerializedName("query")
public String query;
@SerializedName("feed_id")
public String feedId;
@SerializedName("feed_address")
public String feedAddress;
public String feedTitle;
public String faviconUrl;
public ContentValues getValues() {
ContentValues values = new ContentValues();
String feedTitle = "\"<b>" + query + "</b>\" in <b>" + getFeedTitle() + "</b>";
values.put(DatabaseConstants.SAVED_SEARCH_FEED_TITLE, feedTitle);
values.put(DatabaseConstants.SAVED_SEARCH_FAVICON, getFaviconUrl());
values.put(DatabaseConstants.SAVED_SEARCH_ADDRESS, feedAddress);
values.put(DatabaseConstants.SAVED_SEARCH_QUERY, query);
values.put(DatabaseConstants.SAVED_SEARCH_FEED_ID, feedId);
return values;
}
public static SavedSearch fromCursor(Cursor cursor) {
if (cursor.isBeforeFirst()) {
cursor.moveToFirst();
}
SavedSearch savedSearch = new SavedSearch();
savedSearch.feedTitle = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FEED_TITLE));
savedSearch.faviconUrl = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FAVICON));
savedSearch.feedAddress = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_ADDRESS));
savedSearch.query = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_QUERY));
savedSearch.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.SAVED_SEARCH_FEED_ID));
return savedSearch;
}
private String getFeedTitle() {
String feedTitle = null;
if (feedId.equals("river:")) {
feedTitle = "All Site Stories";
} else if (feedId.equals("river:infrequent")) {
feedTitle = "Infrequent Site Stories";
} else if (feedId.startsWith("river:")) {
String folderName = feedId.replace("river:", "");
FeedSet fs = FeedUtils.feedSetFromFolderName(folderName);
feedTitle = fs.getFolderName();
} else if (feedId.equals("read")) {
feedTitle = "Read Stories";
} else if (feedId.startsWith("starred")) {
feedTitle = "Saved Stories";
String tag = feedId.replace("starred:", "");
StarredCount starredFeed = FeedUtils.getStarredFeedByTag(tag);
if (starredFeed != null) {
String tagSlug = tag.replace(" ", "-");
if (starredFeed.tag.equals(tag) || starredFeed.tag.equals(tagSlug)) {
feedTitle = feedTitle + " - " + starredFeed.tag;
}
}
} else if (feedId.startsWith("feed:")) {
Feed feed = FeedUtils.getFeed(feedId.replace("feed:", ""));
if (feed == null) return null;
feedTitle = feed.title;
} else if (feedId.startsWith("social:")) {
Feed feed = FeedUtils.getFeed(feedId.replace("social:", ""));
if (feed == null) return null;
feedTitle = feed.title;
}
return feedTitle;
}
private String getFaviconUrl() {
String url = null;
if (feedId.equals("river:") || feedId.equals("river:infrequent")) {
url = "https://newsblur.com/media/img/icons/circular/ak-icon-allstories.png";
} else if (feedId.startsWith("river:")) {
url = "https://newsblur.com/media/img/icons/circular/g_icn_folder.png";
} else if (feedId.equals("read")) {
url = "https://newsblur.com/media/img/icons/circular/g_icn_unread.png";
} else if (feedId.equals("starred")) {
url = "https://newsblur.com/media/img/icons/circular/clock.png";
} else if (feedId.startsWith("starred:")) {
url = "https://newsblur.com/media/img/reader/tag.png";
} else if (feedId.startsWith("feed:")) {
Feed feed = FeedUtils.getFeed(feedId.replace("feed:", ""));
if (feed != null) {
url = feed.faviconUrl;
}
} else if (feedId.startsWith("social:")) {
Feed feed = FeedUtils.getFeed(feedId.replace("social:", ""));
if (feed != null) {
url = feed.faviconUrl;
}
}
if (url == null) {
url = "https://newsblur.com/media/img/icons/circular/g_icn_search_black.png";
}
return url;
}
public final static Comparator<SavedSearch> SavedSearchComparatorByTitle = new Comparator<SavedSearch>() {
@Override
public int compare(SavedSearch ss1, SavedSearch ss2) {
return String.CASE_INSENSITIVE_ORDER.compare(ss1.feedTitle, ss2.feedTitle);
}
};
}

View file

@ -2,6 +2,7 @@ package com.newsblur.domain;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -82,6 +83,12 @@ public class Story implements Serializable {
@SerializedName("story_hash")
public String storyHash;
@SerializedName("secure_image_urls")
public Map<String, String> secureImageUrls;
@SerializedName("secure_image_thumbnails")
public Map<String, String> secureImageThumbnails;
// NOTE: this is parsed and saved to the DB, but is *not* generally un-thawed when stories are fetched back from the DB
@SerializedName("image_urls")
public String[] imageUrls;
@ -310,10 +317,13 @@ public class Story implements Serializable {
return YT_THUMB_PRE + ytUrl + YT_THUMB_POST;
}
if ((story.imageUrls != null) && (story.imageUrls.length > 0)) {
return story.imageUrls[0];
if (story.imageUrls != null && story.imageUrls.length > 0) {
String thumbnail = story.imageUrls[0];
if (thumbnail.startsWith("http://") && story.secureImageThumbnails != null && story.secureImageThumbnails.containsKey(thumbnail)){
thumbnail = story.secureImageThumbnails.get(thumbnail);
}
return thumbnail;
}
return null;
}

View file

@ -3,83 +3,217 @@ package com.newsblur.fragment;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.newsblur.R;
import com.newsblur.activity.Main;
import com.newsblur.databinding.DialogAddFeedBinding;
import com.newsblur.databinding.RowAddFeedFolderBinding;
import com.newsblur.domain.Folder;
import com.newsblur.network.APIManager;
import com.newsblur.network.domain.AddFeedResponse;
import com.newsblur.network.domain.NewsBlurResponse;
import com.newsblur.service.NBSyncService;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.UIUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class AddFeedFragment extends DialogFragment {
private static final String FEED_URI = "feed_url";
private static final String FEED_NAME = "feed_name";
private static final String FEED_URI = "feed_url";
private static final String FEED_NAME = "feed_name";
private DialogAddFeedBinding binding;
public static AddFeedFragment newInstance(String feedUri, String feedName) {
AddFeedFragment frag = new AddFeedFragment();
Bundle args = new Bundle();
args.putString(FEED_URI, feedUri);
args.putString(FEED_NAME, feedName);
frag.setArguments(args);
return frag;
}
public static AddFeedFragment newInstance(String feedUri, String feedName) {
AddFeedFragment frag = new AddFeedFragment();
Bundle args = new Bundle();
args.putString(FEED_URI, feedUri);
args.putString(FEED_NAME, feedName);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String addFeedString = getResources().getString(R.string.add_feed_message);
final Activity activity = getActivity();
final APIManager apiManager = new APIManager(activity);
final Intent intent = new Intent(activity, Main.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_add_feed, null);
binding = DialogAddFeedBinding.bind(v);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(String.format(addFeedString, getArguments().getString(FEED_NAME)));
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
new AsyncTask<Void, Void, AddFeedResponse>() {
@Override
protected AddFeedResponse doInBackground(Void... arg) {
((AddFeedProgressListener) activity).addFeedStarted();
return apiManager.addFeed(getArguments().getString(FEED_URI));
}
builder.setTitle("Choose folder for " + getArguments().getString(FEED_NAME));
builder.setView(v);
@Override
protected void onPostExecute(AddFeedResponse result) {
if (!result.isError()) {
// trigger a sync when we return to Main so that the new feed will show up
NBSyncService.forceFeedsFolders();
intent.putExtra(Main.EXTRA_FORCE_SHOW_FEED_ID, result.feed.feedId);
} else {
UIUtils.safeToast(activity, R.string.add_feed_error, Toast.LENGTH_SHORT);
}
activity.startActivity(intent);
activity.finish();
AddFeedFragment.this.dismiss();
};
}.execute();
}
});
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
AddFeedAdapter adapter = new AddFeedAdapter(new OnFolderClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
AddFeedFragment.this.dismiss();
activity.startActivity(intent);
activity.finish();
public void onItemClick(Folder folder) {
addFeed(activity, apiManager, folder.name);
}
});
binding.textAddFolderTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (binding.containerAddFolder.getVisibility() == View.GONE) {
binding.containerAddFolder.setVisibility(View.VISIBLE);
} else {
binding.containerAddFolder.setVisibility(View.GONE);
}
}
});
binding.icCreateFolder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (binding.inputFolderName.getText().length() == 0) {
Toast.makeText(activity, R.string.add_folder_name, Toast.LENGTH_SHORT).show();
} else {
addFeedToNewFolder(activity, apiManager, binding.inputFolderName.getText().toString());
}
}
});
binding.recyclerViewFolders.addItemDecoration(new DividerItemDecoration(activity, LinearLayoutManager.VERTICAL));
binding.recyclerViewFolders.setAdapter(adapter);
adapter.setFolders(FeedUtils.dbHelper.getFolders());
return builder.create();
}
public interface AddFeedProgressListener {
public abstract void addFeedStarted();
private void addFeedToNewFolder(final Activity activity, final APIManager apiManager, final String folderName) {
binding.icCreateFolder.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
binding.inputFolderName.setEnabled(false);
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
protected NewsBlurResponse doInBackground(Void... voids) {
return apiManager.addFolder(folderName);
}
@Override
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
super.onPostExecute(newsBlurResponse);
binding.inputFolderName.setEnabled(true);
if (!newsBlurResponse.isError()) {
binding.containerAddFolder.setVisibility(View.GONE);
binding.inputFolderName.getText().clear();
addFeed(activity, apiManager, folderName);
} else {
UIUtils.safeToast(activity, R.string.add_folder_error, Toast.LENGTH_SHORT);
}
}
}.execute();
}
}
private void addFeed(final Activity activity, final APIManager apiManager, @Nullable final String folderName) {
binding.textSyncStatus.setVisibility(View.VISIBLE);
new AsyncTask<Void, Void, AddFeedResponse>() {
@Override
protected AddFeedResponse doInBackground(Void... voids) {
((AddFeedProgressListener) activity).addFeedStarted();
String feedUrl = getArguments().getString(FEED_URI);
return apiManager.addFeed(feedUrl, folderName);
}
@Override
protected void onPostExecute(AddFeedResponse result) {
super.onPostExecute(result);
binding.textSyncStatus.setVisibility(View.GONE);
final Intent intent = new Intent(activity, Main.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (!result.isError()) {
// trigger a sync when we return to Main so that the new feed will show up
NBSyncService.forceFeedsFolders();
intent.putExtra(Main.EXTRA_FORCE_SHOW_FEED_ID, result.feed.feedId);
} else {
UIUtils.safeToast(activity, R.string.add_feed_error, Toast.LENGTH_SHORT);
}
activity.startActivity(intent);
activity.finish();
AddFeedFragment.this.dismiss();
}
}.execute();
}
private static class AddFeedAdapter extends RecyclerView.Adapter<AddFeedAdapter.FolderViewHolder> {
AddFeedAdapter(OnFolderClickListener listener) {
this.listener = listener;
}
private final List<Folder> folders = new ArrayList<>();
private OnFolderClickListener listener;
@NonNull
@Override
public AddFeedAdapter.FolderViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.row_add_feed_folder, viewGroup, false);
return new FolderViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AddFeedAdapter.FolderViewHolder viewHolder, int position) {
final Folder folder = folders.get(position);
if (folder.name.equals(AppConstants.ROOT_FOLDER)) {
viewHolder.binding.textFolderTitle.setText(R.string.top_level);
} else {
viewHolder.binding.textFolderTitle.setText(folder.flatName());
}
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onItemClick(folder);
}
});
}
@Override
public int getItemCount() {
return folders.size();
}
public void setFolders(List<Folder> folders) {
Collections.sort(folders, Folder.FolderComparator);
this.folders.clear();
this.folders.addAll(folders);
this.notifyDataSetChanged();
}
static class FolderViewHolder extends RecyclerView.ViewHolder {
public RowAddFeedFolderBinding binding;
public FolderViewHolder(@NonNull View itemView) {
super(itemView);
binding = RowAddFeedFolderBinding.bind(itemView);
}
}
}
public interface AddFeedProgressListener {
void addFeedStarted();
}
public interface OnFolderClickListener {
void onItemClick(Folder folder);
}
}

View file

@ -19,12 +19,9 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ListAdapter;
import android.widget.ListView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.databinding.DialogChoosefoldersBinding;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.util.FeedUtils;
@ -33,8 +30,6 @@ public class ChooseFoldersFragment extends DialogFragment {
private Feed feed;
@Bind(R.id.choose_folders_list) ListView listView;
public static ChooseFoldersFragment newInstance(Feed feed) {
ChooseFoldersFragment fragment = new ChooseFoldersFragment();
Bundle args = new Bundle();
@ -62,7 +57,7 @@ public class ChooseFoldersFragment extends DialogFragment {
final Activity activity = getActivity();
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_choosefolders, null);
ButterKnife.bind(this, v);
DialogChoosefoldersBinding binding = DialogChoosefoldersBinding.bind(v);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(String.format(getResources().getString(R.string.title_choose_folders), feed.title));
@ -107,7 +102,7 @@ public class ChooseFoldersFragment extends DialogFragment {
return v;
}
};
listView.setAdapter(adapter);
binding.chooseFoldersList.setAdapter(adapter);
Dialog dialog = builder.create();
dialog.getWindow().getAttributes().gravity = Gravity.BOTTOM;

View file

@ -4,9 +4,11 @@ import com.newsblur.R;
import com.newsblur.activity.ItemsList;
import com.newsblur.activity.NbActivity;
import com.newsblur.domain.Feed;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.SocialFeed;
import com.newsblur.network.APIManager;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.UIUtils;
import android.app.Activity;
import android.app.AlertDialog;
@ -22,7 +24,9 @@ public class DeleteFeedFragment extends DialogFragment {
private static final String FOLDER_NAME = "folder_name";
private static final String NORMAL_FEED = "normal";
private static final String SOCIAL_FEED = "social";
private static final String SAVED_SEARCH_FEED = "saved_search";
private static final String QUERY = "query";
public static DeleteFeedFragment newInstance(Feed feed, String folderName) {
DeleteFeedFragment frag = new DeleteFeedFragment();
Bundle args = new Bundle();
@ -44,11 +48,25 @@ public class DeleteFeedFragment extends DialogFragment {
return frag;
}
public static DeleteFeedFragment newInstance(SavedSearch savedSearch) {
DeleteFeedFragment frag = new DeleteFeedFragment();
Bundle args = new Bundle();
args.putString(FEED_TYPE, SAVED_SEARCH_FEED);
args.putString(FEED_ID, savedSearch.feedId);
args.putString(FEED_NAME, savedSearch.feedTitle);
args.putString(QUERY, savedSearch.query);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
if (getArguments().getString(FEED_TYPE).equals(NORMAL_FEED)) {
builder.setMessage(String.format(getResources().getString(R.string.delete_feed_message), getArguments().getString(FEED_NAME)));
} else if (getArguments().getString(FEED_TYPE).equals(SAVED_SEARCH_FEED)) {
String message = String.format(getResources().getString(R.string.delete_saved_search_message), getArguments().getString(FEED_NAME));
builder.setMessage(UIUtils.fromHtml(message));
} else {
builder.setMessage(String.format(getResources().getString(R.string.unfollow_message), getArguments().getString(FEED_NAME)));
}
@ -57,6 +75,8 @@ public class DeleteFeedFragment extends DialogFragment {
public void onClick(DialogInterface dialogInterface, int i) {
if (getArguments().getString(FEED_TYPE).equals(NORMAL_FEED)) {
FeedUtils.deleteFeed(getArguments().getString(FEED_ID), getArguments().getString(FOLDER_NAME), getActivity(), new APIManager(getActivity()));
} else if (getArguments().getString(FEED_TYPE).equals(SAVED_SEARCH_FEED)) {
FeedUtils.deleteSavedSearch(getArguments().getString(FEED_ID), getArguments().getString(QUERY), getActivity(), new APIManager(getActivity()));
} else {
FeedUtils.deleteSocialFeed(getArguments().getString(FEED_ID), getActivity(), new APIManager(getActivity()));
}

View file

@ -0,0 +1,57 @@
package com.newsblur.fragment;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.text.TextUtils;
import com.newsblur.R;
import com.newsblur.network.APIManager;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedUtils;
public class DeleteFolderFragment extends DialogFragment {
private static final String FOLDER_NAME = "folder_name";
private static final String FOLDER_PARENT = "folder_parent";
public static DeleteFolderFragment newInstance(String folderName, String folderParent) {
DeleteFolderFragment frag = new DeleteFolderFragment();
Bundle args = new Bundle();
args.putString(FOLDER_NAME, folderName);
args.putString(FOLDER_PARENT, folderParent);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final String folderName = getArguments().getString(FOLDER_NAME);
final String folderParent = getArguments().getString(FOLDER_PARENT);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(getResources().getString(R.string.delete_folder_message, folderName));
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String inFolder = "";
if (!TextUtils.isEmpty(folderParent) && !folderParent.equals(AppConstants.ROOT_FOLDER)) {
inFolder = folderParent;
}
FeedUtils.deleteFolder(folderName, inFolder, getActivity(), new APIManager(getActivity()));
DeleteFolderFragment.this.dismiss();
}
});
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
DeleteFolderFragment.this.dismiss();
}
});
return builder.create();
}
}

View file

@ -12,13 +12,10 @@ import android.support.v4.app.DialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.databinding.DialogTrainfeedBinding;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Feed;
import com.newsblur.util.FeedSet;
@ -30,14 +27,7 @@ public class FeedIntelTrainerFragment extends DialogFragment {
private Feed feed;
private FeedSet fs;
private Classifier classifier;
@Bind(R.id.intel_title_header) TextView headerTitles;
@Bind(R.id.intel_tag_header) TextView headerTags;
@Bind(R.id.intel_author_header) TextView headerAuthor;
@Bind(R.id.existing_title_intel_container) LinearLayout titleRowsContainer;
@Bind(R.id.existing_tag_intel_container) LinearLayout tagRowsContainer;
@Bind(R.id.existing_author_intel_container) LinearLayout authorRowsContainer;
@Bind(R.id.existing_feed_intel_container) LinearLayout feedRowsContainer;
private DialogTrainfeedBinding binding;
public static FeedIntelTrainerFragment newInstance(Feed feed, FeedSet fs) {
FeedIntelTrainerFragment fragment = new FeedIntelTrainerFragment();
@ -58,7 +48,7 @@ public class FeedIntelTrainerFragment extends DialogFragment {
final Activity activity = getActivity();
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_trainfeed, null);
ButterKnife.bind(this, v);
binding = DialogTrainfeedBinding.bind(v);
// display known title classifiers
for (Map.Entry<String, Integer> rule : classifier.title.entrySet()) {
@ -66,9 +56,9 @@ public class FeedIntelTrainerFragment extends DialogFragment {
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
label.setText(rule.getKey());
UIUtils.setupIntelDialogRow(row, classifier.title, rule.getKey());
titleRowsContainer.addView(row);
binding.existingTitleIntelContainer.addView(row);
}
if (classifier.title.size() < 1) headerTitles.setVisibility(View.GONE);
if (classifier.title.size() < 1) binding.intelTitleHeader.setVisibility(View.GONE);
// get the list of suggested tags
List<String> allTags = FeedUtils.dbHelper.getTagsForFeed(feed.feedId);
@ -83,9 +73,9 @@ public class FeedIntelTrainerFragment extends DialogFragment {
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
label.setText(tag);
UIUtils.setupIntelDialogRow(row, classifier.tags, tag);
tagRowsContainer.addView(row);
binding.existingTagIntelContainer.addView(row);
}
if (allTags.size() < 1) headerTags.setVisibility(View.GONE);
if (allTags.size() < 1) binding.intelTagHeader.setVisibility(View.GONE);
// get the list of suggested authors
List<String> allAuthors = FeedUtils.dbHelper.getAuthorsForFeed(feed.feedId);
@ -100,16 +90,16 @@ public class FeedIntelTrainerFragment extends DialogFragment {
TextView labelAuthor = (TextView) rowAuthor.findViewById(R.id.intel_row_label);
labelAuthor.setText(author);
UIUtils.setupIntelDialogRow(rowAuthor, classifier.authors, author);
authorRowsContainer.addView(rowAuthor);
binding.existingAuthorIntelContainer.addView(rowAuthor);
}
if (allAuthors.size() < 1) headerAuthor.setVisibility(View.GONE);
if (allAuthors.size() < 1) binding.intelAuthorHeader.setVisibility(View.GONE);
// for feel-level intel, the label is the title and the intel identifier is the feed ID
View rowFeed = inflater.inflate(R.layout.include_intel_row, null);
TextView labelFeed = (TextView) rowFeed.findViewById(R.id.intel_row_label);
labelFeed.setText(feed.title);
UIUtils.setupIntelDialogRow(rowFeed, classifier.feeds, feed.feedId);
feedRowsContainer.addView(rowFeed);
binding.existingFeedIntelContainer.addView(rowFeed);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.feed_intel_dialog_title);

View file

@ -26,9 +26,6 @@ import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.ExpandableListView.OnGroupCollapseListener;
import android.widget.ExpandableListView.OnGroupExpandListener;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.activity.AllSharedStoriesItemsList;
import com.newsblur.activity.AllStoriesItemsList;
@ -43,7 +40,10 @@ import com.newsblur.activity.ReadStoriesItemsList;
import com.newsblur.activity.SavedStoriesItemsList;
import com.newsblur.activity.SocialFeedItemsList;
import com.newsblur.database.FolderListAdapter;
import com.newsblur.databinding.FragmentFolderfeedlistBinding;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.SocialFeed;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedSet;
@ -64,11 +64,12 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
private static final int FOLDERS_LOADER = 2;
private static final int FEEDS_LOADER = 3;
private static final int SAVEDCOUNT_LOADER = 4;
private static final int SAVED_SEARCH_LOADER = 5;
private FolderListAdapter adapter;
public StateFilter currentState = StateFilter.SOME;
private SharedPreferences sharedPreferences;
@Bind(R.id.folderfeed_list) ExpandableListView list;
private FragmentFolderfeedlistBinding binding;
public boolean firstCursorSeenYet = false;
// the two-step context menu for feeds requires us to temp store the feed long-pressed so
@ -112,6 +113,8 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
return FeedUtils.dbHelper.getFeedsLoader();
case SAVEDCOUNT_LOADER:
return FeedUtils.dbHelper.getSavedStoryCountsLoader();
case SAVED_SEARCH_LOADER:
return FeedUtils.dbHelper.getSavedSearchLoader();
default:
throw new IllegalArgumentException("unknown loader created");
}
@ -140,6 +143,9 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
case SAVEDCOUNT_LOADER:
adapter.setStarredCountCursor(cursor);
break;
case SAVED_SEARCH_LOADER:
adapter.setSavedSearchesCursor(cursor);
break;
default:
throw new IllegalArgumentException("unknown loader created");
}
@ -163,6 +169,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
getLoaderManager().restartLoader(FOLDERS_LOADER, null, this);
getLoaderManager().restartLoader(FEEDS_LOADER, null, this);
getLoaderManager().restartLoader(SAVEDCOUNT_LOADER, null, this);
getLoaderManager().restartLoader(SAVED_SEARCH_LOADER, null, this);
} catch (Exception e) {
// on heavily loaded devices, the time between isAdded() going false
// and the loader subsystem shutting down can be nontrivial, causing
@ -179,6 +186,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
getLoaderManager().initLoader(FOLDERS_LOADER, null, this);
getLoaderManager().initLoader(FEEDS_LOADER, null, this);
getLoaderManager().initLoader(SAVEDCOUNT_LOADER, null, this);
getLoaderManager().initLoader(SAVED_SEARCH_LOADER, null, this);
}
}
}
@ -190,20 +198,20 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_folderfeedlist, container);
ButterKnife.bind(this, v);
binding = FragmentFolderfeedlistBinding.bind(v);
list.setGroupIndicator(UIUtils.getDrawable(getActivity(), R.drawable.transparent));
list.setOnCreateContextMenuListener(this);
list.setOnChildClickListener(this);
list.setOnGroupClickListener(this);
list.setOnGroupCollapseListener(this);
list.setOnGroupExpandListener(this);
binding.folderfeedList.setGroupIndicator(UIUtils.getDrawable(getActivity(), R.drawable.transparent));
binding.folderfeedList.setOnCreateContextMenuListener(this);
binding.folderfeedList.setOnChildClickListener(this);
binding.folderfeedList.setOnGroupClickListener(this);
binding.folderfeedList.setOnGroupCollapseListener(this);
binding.folderfeedList.setOnGroupExpandListener(this);
adapter.listBackref = new WeakReference(list); // see note in adapter about backref
list.setAdapter(adapter);
adapter.listBackref = new WeakReference(binding.folderfeedList); // see note in adapter about backref
binding.folderfeedList.setAdapter(adapter);
// Main activity needs to listen for scrolls to prevent refresh from firing unnecessarily
list.setOnScrollListener((android.widget.AbsListView.OnScrollListener) getActivity());
binding.folderfeedList.setOnScrollListener((android.widget.AbsListView.OnScrollListener) getActivity());
return v;
}
@ -215,18 +223,18 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
*/
public void checkOpenFolderPreferences() {
// make sure we didn't beat construction
if ((this.list == null) || (this.sharedPreferences == null)) return;
if ((this.binding.folderfeedList == null) || (this.sharedPreferences == null)) return;
for (int i = 0; i < adapter.getGroupCount(); i++) {
String flatGroupName = adapter.getGroupUniqueName(i);
if (sharedPreferences.getBoolean(AppConstants.FOLDER_PRE + "_" + flatGroupName, true)) {
if (list.isGroupExpanded(i) == false) {
list.expandGroup(i);
if (binding.folderfeedList.isGroupExpanded(i) == false) {
binding.folderfeedList.expandGroup(i);
adapter.setFolderClosed(flatGroupName, false);
}
} else {
if (list.isGroupExpanded(i) == true) {
list.collapseGroup(i);
if (binding.folderfeedList.isGroupExpanded(i) == true) {
binding.folderfeedList.collapseGroup(i);
adapter.setFolderClosed(flatGroupName, true);
}
}
@ -249,11 +257,14 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
if (adapter.isRowGlobalSharedStories(groupPosition)) break;
if (adapter.isRowAllSharedStories(groupPosition)) break;
if (adapter.isRowInfrequentStories(groupPosition)) break;
if (adapter.isRowSavedSearches(groupPosition)) break;
inflater.inflate(R.menu.context_folder, menu);
if (adapter.isRowAllStories(groupPosition)) {
menu.removeItem(R.id.menu_mute_folder);
menu.removeItem(R.id.menu_unmute_folder);
menu.removeItem(R.id.menu_delete_folder);
menu.removeItem(R.id.menu_rename_folder);
}
break;
@ -272,9 +283,22 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
menu.removeItem(R.id.menu_instafetch_feed);
menu.removeItem(R.id.menu_intel);
menu.removeItem(R.id.menu_rename_feed);
menu.removeItem(R.id.menu_delete_saved_search);
} else if (adapter.isRowSavedSearches(groupPosition)) {
menu.removeItem(R.id.menu_mark_feed_as_read);
menu.removeItem(R.id.menu_delete_feed);
menu.removeItem(R.id.menu_unfollow);
menu.removeItem(R.id.menu_choose_folders);
menu.removeItem(R.id.menu_rename_feed);
menu.removeItem(R.id.menu_notifications);
menu.removeItem(R.id.menu_mute_feed);
menu.removeItem(R.id.menu_unmute_feed);
menu.removeItem(R.id.menu_instafetch_feed);
menu.removeItem(R.id.menu_intel);
} else {
// normal feeds
menu.removeItem(R.id.menu_unfollow);
menu.removeItem(R.id.menu_delete_saved_search);
Feed feed = adapter.getFeed(groupPosition, childPosition);
if (feed.active) {
@ -359,7 +383,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
} else if (item.getItemId() == R.id.menu_rename_feed) {
Feed feed = adapter.getFeed(groupPosition, childPosition);
if (feed != null) {
DialogFragment renameFeedFragment = RenameFeedFragment.newInstance(feed);
DialogFragment renameFeedFragment = RenameDialogFragment.newInstance(feed);
renameFeedFragment.show(getFragmentManager(), "dialog");
}
} else if (item.getItemId() == R.id.menu_mute_feed) {
@ -379,6 +403,22 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
} else if (item.getItemId() == R.id.menu_intel) {
FeedIntelTrainerFragment intelFrag = FeedIntelTrainerFragment.newInstance(adapter.getFeed(groupPosition, childPosition), adapter.getChild(groupPosition, childPosition));
intelFrag.show(getFragmentManager(), FeedIntelTrainerFragment.class.getName());
} else if (item.getItemId() == R.id.menu_delete_saved_search) {
SavedSearch savedSearch = adapter.getSavedSearch(childPosition);
if (savedSearch != null) {
DialogFragment deleteFeedFragment = DeleteFeedFragment.newInstance(savedSearch);
deleteFeedFragment.show(getFragmentManager(), "dialog");
}
} else if (item.getItemId() == R.id.menu_delete_folder) {
Folder folder = adapter.getGroupFolder(groupPosition);
String folderParentName = folder.getFirstParentName();
DeleteFolderFragment deleteFolderFragment = DeleteFolderFragment.newInstance(folder.name, folderParentName);
deleteFolderFragment.show(getFragmentManager(), deleteFolderFragment.getTag());
} else if (item.getItemId() == R.id.menu_rename_folder) {
Folder folder = adapter.getGroupFolder(groupPosition);
String folderParentName = folder.getFirstParentName();
RenameDialogFragment renameDialogFragment = RenameDialogFragment.newInstance(folder.name, folderParentName);
renameDialogFragment.show(getFragmentManager(), renameDialogFragment.getTag());
}
return super.onContextItemSelected(item);
@ -449,6 +489,9 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
i = new Intent(getActivity(), ReadStoriesItemsList.class);
} else if (adapter.isRowSavedStories(groupPosition)) {
i = new Intent(getActivity(), SavedStoriesItemsList.class);
} else if (adapter.isRowSavedSearches(groupPosition)) {
// group not clickable
return true;
} else {
i = new Intent(getActivity(), FolderItemsList.class);
String canonicalFolderName = adapter.getGroupFolderName(groupPosition);
@ -472,6 +515,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
// these shouldn't ever be collapsible
if (adapter.isRowRootFolder(groupPosition)) return;
if (adapter.isRowReadStories(groupPosition)) return;
if (adapter.isRowSavedSearches(groupPosition)) return;
String flatGroupName = adapter.getGroupUniqueName(groupPosition);
// save the expanded preference, since the widget likes to forget it
@ -490,6 +534,7 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
// these shouldn't ever be collapsible
if (adapter.isRowRootFolder(groupPosition)) return;
if (adapter.isRowReadStories(groupPosition)) return;
if (adapter.isRowSavedSearches(groupPosition)) return;
String flatGroupName = adapter.getGroupUniqueName(groupPosition);
// save the collapsed preference, since the widget likes to forget it
@ -515,7 +560,9 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
Intent intent = new Intent(getActivity(), SavedStoriesItemsList.class);
intent.putExtra(ItemsList.EXTRA_FEED_SET, fs);
getActivity().startActivity(intent);
} else {
} else if (adapter.isRowSavedSearches(groupPosition)) {
openSavedSearch(adapter.getSavedSearch(childPosition));
} else {
Feed feed = adapter.getFeed(groupPosition, childPosition);
// NB: FeedItemsList needs the name of the containing folder, but this is not the same as setting
// a folder name on the FeedSet and making it into a folder-type set. it is just a single feed,
@ -534,6 +581,53 @@ public class FolderListFragment extends NbFragment implements OnCreateContextMen
return true;
}
private void openSavedSearch(SavedSearch savedSearch) {
Intent intent = null;
FeedSet fs = null;
String feedId = savedSearch.feedId;
if (feedId.equals("river:")) {
// all site stories
intent = new Intent(getActivity(), AllStoriesItemsList.class);
fs = FeedSet.allFeeds();
} else if (feedId.equals("river:infrequent")) {
// infrequent stories
intent = new Intent(getActivity(), InfrequentItemsList.class);
fs = FeedSet.infrequentFeeds();
} else if (feedId.startsWith("river:")) {
intent = new Intent(getActivity(), FolderItemsList.class);
String folderName = feedId.replace("river:", "");
fs = FeedUtils.feedSetFromFolderName(folderName);
intent.putExtra(FolderItemsList.EXTRA_FOLDER_NAME, folderName);
} else if (feedId.equals("read")) {
intent = new Intent(getActivity(), ReadStoriesItemsList.class);
fs = FeedSet.allRead();
} else if (feedId.equals("starred")) {
intent = new Intent(getActivity(), SavedStoriesItemsList.class);
fs = FeedSet.allSaved();
} else if (feedId.startsWith("starred:")) {
intent = new Intent(getActivity(), SavedStoriesItemsList.class);
fs = FeedSet.singleSavedTag(feedId.replace("starred:", ""));
} else if (feedId.startsWith("feed:")) {
intent = new Intent(getActivity(), FeedItemsList.class);
String cleanFeedId = feedId.replace("feed:", "");
Feed feed = FeedUtils.getFeed(cleanFeedId);
fs = FeedSet.singleFeed(cleanFeedId);
intent.putExtra(FeedItemsList.EXTRA_FEED, feed);
} else if (feedId.startsWith("social:")) {
intent = new Intent(getActivity(), SocialFeedItemsList.class);
String cleanFeedId = feedId.replace("social:", "");
fs = FeedSet.singleFeed(cleanFeedId);
Feed feed = FeedUtils.getFeed(cleanFeedId);
intent.putExtra(FeedItemsList.EXTRA_FEED, feed);
}
if (intent != null) {
fs.setSearchQuery(savedSearch.query);
intent.putExtra(ItemsList.EXTRA_FEED_SET, fs);
startActivity(intent);
}
}
public void setTextSize(Float size) {
if (adapter != null) {
adapter.setTextSize(size);

View file

@ -1,28 +1,22 @@
package com.newsblur.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.databinding.InfrequentCutoffDialogBinding;
public class InfrequentCutoffDialogFragment extends DialogFragment {
private static String CURRENT_CUTOFF = "currentCutoff";
private int currentValue;
@Bind(R.id.radio_5) RadioButton button5;
@Bind(R.id.radio_15) RadioButton button15;
@Bind(R.id.radio_30) RadioButton button30;
@Bind(R.id.radio_60) RadioButton button60;
@Bind(R.id.radio_90) RadioButton button90;
private InfrequentCutoffDialogBinding binding;
public static InfrequentCutoffDialogFragment newInstance(int currentValue) {
InfrequentCutoffDialogFragment dialog = new InfrequentCutoffDialogFragment();
@ -42,13 +36,13 @@ public class InfrequentCutoffDialogFragment extends DialogFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
currentValue = getArguments().getInt(CURRENT_CUTOFF);
View v = inflater.inflate(R.layout.infrequent_cutoff_dialog, null);
ButterKnife.bind(this, v);
binding = InfrequentCutoffDialogBinding.bind(v);
button5.setChecked(currentValue == 5);
button15.setChecked(currentValue == 15);
button30.setChecked(currentValue == 30);
button60.setChecked(currentValue == 60);
button90.setChecked(currentValue == 90);
binding.radio5.setChecked(currentValue == 5);
binding.radio15.setChecked(currentValue == 15);
binding.radio30.setChecked(currentValue == 30);
binding.radio60.setChecked(currentValue == 60);
binding.radio90.setChecked(currentValue == 90);
getDialog().setTitle(R.string.infrequent_choice_title);
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
@ -56,31 +50,66 @@ public class InfrequentCutoffDialogFragment extends DialogFragment {
return v;
}
@OnClick(R.id.radio_5) void select5() {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.radio5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select5();
}
});
binding.radio15.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select15();
}
});
binding.radio30.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select30();
}
});
binding.radio60.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select60();
}
});
binding.radio90.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select90();
}
});
}
private void select5() {
if (currentValue != 5) {
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(5);
}
dismiss();
}
@OnClick(R.id.radio_15) void select15() {
private void select15() {
if (currentValue != 15) {
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(15);
}
dismiss();
}
@OnClick(R.id.radio_30) void select30() {
private void select30() {
if (currentValue != 30) {
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(30);
}
dismiss();
}
@OnClick(R.id.radio_60) void select60() {
private void select60() {
if (currentValue != 60) {
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(60);
}
dismiss();
}
@OnClick(R.id.radio_90) void select90() {
private void select90() {
if (currentValue != 90) {
((InfrequentCutoffChangedListener) getActivity()).infrequentCutoffChanged(90);
}

View file

@ -15,17 +15,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.activity.ItemsList;
import com.newsblur.activity.NbActivity;
import com.newsblur.database.StoryViewAdapter;
import com.newsblur.databinding.FragmentItemgridBinding;
import com.newsblur.domain.Story;
import com.newsblur.service.NBSyncService;
import com.newsblur.util.FeedSet;
@ -50,21 +46,17 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
private final static int GRID_SPACING_DP = 5;
private int gridSpacingPx;
@Bind(R.id.itemgridfragment_grid) RecyclerView itemGrid;
private GridLayoutManager layoutManager;
private StoryViewAdapter adapter;
// an optional pending scroll state to restore.
private Parcelable gridState;
// loading indicator for when stories are absent or stale (at top of list)
@Bind(R.id.top_loading_throb) ProgressThrobber topProgressView;
// R.id.top_loading_throb
// loading indicator for when stories are present and fresh (at bottom of list)
protected ProgressThrobber bottomProgressView;
@Bind(R.id.empty_view) View emptyView;
@Bind(R.id.empty_view_text) TextView emptyViewText;
@Bind(R.id.empty_view_image) ImageView emptyViewImage;
private View fleuronFooter;
// the fleuron has padding that can't be calculated until after layout, but only changes
// rarely thereafter
@ -76,6 +68,8 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
public int indexOfLastUnread = -1;
public boolean fullFlingComplete = false;
private FragmentItemgridBinding binding;
public static ItemSetFragment newInstance() {
ItemSetFragment fragment = new ItemSetFragment();
Bundle arguments = new Bundle();
@ -123,13 +117,13 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_itemgrid, null);
ButterKnife.bind(this, v);
binding = FragmentItemgridBinding.bind(v);
// disable the throbbers if animations are going to have a zero time scale
boolean isDisableAnimations = ViewUtils.isPowerSaveMode(getActivity());
topProgressView.setEnabled(!isDisableAnimations);
topProgressView.setColors(UIUtils.getColor(getActivity(), R.color.refresh_1),
binding.topLoadingThrob.setEnabled(!isDisableAnimations);
binding.topLoadingThrob.setColors(UIUtils.getColor(getActivity(), R.color.refresh_1),
UIUtils.getColor(getActivity(), R.color.refresh_2),
UIUtils.getColor(getActivity(), R.color.refresh_3),
UIUtils.getColor(getActivity(), R.color.refresh_4));
@ -145,11 +139,11 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
fleuronFooter = inflater.inflate(R.layout.row_fleuron, null);
fleuronFooter.setVisibility(View.INVISIBLE);
itemGrid.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
binding.itemgridfragmentGrid.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
itemGridWidthPx = itemGrid.getMeasuredWidth();
itemGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
itemGridWidthPx = binding.itemgridfragmentGrid.getMeasuredWidth();
binding.itemgridfragmentGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
updateStyle();
}
});
@ -158,11 +152,11 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
calcColumnCount(listStyle);
layoutManager = new GridLayoutManager(getActivity(), columnCount);
itemGrid.setLayoutManager(layoutManager);
binding.itemgridfragmentGrid.setLayoutManager(layoutManager);
setupAnimSpeeds();
calcGridSpacing(listStyle);
itemGrid.addItemDecoration(new RecyclerView.ItemDecoration() {
binding.itemgridfragmentGrid.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(gridSpacingPx, gridSpacingPx, gridSpacingPx, gridSpacingPx);
@ -172,7 +166,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
adapter = new StoryViewAdapter(((NbActivity) getActivity()), this, getFeedSet(), listStyle);
adapter.addFooterView(footerView);
adapter.addFooterView(fleuronFooter);
itemGrid.setAdapter(adapter);
binding.itemgridfragmentGrid.setAdapter(adapter);
// the layout manager needs to know that the footer rows span all the way across
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@ -187,14 +181,14 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
}
});
itemGrid.addOnScrollListener(new RecyclerView.OnScrollListener() {
binding.itemgridfragmentGrid.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
ItemSetFragment.this.onScrolled(recyclerView, dx, dy);
}
});
setupGestureDetector(itemGrid);
setupGestureDetector(binding.itemgridfragmentGrid);
return v;
}
@ -281,13 +275,13 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
}
protected void updateAdapter(Cursor cursor) {
adapter.swapCursor(cursor, itemGrid, gridState);
adapter.swapCursor(cursor, binding.itemgridfragmentGrid, gridState);
gridState = null;
adapter.updateFeedSet(getFeedSet());
if ((cursor != null) && (cursor.getCount() > 0)) {
emptyView.setVisibility(View.INVISIBLE);
binding.emptyView.setVisibility(View.INVISIBLE);
} else {
emptyView.setVisibility(View.VISIBLE);
binding.emptyView.setVisibility(View.VISIBLE);
}
// though we have stories, we might not yet have as many as we want
@ -305,38 +299,38 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
calcFleuronPadding();
if (getFeedSet().isMuted()) {
emptyViewText.setText(R.string.empty_list_view_muted_feed);
emptyViewText.setTypeface(null, Typeface.NORMAL);
emptyViewImage.setVisibility(View.VISIBLE);
topProgressView.setVisibility(View.INVISIBLE);
binding.emptyViewText.setText(R.string.empty_list_view_muted_feed);
binding.emptyViewText.setTypeface(null, Typeface.NORMAL);
binding.emptyViewImage.setVisibility(View.VISIBLE);
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
bottomProgressView.setVisibility(View.INVISIBLE);
return;
}
if ( (!cursorSeenYet) || NBSyncService.isFeedSetSyncing(getFeedSet(), getActivity()) ) {
emptyViewText.setText(R.string.empty_list_view_loading);
emptyViewText.setTypeface(null, Typeface.ITALIC);
emptyViewImage.setVisibility(View.INVISIBLE);
binding.emptyViewText.setText(R.string.empty_list_view_loading);
binding.emptyViewText.setTypeface(null, Typeface.ITALIC);
binding.emptyViewImage.setVisibility(View.INVISIBLE);
if (NBSyncService.isFeedSetStoriesFresh(getFeedSet())) {
topProgressView.setVisibility(View.INVISIBLE);
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
bottomProgressView.setVisibility(View.VISIBLE);
} else {
topProgressView.setVisibility(View.VISIBLE);
binding.topLoadingThrob.setVisibility(View.VISIBLE);
bottomProgressView.setVisibility(View.GONE);
}
fleuronFooter.setVisibility(View.INVISIBLE);
} else {
ReadFilter readFilter = PrefsUtils.getReadFilter(getActivity(), getFeedSet());
if (readFilter == ReadFilter.UNREAD) {
emptyViewText.setText(R.string.empty_list_view_no_stories_unread);
binding.emptyViewText.setText(R.string.empty_list_view_no_stories_unread);
} else {
emptyViewText.setText(R.string.empty_list_view_no_stories);
binding.emptyViewText.setText(R.string.empty_list_view_no_stories);
}
emptyViewText.setTypeface(null, Typeface.NORMAL);
emptyViewImage.setVisibility(View.VISIBLE);
binding.emptyViewText.setTypeface(null, Typeface.NORMAL);
binding.emptyViewImage.setVisibility(View.VISIBLE);
topProgressView.setVisibility(View.INVISIBLE);
binding.topLoadingThrob.setVisibility(View.INVISIBLE);
bottomProgressView.setVisibility(View.INVISIBLE);
if (cursorSeenYet && NBSyncService.isFeedSetExhausted(getFeedSet()) && (adapter.getRawStoryCount() > 0)) {
fleuronFooter.setVisibility(View.VISIBLE);
@ -411,7 +405,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
targetMovDuration = 0L;
}
RecyclerView.ItemAnimator anim = itemGrid.getItemAnimator();
RecyclerView.ItemAnimator anim = binding.itemgridfragmentGrid.getItemAnimator();
anim.setAddDuration((long) ((anim.getAddDuration() + targetAddDuration)/2L));
anim.setMoveDuration((long) ((anim.getMoveDuration() + targetMovDuration)/2L));
}
@ -429,7 +423,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
// past the last item, which can be confusing to users who don't know about or need the offset
if ( (!fullFlingComplete) &&
(layoutManager.findLastCompletelyVisibleItemPosition() >= adapter.getStoryCount()) ) {
itemGrid.stopScroll();
binding.itemgridfragmentGrid.stopScroll();
// but after halting at the end once, do allow scrolling past the bottom
fullFlingComplete = true;
}
@ -439,7 +433,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
(layoutManager.findLastCompletelyVisibleItemPosition() >= indexOfLastUnread) ) {
// but don't interrupt if already past the last unread
if (indexOfLastUnread >= layoutManager.findFirstCompletelyVisibleItemPosition()) {
itemGrid.stopScroll();
binding.itemgridfragmentGrid.stopScroll();
}
indexOfLastUnread = -1;
}
@ -507,7 +501,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
*/
private void calcFleuronPadding() {
if (fleuronResized) return;
int listHeight = itemGrid.getMeasuredHeight();
int listHeight = binding.itemgridfragmentGrid.getMeasuredHeight();
View innerView = fleuronFooter.findViewById(R.id.fleuron);
ViewGroup.LayoutParams oldLayout = innerView.getLayoutParams();
ViewGroup.MarginLayoutParams newLayout = new LinearLayout.LayoutParams(oldLayout);
@ -526,7 +520,7 @@ public class ItemSetFragment extends NbFragment implements LoaderManager.LoaderC
@Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BUNDLE_GRIDSTATE, itemGrid.getLayoutManager().onSaveInstanceState());
outState.putParcelable(BUNDLE_GRIDSTATE, binding.itemgridfragmentGrid.getLayoutManager().onSaveInstanceState());
}
}

View file

@ -11,17 +11,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.activity.Login;
import com.newsblur.activity.Main;
import com.newsblur.databinding.FragmentLoginprogressBinding;
import com.newsblur.network.APIManager;
import com.newsblur.network.domain.LoginResponse;
import com.newsblur.util.PrefsUtils;
@ -30,14 +25,10 @@ import com.newsblur.util.UIUtils;
public class LoginProgressFragment extends Fragment {
private APIManager apiManager;
@Bind(R.id.login_logging_in) TextView updateStatus;
@Bind(R.id.login_retrieving_feeds) TextView retrievingFeeds;
@Bind(R.id.login_profile_picture) ImageView loginProfilePicture;
@Bind(R.id.login_feed_progress) ProgressBar feedProgress;
@Bind(R.id.login_logging_in_progress) ProgressBar loggingInProgress;
private LoginTask loginTask;
private String username;
private String password;
private FragmentLoginprogressBinding binding;
public static LoginProgressFragment getInstance(String username, String password) {
LoginProgressFragment fragment = new LoginProgressFragment();
@ -61,7 +52,7 @@ public class LoginProgressFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_loginprogress, null);
ButterKnife.bind(this, v);
binding = FragmentLoginprogressBinding.bind(v);
loginTask = new LoginTask();
loginTask.execute();
@ -73,7 +64,7 @@ public class LoginProgressFragment extends Fragment {
@Override
protected void onPreExecute() {
Animation a = AnimationUtils.loadAnimation(getActivity(), R.anim.text_up);
updateStatus.startAnimation(a);
binding.loginLoggingIn.startAnimation(a);
}
@Override
@ -90,20 +81,20 @@ public class LoginProgressFragment extends Fragment {
if (c == null) return; // we might have run past the lifecycle of the activity
if (!result.isError()) {
final Animation a = AnimationUtils.loadAnimation(c, R.anim.text_down);
updateStatus.setText(R.string.login_logged_in);
loggingInProgress.setVisibility(View.GONE);
updateStatus.startAnimation(a);
binding.loginLoggingIn.setText(R.string.login_logged_in);
binding.loginLoggingInProgress.setVisibility(View.GONE);
binding.loginLoggingIn.startAnimation(a);
Bitmap userImage = PrefsUtils.getUserImage(c);
if (userImage != null ) {
loginProfilePicture.setVisibility(View.VISIBLE);
loginProfilePicture.setImageBitmap(UIUtils.clipAndRound(userImage, 10f, false));
binding.loginProfilePicture.setVisibility(View.VISIBLE);
binding.loginProfilePicture.setImageBitmap(UIUtils.clipAndRound(userImage, 10f, false));
}
feedProgress.setVisibility(View.VISIBLE);
binding.loginFeedProgress.setVisibility(View.VISIBLE);
final Animation b = AnimationUtils.loadAnimation(c, R.anim.text_up);
retrievingFeeds.setText(R.string.login_retrieving_feeds);
retrievingFeeds.startAnimation(b);
binding.loginRetrievingFeeds.setText(R.string.login_retrieving_feeds);
binding.loginFeedProgress.startAnimation(b);
Intent startMain = new Intent(getActivity(), Main.class);
c.startActivity(startMain);

View file

@ -3,6 +3,8 @@ package com.newsblur.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.KeyEvent;
@ -10,39 +12,27 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.ViewSwitcher;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.activity.LoginProgress;
import com.newsblur.activity.RegisterProgress;
import com.newsblur.databinding.FragmentLoginregisterBinding;
import com.newsblur.network.APIConstants;
import com.newsblur.util.AppConstants;
import com.newsblur.util.PrefsUtils;
public class LoginRegisterFragment extends Fragment {
@Bind(R.id.login_username) EditText username;
@Bind(R.id.login_password) EditText password;
@Bind(R.id.registration_username) EditText register_username;
@Bind(R.id.registration_password) EditText register_password;
@Bind(R.id.registration_email) EditText register_email;
@Bind(R.id.login_viewswitcher) ViewSwitcher viewSwitcher;
@Bind(R.id.login_custom_server) View customServer;
@Bind(R.id.login_custom_server_value) EditText customServerValue;
private FragmentLoginregisterBinding binding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_loginregister, container, false);
ButterKnife.bind(this, v);
binding = FragmentLoginregisterBinding.bind(v);
password.setOnEditorActionListener(new OnEditorActionListener() {
binding.loginPassword.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE ) {
@ -52,7 +42,7 @@ public class LoginRegisterFragment extends Fragment {
}
});
register_email.setOnEditorActionListener(new OnEditorActionListener() {
binding.registrationEmail.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE ) {
@ -65,36 +55,77 @@ public class LoginRegisterFragment extends Fragment {
return v;
}
@OnClick(R.id.login_button) void logIn() {
if (!TextUtils.isEmpty(username.getText().toString())) {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
logIn();
}
});
binding.registrationButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signUp();
}
});
binding.loginChangeToLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showLogin();
}
});
binding.loginChangeToRegister.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showRegister();
}
});
binding.loginForgotPassword.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
launchForgotPasswordPage();
}
});
binding.loginCustomServer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showCustomServer();
}
});
}
private void logIn() {
if (!TextUtils.isEmpty(binding.loginUsername.getText().toString())) {
// set the custom server endpoint before any API access, even the cookie fetch.
APIConstants.setCustomServer(customServerValue.getText().toString());
PrefsUtils.saveCustomServer(getActivity(), customServerValue.getText().toString());
APIConstants.setCustomServer(binding.loginCustomServerValue.getText().toString());
PrefsUtils.saveCustomServer(getActivity(), binding.loginCustomServerValue.getText().toString());
Intent i = new Intent(getActivity(), LoginProgress.class);
i.putExtra("username", username.getText().toString());
i.putExtra("password", password.getText().toString());
i.putExtra("username", binding.loginUsername.getText().toString());
i.putExtra("password", binding.loginUsername.getText().toString());
startActivity(i);
}
}
@OnClick(R.id.registration_button) void signUp() {
private void signUp() {
Intent i = new Intent(getActivity(), RegisterProgress.class);
i.putExtra("username", register_username.getText().toString());
i.putExtra("password", register_password.getText().toString());
i.putExtra("email", register_email.getText().toString());
i.putExtra("username", binding.registrationUsername.getText().toString());
i.putExtra("password", binding.registrationPassword.getText().toString());
i.putExtra("email", binding.registrationEmail.getText().toString());
startActivity(i);
}
@OnClick(R.id.login_change_to_login) void showLogin() {
viewSwitcher.showPrevious();
private void showLogin() {
binding.loginViewswitcher.showPrevious();
}
@OnClick(R.id.login_change_to_register) void showRegister() {
viewSwitcher.showNext();
private void showRegister() {
binding.loginViewswitcher.showNext();
}
@OnClick(R.id.login_forgot_password) void launchForgotPasswordPage() {
private void launchForgotPasswordPage() {
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(AppConstants.FORGOT_PASWORD_URL));
@ -104,9 +135,8 @@ public class LoginRegisterFragment extends Fragment {
}
}
@OnClick(R.id.login_custom_server) void showCustomServer() {
customServer.setVisibility(View.GONE);
customServerValue.setVisibility(View.VISIBLE);
private void showCustomServer() {
binding.loginCustomServer.setVisibility(View.GONE);
binding.loginCustomServerValue.setVisibility(View.VISIBLE);
}
}
}

View file

@ -1,19 +1,17 @@
package com.newsblur.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RadioButton;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.databinding.ReadfilterDialogBinding;
import com.newsblur.util.ReadFilter;
import com.newsblur.util.ReadFilterChangedListener;
@ -21,8 +19,7 @@ public class ReadFilterDialogFragment extends DialogFragment {
private static String CURRENT_FILTER = "currentFilter";
private ReadFilter currentValue;
@Bind(R.id.radio_all) RadioButton allButton;
@Bind(R.id.radio_unread) RadioButton unreadButton;
private ReadfilterDialogBinding binding;
public static ReadFilterDialogFragment newInstance(ReadFilter currentValue) {
ReadFilterDialogFragment dialog = new ReadFilterDialogFragment();
@ -42,10 +39,10 @@ public class ReadFilterDialogFragment extends DialogFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
currentValue = (ReadFilter) getArguments().getSerializable(CURRENT_FILTER);
View v = inflater.inflate(R.layout.readfilter_dialog, null);
ButterKnife.bind(this, v);
binding = ReadfilterDialogBinding.bind(v);
allButton.setChecked(currentValue == ReadFilter.ALL);
unreadButton.setChecked(currentValue == ReadFilter.UNREAD);
binding.radioAll.setChecked(currentValue == ReadFilter.ALL);
binding.radioUnread.setChecked(currentValue == ReadFilter.UNREAD);
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
@ -53,14 +50,31 @@ public class ReadFilterDialogFragment extends DialogFragment {
return v;
}
@OnClick(R.id.radio_all) void selectAll() {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.radioAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectAll();
}
});
binding.radioUnread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectUnread();
}
});
}
private void selectAll() {
if (currentValue != ReadFilter.ALL) {
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.ALL);
}
dismiss();
}
@OnClick(R.id.radio_unread) void selectUnread() {
private void selectUnread() {
if (currentValue != ReadFilter.UNREAD) {
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.UNREAD);
}

View file

@ -1,21 +1,19 @@
package com.newsblur.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RadioButton;
import com.newsblur.R;
import com.newsblur.databinding.ReadingfontDialogBinding;
import com.newsblur.util.ReadingFontChangedListener;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by mark on 02/05/2017.
*/
@ -26,14 +24,7 @@ public class ReadingFontDialogFragment extends DialogFragment {
private String currentValue;
@Bind(R.id.radio_anonymous) RadioButton anonymousButton;
@Bind(R.id.radio_chronicle) RadioButton chronicleButton;
@Bind(R.id.radio_default) RadioButton defaultButton;
@Bind(R.id.radio_gotham) RadioButton gothamButton;
@Bind(R.id.radio_noto_sans) RadioButton notoSansButton;
@Bind(R.id.radio_noto_serif) RadioButton notoSerifButton;
@Bind(R.id.radio_open_sans_condensed) RadioButton openSansCondensedButton;
@Bind(R.id.radio_whitney) RadioButton whitneyButton;
private ReadingfontDialogBinding binding;
public static ReadingFontDialogFragment newInstance(String selectedFont) {
ReadingFontDialogFragment dialog = new ReadingFontDialogFragment();
@ -52,16 +43,16 @@ public class ReadingFontDialogFragment extends DialogFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
currentValue = getArguments().getString(SELECTED_FONT);
View v = inflater.inflate(R.layout.readingfont_dialog, null);
ButterKnife.bind(this, v);
binding = ReadingfontDialogBinding.bind(v);
anonymousButton.setChecked(currentValue.equals(getString(R.string.anonymous_pro_font_prefvalue)));
chronicleButton.setChecked(currentValue.equals(getString(R.string.chronicle_font_prefvalue)));
defaultButton.setChecked(currentValue.equals(getString(R.string.default_font_prefvalue)));
gothamButton.setChecked(currentValue.equals(getString(R.string.gotham_narrow_font_prefvalue)));
notoSansButton.setChecked(currentValue.equals(getString(R.string.noto_sans_font_prefvalue)));
notoSerifButton.setChecked(currentValue.equals(getString(R.string.noto_serif_font_prefvalue)));
openSansCondensedButton.setChecked(currentValue.equals(getString(R.string.open_sans_condensed_font_prefvalue)));
whitneyButton.setChecked(currentValue.equals(getString(R.string.whitney_font_prefvalue)));
binding.radioAnonymous.setChecked(currentValue.equals(getString(R.string.anonymous_pro_font_prefvalue)));
binding.radioChronicle.setChecked(currentValue.equals(getString(R.string.chronicle_font_prefvalue)));
binding.radioDefault.setChecked(currentValue.equals(getString(R.string.default_font_prefvalue)));
binding.radioGotham.setChecked(currentValue.equals(getString(R.string.gotham_narrow_font_prefvalue)));
binding.radioNotoSans.setChecked(currentValue.equals(getString(R.string.noto_sans_font_prefvalue)));
binding.radioNotoSerif.setChecked(currentValue.equals(getString(R.string.noto_serif_font_prefvalue)));
binding.radioOpenSansCondensed.setChecked(currentValue.equals(getString(R.string.open_sans_condensed_font_prefvalue)));
binding.radioWhitney.setChecked(currentValue.equals(getString(R.string.whitney_font_prefvalue)));
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
@ -69,8 +60,57 @@ public class ReadingFontDialogFragment extends DialogFragment {
return v;
}
@OnClick(R.id.radio_anonymous) void selectAnonymousPro() {
switchFont(getString(R.string.anonymous_pro_font_prefvalue));
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.radioAnonymous.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.anonymous_pro_font_prefvalue));
}
});
binding.radioDefault.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.default_font_prefvalue));
}
});
binding.radioChronicle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.chronicle_font_prefvalue));
}
});
binding.radioGotham.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.gotham_narrow_font_prefvalue));
}
});
binding.radioNotoSans.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.noto_sans_font_prefvalue));
}
});
binding.radioNotoSerif.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.noto_serif_font_prefvalue));
}
});
binding.radioOpenSansCondensed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.open_sans_condensed_font_prefvalue));
}
});
binding.radioWhitney.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFont(getString(R.string.whitney_font_prefvalue));
}
});
}
private void switchFont(String newValue) {
@ -79,32 +119,4 @@ public class ReadingFontDialogFragment extends DialogFragment {
currentValue = newValue;
}
}
@OnClick(R.id.radio_chronicle) void selectChronicle() {
switchFont(getString(R.string.chronicle_font_prefvalue));
}
@OnClick(R.id.radio_default) void selectDefault() {
switchFont(getString(R.string.default_font_prefvalue));
}
@OnClick(R.id.radio_gotham) void selectGotham() {
switchFont(getString(R.string.gotham_narrow_font_prefvalue));
}
@OnClick(R.id.radio_noto_sans) void selectNotoSans() {
switchFont(getString(R.string.noto_sans_font_prefvalue));
}
@OnClick(R.id.radio_noto_serif) void selectNotoSerif() {
switchFont(getString(R.string.noto_serif_font_prefvalue));
}
@OnClick(R.id.radio_open_sans_condensed) void selectOpenSansCondensed() {
switchFont(getString(R.string.open_sans_condensed_font_prefvalue));
}
@OnClick(R.id.radio_whitney) void selectWhitney() {
switchFont(getString(R.string.whitney_font_prefvalue));
}
}
}

View file

@ -13,6 +13,8 @@ import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.text.TextUtils;
import android.util.Log;
@ -26,22 +28,17 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebView.HitTestResult;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.ScrollView;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.activity.FeedItemsList;
import com.newsblur.activity.NbActivity;
import com.newsblur.activity.Reading;
import com.newsblur.databinding.FragmentReadingitemBinding;
import com.newsblur.databinding.IncludeReadingItemCommentBinding;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Story;
import com.newsblur.domain.UserDetails;
import com.newsblur.service.OriginalTextService;
@ -54,14 +51,9 @@ import com.newsblur.util.PrefsUtils;
import com.newsblur.util.StoryUtils;
import com.newsblur.util.UIUtils;
import com.newsblur.util.ViewUtils;
import com.newsblur.view.FlowLayout;
import com.newsblur.view.NewsblurWebview;
import com.newsblur.view.ReadingScrollView;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -76,24 +68,12 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
private LayoutInflater inflater;
private String feedColor, feedTitle, feedFade, feedBorder, feedIconUrl, faviconText;
private Classifier classifier;
@Bind(R.id.reading_webview) NewsblurWebview web;
@Bind(R.id.custom_view_container) ViewGroup webviewCustomViewLayout;
@Bind(R.id.reading_scrollview) ScrollView fragmentScrollview;
private BroadcastReceiver textSizeReceiver, readingFontReceiver;
@Bind(R.id.reading_item_title) TextView itemTitle;
@Bind(R.id.reading_item_authors) TextView itemAuthors;
@Bind(R.id.reading_feed_title) TextView itemFeed;
private boolean displayFeedDetails;
@Bind(R.id.reading_item_tags) FlowLayout tagContainer;
private View view;
private UserDetails user;
private DefaultFeedView selectedFeedView;
private boolean textViewUnavailable;
@Bind(R.id.reading_textloading) TextView textViewLoadingMsg;
@Bind(R.id.reading_textmodefailed) TextView textViewLoadingFailedMsg;
@Bind(R.id.save_story_button) Button saveButton;
@Bind(R.id.share_story_button) Button shareButton;
@Bind(R.id.story_context_menu_button) Button menuButton;
/** The story HTML, as provided by the 'content' element of the stories API. */
private String storyContent;
@ -115,6 +95,9 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
private final Object WEBVIEW_CONTENT_MUTEX = new Object();
private FragmentReadingitemBinding binding;
private IncludeReadingItemCommentBinding itemCommentBinding;
public static ReadingItemFragment newInstance(Story story, String feedTitle, String feedFaviconColor, String feedFaviconFade, String feedFaviconBorder, String faviconText, String faviconUrl, Classifier classifier, boolean displayFeedDetails, String sourceUserId) {
ReadingItemFragment readingFragment = new ReadingItemFragment();
@ -168,8 +151,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
int heightm = fragmentScrollview.getChildAt(0).getMeasuredHeight();
int pos = fragmentScrollview.getScrollY();
int heightm = binding.readingScrollview.getChildAt(0).getMeasuredHeight();
int pos = binding.readingScrollview.getScrollY();
outState.putFloat(BUNDLE_SCROLL_POS_REL, (((float)pos)/heightm));
}
@ -177,7 +160,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
public void onDestroy() {
getActivity().unregisterReceiver(textSizeReceiver);
getActivity().unregisterReceiver(readingFontReceiver);
web.setOnTouchListener(null);
binding.readingWebview.setOnTouchListener(null);
view.setOnTouchListener(null);
getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(null);
super.onDestroy();
@ -187,7 +170,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
// state into the webview so it behaves.
@Override
public void onPause() {
if (this.web != null ) { this.web.onPause(); }
if (this.binding.readingWebview != null ) { this.binding.readingWebview.onPause(); }
super.onPause();
}
@ -195,25 +178,26 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
public void onResume() {
super.onResume();
reloadStoryContent();
if (this.web != null ) { this.web.onResume(); }
if (this.binding.readingWebview != null ) { this.binding.readingWebview.onResume(); }
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.inflater = inflater;
view = inflater.inflate(R.layout.fragment_readingitem, null);
ButterKnife.bind(this, view);
binding = FragmentReadingitemBinding.bind(view);
itemCommentBinding = IncludeReadingItemCommentBinding.bind(binding.getRoot());
Reading activity = (Reading) getActivity();
fs = activity.getFeedSet();
selectedFeedView = PrefsUtils.getDefaultViewModeForFeed(activity, story.feedId);
registerForContextMenu(web);
web.setCustomViewLayout(webviewCustomViewLayout);
web.setWebviewWrapperLayout(fragmentScrollview);
web.setBackgroundColor(Color.TRANSPARENT);
web.fragment = this;
web.activity = activity;
registerForContextMenu(binding.readingWebview);
binding.readingWebview.setCustomViewLayout(binding.customViewContainer);
binding.readingWebview.setWebviewWrapperLayout(binding.readingScrollview);
binding.readingWebview.setBackgroundColor(Color.TRANSPARENT);
binding.readingWebview.fragment = this;
binding.readingWebview.activity = activity;
setupItemMetadata();
updateShareButton();
@ -228,6 +212,29 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.storyContextMenuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onClickMenuButton();
}
});
itemCommentBinding.saveStoryButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
clickSave();
}
});
itemCommentBinding.shareStoryButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
clickShare();
}
});
}
private void setupImmersiveViewGestureDetector() {
// Change the system visibility on the decorview from the activity so that the state is maintained as we page through
// fragments
@ -239,7 +246,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
return gestureDetector.onTouchEvent(motionEvent);
}
};
web.setOnTouchListener(touchListener);
binding.readingWebview.setOnTouchListener(touchListener);
view.setOnTouchListener(touchListener);
getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(immersiveViewHandler);
@ -247,7 +254,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
HitTestResult result = web.getHitTestResult();
HitTestResult result = binding.readingWebview.getHitTestResult();
if (result.getType() == HitTestResult.IMAGE_TYPE ||
result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE ) {
// if the long-pressed item was an image, see if we can pop up a little dialogue
@ -298,8 +305,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
}
@OnClick(R.id.story_context_menu_button) void onClickMenuButton() {
PopupMenu pm = new PopupMenu(getActivity(), menuButton);
private void onClickMenuButton() {
PopupMenu pm = new PopupMenu(getActivity(), binding.storyContextMenuButton);
Menu menu = pm.getMenu();
pm.getMenuInflater().inflate(R.menu.story_context, menu);
@ -386,7 +393,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
}
@OnClick(R.id.save_story_button) void clickSave() {
private void clickSave() {
if (story.starred) {
FeedUtils.setStorySaved(story.storyHash, false, getActivity());
} else {
@ -395,24 +402,24 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
private void updateSaveButton() {
if (saveButton == null) return;
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
if (itemCommentBinding.saveStoryButton == null) return;
itemCommentBinding.saveStoryButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
}
@OnClick(R.id.share_story_button) void clickShare() {
private void clickShare() {
DialogFragment newFragment = ShareDialogFragment.newInstance(story, sourceUserId);
newFragment.show(getFragmentManager(), "dialog");
}
private void updateShareButton() {
if (shareButton == null) return;
if (itemCommentBinding.shareStoryButton == null) return;
for (String userId : story.sharedUserIds) {
if (TextUtils.equals(userId, user.id)) {
shareButton.setText(R.string.already_shared);
itemCommentBinding.shareStoryButton.setText(R.string.already_shared);
return;
}
}
shareButton.setText(R.string.share_this);
itemCommentBinding.shareStoryButton.setText(R.string.share_this);
}
private void setupItemCommentsAndShares() {
@ -443,28 +450,28 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
feedHeaderBorder.setBackgroundColor(Color.parseColor("#" + feedBorder));
if (TextUtils.equals(faviconText, "black")) {
itemFeed.setTextColor(UIUtils.getColor(getActivity(), R.color.text));
itemFeed.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_white));
binding.readingFeedTitle.setTextColor(UIUtils.getColor(getActivity(), R.color.text));
binding.readingFeedTitle.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_white));
} else {
itemFeed.setTextColor(UIUtils.getColor(getActivity(), R.color.white));
itemFeed.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_black));
binding.readingFeedTitle.setTextColor(UIUtils.getColor(getActivity(), R.color.white));
binding.readingFeedTitle.setShadowLayer(1, 0, 1, UIUtils.getColor(getActivity(), R.color.half_black));
}
if (!displayFeedDetails) {
itemFeed.setVisibility(View.GONE);
binding.readingFeedTitle.setVisibility(View.GONE);
feedIcon.setVisibility(View.GONE);
} else {
FeedUtils.iconLoader.displayImage(feedIconUrl, feedIcon, 0, false);
itemFeed.setText(feedTitle);
binding.readingFeedTitle.setText(feedTitle);
}
itemDate.setText(StoryUtils.formatLongDate(getActivity(), story.timestamp));
if (story.tags.length <= 0) {
tagContainer.setVisibility(View.GONE);
binding.readingItemTags.setVisibility(View.GONE);
}
itemAuthors.setOnClickListener(new OnClickListener() {
binding.readingItemAuthors.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (story.feedId.equals("0")) return; // cannot train on feedless stories
@ -473,7 +480,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
});
itemFeed.setOnClickListener(new OnClickListener() {
binding.readingFeedTitle.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (story.feedId.equals("0")) return; // cannot train on feedless stories
@ -482,7 +489,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
});
itemTitle.setOnClickListener(new OnClickListener() {
binding.readingItemTitle.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_VIEW);
@ -506,7 +513,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
Drawable tag_green_background = UIUtils.getDrawable(getActivity(), R.drawable.tag_background_positive);
Drawable tag_red_background = UIUtils.getDrawable(getActivity(), R.drawable.tag_background_negative);
tagContainer.removeAllViews();
binding.readingItemTags.removeAllViews();
for (String tag : story.tags) {
// TODO: these textviews with compound images are buggy, but stubbed in to let colourblind users
// see what is going on. these should be replaced with proper Chips when the v28 Chip lib
@ -549,21 +556,21 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
});
}
tagContainer.addView(v);
binding.readingItemTags.addView(v);
}
if (!TextUtils.isEmpty(story.authors)) {
itemAuthors.setText("" + story.authors);
binding.readingItemAuthors.setText("" + story.authors);
if (classifier != null && classifier.authors.containsKey(story.authors)) {
switch (classifier.authors.get(story.authors)) {
case Classifier.LIKE:
itemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.positive));
binding.readingItemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.positive));
break;
case Classifier.DISLIKE:
itemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.negative));
binding.readingItemAuthors.setTextColor(UIUtils.getColor(getActivity(), R.color.negative));
break;
default:
itemAuthors.setTextColor(UIUtils.getThemedColor(getActivity(), R.attr.readingItemMetadata, android.R.attr.textColor));
binding.readingItemAuthors.setTextColor(UIUtils.getThemedColor(getActivity(), R.attr.readingItemMetadata, android.R.attr.textColor));
break;
}
}
@ -571,7 +578,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
String title = story.title;
title = UIUtils.colourTitleFromClassifier(title, classifier);
itemTitle.setText(UIUtils.fromHtml(title));
binding.readingItemTitle.setText(UIUtils.fromHtml(title));
}
public void switchSelectedViewMode() {
@ -613,8 +620,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
private void reloadStoryContent() {
// reset indicators
textViewLoadingMsg.setVisibility(View.GONE);
textViewLoadingFailedMsg.setVisibility(View.GONE);
binding.readingTextloading.setVisibility(View.GONE);
binding.readingTextmodefailed.setVisibility(View.GONE);
enableProgress(false);
boolean needStoryContent = false;
@ -623,10 +630,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
needStoryContent = true;
} else {
if (textViewUnavailable) {
textViewLoadingFailedMsg.setVisibility(View.VISIBLE);
binding.readingTextmodefailed.setVisibility(View.VISIBLE);
needStoryContent = true;
} else if (originalText == null) {
textViewLoadingMsg.setVisibility(View.VISIBLE);
binding.readingTextloading.setVisibility(View.VISIBLE);
enableProgress(true);
loadOriginalText();
// still show the story mode version, as the text mode one may take some time
@ -786,7 +793,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
builder.append("</head><body><div class=\"NB-story\">");
builder.append(storyText);
builder.append("</div></body></html>");
web.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
binding.readingWebview.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "UTF-8", null);
}
}
@ -887,10 +894,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
// insufficient time to allow the WebView to actually finish internally computing state and size.
// an additional fixed delay is added in a last ditch attempt to give the black-box platform
// threads a chance to finish their work.
fragmentScrollview.postDelayed(new Runnable() {
binding.readingScrollview.postDelayed(new Runnable() {
public void run() {
int relPos = Math.round(fragmentScrollview.getChildAt(0).getMeasuredHeight() * savedScrollPosRel);
fragmentScrollview.scrollTo(0, relPos);
int relPos = Math.round(binding.readingScrollview.getChildAt(0).getMeasuredHeight() * savedScrollPosRel);
binding.readingScrollview.scrollTo(0, relPos);
}
}, 75L);
}
@ -903,7 +910,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
private class TextSizeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
web.setTextSize(intent.getFloatExtra(TEXT_SIZE_VALUE, 1.0f));
binding.readingWebview.setTextSize(intent.getFloatExtra(TEXT_SIZE_VALUE, 1.0f));
}
}
@ -924,7 +931,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (web.wasLinkClicked()) {
if (binding.readingWebview.wasLinkClicked()) {
// Clicked a link so ignore immersive view
return super.onSingleTapUp(e);
}

View file

@ -1,16 +1,13 @@
package com.newsblur.fragment;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.activity.Reading;
import com.newsblur.databinding.FragmentReadingpagerBinding;
/*
* A fragment to hold the story pager. Eventually this fragment should hold much of the UI and logic
@ -21,8 +18,6 @@ import com.newsblur.activity.Reading;
*/
public class ReadingPagerFragment extends NbFragment {
@Bind(R.id.reading_pager) ViewPager pager;
public static ReadingPagerFragment newInstance() {
ReadingPagerFragment fragment = new ReadingPagerFragment();
Bundle arguments = new Bundle();
@ -33,12 +28,12 @@ public class ReadingPagerFragment extends NbFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_readingpager, null);
ButterKnife.bind(this, v);
FragmentReadingpagerBinding binding = FragmentReadingpagerBinding.bind(v);
Reading activity = ((Reading) getActivity());
pager.addOnPageChangeListener(activity);
activity.offerPager(pager, getChildFragmentManager());
binding.readingPager.addOnPageChangeListener(activity);
activity.offerPager(binding.readingPager, getChildFragmentManager());
return v;
}

View file

@ -3,23 +3,19 @@ package com.newsblur.fragment;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.ViewSwitcher;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.activity.AddSocial;
import com.newsblur.activity.Login;
import com.newsblur.databinding.FragmentRegisterprogressBinding;
import com.newsblur.network.APIManager;
import com.newsblur.network.domain.RegisterResponse;
@ -31,9 +27,7 @@ public class RegisterProgressFragment extends Fragment {
private String password;
private String email;
private RegisterTask registerTask;
@Bind(R.id.register_viewswitcher) ViewSwitcher switcher;
@Bind(R.id.registering_next_1) Button next;
@Bind(R.id.registerprogress_logo) ImageView registerProgressLogo;
private FragmentRegisterprogressBinding binding;
public static RegisterProgressFragment getInstance(String username, String password, String email) {
RegisterProgressFragment fragment = new RegisterProgressFragment();
@ -59,12 +53,12 @@ public class RegisterProgressFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_registerprogress, null);
ButterKnife.bind(this, v);
binding = FragmentRegisterprogressBinding.bind(v);
registerProgressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
binding.registerprogressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
if (registerTask != null) {
switcher.showNext();
binding.registerViewswitcher.showNext();
} else {
registerTask = new RegisterTask();
registerTask.execute();
@ -73,7 +67,18 @@ public class RegisterProgressFragment extends Fragment {
return v;
}
@OnClick(R.id.registering_next_1) void next() {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.registeringNext1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
next();
}
});
}
private void next() {
Intent i = new Intent(getActivity(), AddSocial.class);
startActivity(i);
}
@ -88,7 +93,7 @@ public class RegisterProgressFragment extends Fragment {
@Override
protected void onPostExecute(RegisterResponse response) {
if (response.authenticated) {
switcher.showNext();
binding.registerViewswitcher.showNext();
} else {
String errorMessage = response.getErrorMessage();
if(errorMessage == null) {

View file

@ -0,0 +1,106 @@
package com.newsblur.fragment;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import com.newsblur.R;
import com.newsblur.databinding.DialogRenameBinding;
import com.newsblur.domain.Feed;
import com.newsblur.network.APIManager;
import com.newsblur.util.AppConstants;
import com.newsblur.util.FeedUtils;
public class RenameDialogFragment extends DialogFragment {
private static final String FEED = "feed";
private static final String FOLDER = "folder";
private static final String FOLDER_NAME = "folder_name";
private static final String FOLDER_PARENT = "folder_parent";
private static final String RENAME_TYPE = "rename_type";
public static RenameDialogFragment newInstance(Feed feed) {
RenameDialogFragment fragment = new RenameDialogFragment();
Bundle args = new Bundle();
args.putSerializable(FEED, feed);
args.putString(RENAME_TYPE, FEED);
fragment.setArguments(args);
return fragment;
}
public static RenameDialogFragment newInstance(String folderName, String folderParent) {
RenameDialogFragment fragment = new RenameDialogFragment();
Bundle args = new Bundle();
args.putString(FOLDER_NAME, folderName);
args.putString(FOLDER_PARENT, folderParent);
args.putString(RENAME_TYPE, FOLDER);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Activity activity = getActivity();
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_rename, null);
final DialogRenameBinding binding = DialogRenameBinding.bind(v);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(v);
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
RenameDialogFragment.this.dismiss();
}
});
if (getArguments().getString(RENAME_TYPE).equals(FEED)) {
final Feed feed = (Feed) getArguments().getSerializable(FEED);
builder.setTitle(String.format(getResources().getString(R.string.title_rename_feed), feed.title));
binding.inputName.setText(feed.title);
builder.setPositiveButton(R.string.feed_name_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
FeedUtils.renameFeed(activity, feed.feedId, binding.inputName.getText().toString());
RenameDialogFragment.this.dismiss();
}
});
} else { // FOLDER
final String folderName = getArguments().getString(FOLDER_NAME);
final String folderParentName = getArguments().getString(FOLDER_PARENT);
builder.setTitle(String.format(getResources().getString(R.string.title_rename_folder), folderName));
binding.inputName.setText(folderName);
builder.setPositiveButton(R.string.folder_name_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String newFolderName = binding.inputName.getText().toString().toUpperCase();
if (TextUtils.isEmpty(newFolderName)) {
Toast.makeText(activity, R.string.add_folder_name, Toast.LENGTH_SHORT).show();
return;
}
String inFolder = "";
if (!TextUtils.isEmpty(folderParentName) && !folderParentName.equals(AppConstants.ROOT_FOLDER)) {
inFolder = folderParentName;
}
FeedUtils.renameFolder(folderName, newFolderName, inFolder, activity, new APIManager(activity));
RenameDialogFragment.this.dismiss();
}
});
}
return builder.create();
}
}

View file

@ -1,69 +0,0 @@
package com.newsblur.fragment;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.domain.Feed;
import com.newsblur.util.FeedUtils;
public class RenameFeedFragment extends DialogFragment {
private Feed feed;
@Bind(R.id.feed_name_field) EditText feedNameView;
public static RenameFeedFragment newInstance(Feed feed) {
RenameFeedFragment fragment = new RenameFeedFragment();
Bundle args = new Bundle();
args.putSerializable("feed", feed);
fragment.setArguments(args);
return fragment;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
feed = (Feed) getArguments().getSerializable("feed");
final Activity activity = getActivity();
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_rename_feed, null);
ButterKnife.bind(this, v);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(String.format(getResources().getString(R.string.title_rename_feed), feed.title));
builder.setView(v);
feedNameView.setText(feed.title);
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
RenameFeedFragment.this.dismiss();
}
});
builder.setPositiveButton(R.string.feed_name_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
FeedUtils.renameFeed(activity, feed.feedId, feedNameView.getText().toString());
RenameFeedFragment.this.dismiss();
}
});
Dialog dialog = builder.create();
return dialog;
}
}

View file

@ -0,0 +1,48 @@
package com.newsblur.fragment;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import com.newsblur.R;
import com.newsblur.network.APIManager;
import com.newsblur.util.FeedUtils;
public class SaveSearchFragment extends DialogFragment {
private static final String FEED_ID = "feed_id";
private static final String QUERY = "query";
public static SaveSearchFragment newInstance(String feedId, String query) {
SaveSearchFragment frag = new SaveSearchFragment();
Bundle args = new Bundle();
args.putString(FEED_ID, feedId);
args.putString(QUERY, query);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(String.format(getResources().getString(R.string.add_saved_search_message), getArguments().getString(QUERY)));
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
FeedUtils.saveSearch(getArguments().getString(FEED_ID), getArguments().getString(QUERY), getActivity(), new APIManager(getActivity()));
SaveSearchFragment.this.dismiss();
}
});
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
SaveSearchFragment.this.dismiss();
}
});
return builder.create();
}
}

View file

@ -14,20 +14,15 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import butterknife.ButterKnife;
import butterknife.Bind;
import com.newsblur.R;
import com.newsblur.databinding.DialogTrainstoryBinding;
import com.newsblur.domain.Classifier;
import com.newsblur.domain.Story;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.UIUtils;
import com.newsblur.view.SelectOnlyEditText;
public class StoryIntelTrainerFragment extends DialogFragment {
@ -35,17 +30,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
private FeedSet fs;
private Classifier classifier;
private Integer newTitleTraining;
@Bind(R.id.intel_tag_header) TextView headerTags;
@Bind(R.id.intel_author_header) TextView headerAuthor;
@Bind(R.id.intel_title_selection) SelectOnlyEditText titleSelection;
@Bind(R.id.intel_title_like) Button titleLikeButton;
@Bind(R.id.intel_title_dislike) Button titleDislikeButton;
@Bind(R.id.intel_title_clear) Button titleClearButton;
@Bind(R.id.existing_title_intel_container) LinearLayout titleRowsContainer;
@Bind(R.id.existing_tag_intel_container) LinearLayout tagRowsContainer;
@Bind(R.id.existing_author_intel_container) LinearLayout authorRowsContainer;
@Bind(R.id.existing_feed_intel_container) LinearLayout feedRowsContainer;
private DialogTrainstoryBinding binding;
public static StoryIntelTrainerFragment newInstance(Story story, FeedSet fs) {
if (story.feedId.equals("0")) {
@ -69,44 +54,44 @@ public class StoryIntelTrainerFragment extends DialogFragment {
final Activity activity = getActivity();
LayoutInflater inflater = LayoutInflater.from(activity);
View v = inflater.inflate(R.layout.dialog_trainstory, null);
ButterKnife.bind(this, v);
binding = DialogTrainstoryBinding.bind(v);
// set up the special title training box for the title from this story and the associated buttons
titleSelection.setText(story.title);
binding.intelTitleSelection.setText(story.title);
// the layout sets inputType="none" on this EditText, but a widespread platform bug requires us
// to also set this programmatically to make the field read-only for selection.
titleSelection.setInputType(InputType.TYPE_NULL);
binding.intelTitleSelection.setInputType(InputType.TYPE_NULL);
// the user is selecting for our custom widget, not to copy/paste
titleSelection.disableActionMenu();
binding.intelTitleSelection.disableActionMenu();
// pre-select the whole title to make it easier for the user to manipulate the selection handles
titleSelection.selectAll();
binding.intelTitleSelection.selectAll();
// do this after init and selection to prevent toast spam
titleSelection.setForceSelection(true);
binding.intelTitleSelection.setForceSelection(true);
// the disposition buttons for a new title training don't immediately impact the classifier object,
// lest the user want to change selection substring after choosing the disposition. so just store
// the training factor in a variable that can be pulled on completion
titleLikeButton.setOnClickListener(new OnClickListener() {
binding.intelTitleLike.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
newTitleTraining = Classifier.LIKE;
titleLikeButton.setBackgroundResource(R.drawable.ic_like_active);
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_gray55);
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_active);
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_gray55);
}
});
titleDislikeButton.setOnClickListener(new OnClickListener() {
binding.intelTitleDislike.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
newTitleTraining = Classifier.DISLIKE;
titleLikeButton.setBackgroundResource(R.drawable.ic_like_gray55);
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_active);
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_gray55);
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_active);
}
});
titleClearButton.setOnClickListener(new OnClickListener() {
binding.intelTitleClear.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
newTitleTraining = null;
titleLikeButton.setBackgroundResource(R.drawable.ic_like_gray55);
titleDislikeButton.setBackgroundResource(R.drawable.ic_dislike_gray55);
binding.intelTitleLike.setBackgroundResource(R.drawable.ic_like_gray55);
binding.intelTitleDislike.setBackgroundResource(R.drawable.ic_dislike_gray55);
}
});
@ -117,7 +102,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
label.setText(rule.getKey());
UIUtils.setupIntelDialogRow(row, classifier.title, rule.getKey());
titleRowsContainer.addView(row);
binding.existingTitleIntelContainer.addView(row);
}
}
@ -127,9 +112,9 @@ public class StoryIntelTrainerFragment extends DialogFragment {
TextView label = (TextView) row.findViewById(R.id.intel_row_label);
label.setText(tag);
UIUtils.setupIntelDialogRow(row, classifier.tags, tag);
tagRowsContainer.addView(row);
binding.existingTagIntelContainer.addView(row);
}
if (story.tags.length < 1) headerTags.setVisibility(View.GONE);
if (story.tags.length < 1) binding.intelTagHeader.setVisibility(View.GONE);
// there is a single author per story
if (!TextUtils.isEmpty(story.authors)) {
@ -137,9 +122,9 @@ public class StoryIntelTrainerFragment extends DialogFragment {
TextView labelAuthor = (TextView) rowAuthor.findViewById(R.id.intel_row_label);
labelAuthor.setText(story.authors);
UIUtils.setupIntelDialogRow(rowAuthor, classifier.authors, story.authors);
authorRowsContainer.addView(rowAuthor);
binding.existingAuthorIntelContainer.addView(rowAuthor);
} else {
headerAuthor.setVisibility(View.GONE);
binding.intelAuthorHeader.setVisibility(View.GONE);
}
// there is a single feed to be trained, but it is a bit odd in that the label is the title and
@ -148,7 +133,7 @@ public class StoryIntelTrainerFragment extends DialogFragment {
TextView labelFeed = (TextView) rowFeed.findViewById(R.id.intel_row_label);
labelFeed.setText(FeedUtils.getFeedTitle(story.feedId));
UIUtils.setupIntelDialogRow(rowFeed, classifier.feeds, story.feedId);
feedRowsContainer.addView(rowFeed);
binding.existingFeedIntelContainer.addView(rowFeed);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.story_intel_dialog_title);
@ -163,8 +148,8 @@ public class StoryIntelTrainerFragment extends DialogFragment {
builder.setPositiveButton(R.string.dialog_story_intel_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if ((newTitleTraining != null) && (!TextUtils.isEmpty(titleSelection.getSelection()))) {
classifier.title.put(titleSelection.getSelection(), newTitleTraining);
if ((newTitleTraining != null) && (!TextUtils.isEmpty(binding.intelTitleSelection.getSelection()))) {
classifier.title.put(binding.intelTitleSelection.getSelection(), newTitleTraining);
}
FeedUtils.updateClassifier(story.feedId, classifier, fs, activity);
StoryIntelTrainerFragment.this.dismiss();

View file

@ -7,13 +7,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RadioButton;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.databinding.StoryorderDialogBinding;
import com.newsblur.util.StoryOrder;
import com.newsblur.util.StoryOrderChangedListener;
@ -21,8 +17,6 @@ public class StoryOrderDialogFragment extends DialogFragment {
private static String CURRENT_ORDER = "currentOrder";
private StoryOrder currentValue;
@Bind(R.id.radio_newest) RadioButton newestButton;
@Bind(R.id.radio_oldest) RadioButton oldestButton;
public static StoryOrderDialogFragment newInstance(StoryOrder currentValue) {
StoryOrderDialogFragment dialog = new StoryOrderDialogFragment();
@ -42,25 +36,38 @@ public class StoryOrderDialogFragment extends DialogFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
currentValue = (StoryOrder) getArguments().getSerializable(CURRENT_ORDER);
View v = inflater.inflate(R.layout.storyorder_dialog, null);
ButterKnife.bind(this, v);
StoryorderDialogBinding binding = StoryorderDialogBinding.bind(v);
newestButton.setChecked(currentValue == StoryOrder.NEWEST);
oldestButton.setChecked(currentValue == StoryOrder.OLDEST);
binding.radioNewest.setChecked(currentValue == StoryOrder.NEWEST);
binding.radioOldest.setChecked(currentValue == StoryOrder.OLDEST);
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().getAttributes().gravity = Gravity.BOTTOM;
binding.radioNewest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectNewest();
}
});
binding.radioOldest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectOldest();
}
});
return v;
}
@OnClick(R.id.radio_newest) void selectNewest() {
private void selectNewest() {
if (currentValue != StoryOrder.NEWEST) {
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.NEWEST);
}
dismiss();
}
@OnClick(R.id.radio_oldest) void selectOldest() {
private void selectOldest() {
if (currentValue != StoryOrder.OLDEST) {
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.OLDEST);
}

View file

@ -71,6 +71,11 @@ public class APIConstants {
public static final String PATH_SET_NOTIFICATIONS = "/notifications/feed/";
public static final String PATH_INSTA_FETCH = "/rss_feeds/exception_retry";
public static final String PATH_RENAME_FEED = "/reader/rename_feed";
public static final String PATH_DELETE_SEARCH = "/reader/delete_search";
public static final String PATH_SAVE_SEARCH = "/reader/save_search";
public static final String PATH_ADD_FOLDER = "/reader/add_folder";
public static final String PATH_DELETE_FOLDER = "/reader/delete_folder";
public static final String PATH_RENAME_FOLDER = "/reader/rename_folder";
public static String buildUrl(String path) {
return CurrentUrlBase + path;
@ -121,6 +126,9 @@ public class APIConstants {
public static final String PARAMETER_RESET_FETCH = "reset_fetch";
public static final String PARAMETER_INFREQUENT = "infrequent";
public static final String PARAMETER_FEEDTITLE = "feed_title";
public static final String PARAMETER_FOLDER_TO_DELETE = "folder_to_delete";
public static final String PARAMETER_FOLDER_TO_RENAME = "folder_to_rename";
public static final String PARAMETER_NEW_FOLDER_NAME = "new_folder_name";
public static final String VALUE_PREFIX_SOCIAL = "social:";
public static final String VALUE_ALLSOCIAL = "river:blurblogs"; // the magic value passed to the mark-read API for all social feeds

View file

@ -15,6 +15,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
@ -550,11 +551,21 @@ public class APIManager {
return (CommentResponse) response.getResponse(gson, CommentResponse.class);
}
public AddFeedResponse addFeed(String feedUrl) {
public NewsBlurResponse addFolder(String folderName) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FOLDER, folderName);
APIResponse response = post(buildUrl(APIConstants.PATH_ADD_FOLDER), values);
return response.getResponse(gson, NewsBlurResponse.class);
}
public AddFeedResponse addFeed(String feedUrl, @Nullable String folderName) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_URL, feedUrl);
if (!TextUtils.isEmpty(folderName)) {
values.put(APIConstants.PARAMETER_FOLDER, folderName);
}
APIResponse response = post(buildUrl(APIConstants.PATH_ADD_FEED), values);
return (AddFeedResponse) response.getResponse(gson, AddFeedResponse.class);
return response.getResponse(gson, AddFeedResponse.class);
}
public FeedResult[] searchForFeed(String searchTerm) {
@ -579,6 +590,30 @@ public class APIManager {
return response.getResponse(gson, NewsBlurResponse.class);
}
public NewsBlurResponse deleteFolder(String folderName, String inFolder) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FOLDER_TO_DELETE, folderName);
values.put(APIConstants.PARAMETER_IN_FOLDER, inFolder);
APIResponse response = post(buildUrl(APIConstants.PATH_DELETE_FOLDER), values);
return response.getResponse(gson, NewsBlurResponse.class);
}
public NewsBlurResponse deleteSearch(String feedId, String query) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FEEDID, feedId);
values.put(APIConstants.PARAMETER_QUERY, query);
APIResponse response = post(buildUrl(APIConstants.PATH_DELETE_SEARCH), values);
return response.getResponse(gson, NewsBlurResponse.class);
}
public NewsBlurResponse saveSearch(String feedId, String query) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FEEDID, feedId);
values.put(APIConstants.PARAMETER_QUERY, query);
APIResponse response = post(buildUrl(APIConstants.PATH_SAVE_SEARCH), values);
return response.getResponse(gson, NewsBlurResponse.class);
}
public NewsBlurResponse saveFeedChooser(Set<String> feeds) {
ValueMultimap values = new ValueMultimap();
for (String feed : feeds) {
@ -617,6 +652,15 @@ public class APIManager {
return response.getResponse(gson, NewsBlurResponse.class);
}
public NewsBlurResponse renameFolder(String folderName, String newFolderName, String inFolder) {
ContentValues values = new ContentValues();
values.put(APIConstants.PARAMETER_FOLDER_TO_RENAME, folderName);
values.put(APIConstants.PARAMETER_NEW_FOLDER_NAME, newFolderName);
values.put(APIConstants.PARAMETER_IN_FOLDER, inFolder);
APIResponse response = post(buildUrl(APIConstants.PATH_RENAME_FOLDER), values);
return response.getResponse(gson, NewsBlurResponse.class);
}
/* HTTP METHODS */
private APIResponse get(final String urlString) {

View file

@ -15,6 +15,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.StarredCount;
import com.newsblur.util.AppConstants;
@ -30,6 +31,7 @@ public class FeedFolderResponse {
public Set<Feed> feeds;
public Set<SocialFeed> socialFeeds;
public Set<StarredCount> starredCounts;
public Set<SavedSearch> savedSearches;
public boolean isAuthenticated;
public boolean isPremium;
@ -109,6 +111,16 @@ public class FeedFolderResponse {
}
}
savedSearches = new HashSet<>();
JsonArray savedSearchesArray = (JsonArray) asJsonObject.get("saved_searches");
if (savedSearchesArray != null) {
for (int i=0; i<savedSearchesArray.size(); i++) {
JsonElement jsonElement = savedSearchesArray.get(i);
SavedSearch savedSearch = gson.fromJson(jsonElement, SavedSearch.class);
savedSearches.add(savedSearch);
}
}
parseTime = System.currentTimeMillis() - startTime;
}

View file

@ -24,6 +24,7 @@ public class StoryTypeAdapter implements JsonDeserializer<Story> {
// any characters we don't want in the short description, such as newlines or placeholders
private final static Pattern ShortContentExcludes = Pattern.compile("[\\uFFFC\\u000A\\u000B\\u000C\\u000D]");
private final static Pattern httpSniff = Pattern.compile("(?:http):\\/\\/");
public StoryTypeAdapter() {
this.gson = new GsonBuilder()
@ -39,6 +40,14 @@ public class StoryTypeAdapter implements JsonDeserializer<Story> {
// Convert story_timestamp to milliseconds
story.timestamp = story.timestamp * 1000;
// replace http image urls with https
if (httpSniff.matcher(story.content).find() && story.secureImageUrls != null && story.secureImageUrls.size() > 0) {
for (String httpUrl : story.secureImageUrls.keySet()) {
String httpsUrl = story.secureImageUrls.get(httpUrl);
story.content = story.content.replace(httpUrl, httpsUrl);
}
}
// populate the shortContent field
if (story.content != null) {

View file

@ -16,6 +16,7 @@ import static com.newsblur.database.BlurDatabaseHelper.closeQuietly;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SavedSearch;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.StarredCount;
import com.newsblur.domain.Story;
@ -586,6 +587,12 @@ public class NBSyncService extends JobService {
for (StarredCount sc : feedResponse.starredCounts) {
starredCountValues.add(sc.getValues());
}
// saved searches table
List<ContentValues> savedSearchesValues = new ArrayList<>();
for (SavedSearch savedSearch : feedResponse.savedSearches) {
savedSearchesValues.add(savedSearch.getValues());
}
// the API vends the starred total as a different element, roll it into
// the starred counts table using a special tag
StarredCount totalStarred = new StarredCount();
@ -593,7 +600,7 @@ public class NBSyncService extends JobService {
totalStarred.tag = StarredCount.TOTAL_STARRED;
starredCountValues.add(totalStarred.getValues());
dbHelper.setFeedsFolders(folderValues, feedValues, socialFeedValues, starredCountValues);
dbHelper.setFeedsFolders(folderValues, feedValues, socialFeedValues, starredCountValues, savedSearchesValues);
lastFFWriteMillis = System.currentTimeMillis() - startTime;
lastFeedCount = feedValues.size();

View file

@ -34,6 +34,7 @@ public class FeedSet implements Serializable {
private String folderName;
private String searchQuery;
private String searchFeedId;
private boolean isFilterSaved = false;
private boolean muted = false;
@ -75,17 +76,6 @@ public class FeedSet implements Serializable {
return fs;
}
/**
* Convenience constructor for multiple feeds with IDs
*/
public static FeedSet multipleFeeds(Set<String> feedIds) {
FeedSet fs = new FeedSet();
fs.feeds = new HashSet<>(feedIds.size());
fs.feeds.addAll(feedIds);
fs.feeds = Collections.unmodifiableSet(fs.feeds);
return fs;
}
/**
* Convenience constructor for all (non-social) feeds.
*/
@ -133,6 +123,16 @@ public class FeedSet implements Serializable {
return fs;
}
/**
* Convenience constructor for a single saved search.
*/
public static FeedSet singleSavedSearch(String feedId, String searchQuery) {
FeedSet fs = new FeedSet();
fs.searchQuery = searchQuery;
fs.searchFeedId = feedId;
return fs;
}
/**
* Convenience constructor for global shared stories feed.
*/
@ -281,6 +281,10 @@ public class FeedSet implements Serializable {
return this.searchQuery;
}
public String getSearchFeedId() {
return this.searchFeedId;
}
public void setFilterSaved(boolean isFilterSaved) {
this.isFilterSaved = isFilterSaved;
}

View file

@ -9,6 +9,7 @@ import java.util.Set;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.newsblur.R;
@ -18,6 +19,7 @@ import com.newsblur.domain.Classifier;
import com.newsblur.domain.Feed;
import com.newsblur.domain.Folder;
import com.newsblur.domain.SocialFeed;
import com.newsblur.domain.StarredCount;
import com.newsblur.domain.Story;
import com.newsblur.fragment.ReadingActionConfirmationFragment;
import com.newsblur.network.APIManager;
@ -109,6 +111,40 @@ public class FeedUtils {
}.execute();
}
public static void deleteSavedSearch(final String feedId, final String query, final Context context, final APIManager apiManager) {
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
protected NewsBlurResponse doInBackground(Void... voids) {
return apiManager.deleteSearch(feedId, query);
}
@Override
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
if (!newsBlurResponse.isError()) {
dbHelper.deleteSavedSearch(feedId, query);
NbActivity.updateAllActivities(NbActivity.UPDATE_METADATA);
}
}
}.execute();
}
public static void saveSearch(final String feedId, final String query, final Context context, final APIManager apiManager) {
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
protected NewsBlurResponse doInBackground(Void... voids) {
return apiManager.saveSearch(feedId, query);
}
@Override
protected void onPostExecute(NewsBlurResponse newsBlurResponse) {
if (!newsBlurResponse.isError()) {
NBSyncService.forceFeedsFolders();
triggerSync(context);
}
}
}.execute();
}
public static void deleteFeed(final String feedId, final String folderName, final Context context, final APIManager apiManager) {
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
@ -140,6 +176,42 @@ public class FeedUtils {
}.execute();
}
public static void deleteFolder(final String folderName, final String inFolder, final Context context, final APIManager apiManager) {
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
protected NewsBlurResponse doInBackground(Void... voids) {
return apiManager.deleteFolder(folderName, inFolder);
}
@Override
protected void onPostExecute(NewsBlurResponse result) {
super.onPostExecute(result);
if (!result.isError()) {
NBSyncService.forceFeedsFolders();
triggerSync(context);
}
}
}.execute();
}
public static void renameFolder(final String folderName, final String newFolderName, final String inFolder, final Context context, final APIManager apiManager) {
new AsyncTask<Void, Void, NewsBlurResponse>() {
@Override
protected NewsBlurResponse doInBackground(Void... voids) {
return apiManager.renameFolder(folderName, newFolderName, inFolder);
}
@Override
protected void onPostExecute(NewsBlurResponse result) {
super.onPostExecute(result);
if (!result.isError()) {
NBSyncService.forceFeedsFolders();
triggerSync(context);
}
}
}.execute();
}
public static void markStoryUnread(final Story story, final Context context) {
new AsyncTask<Void, Void, Void>() {
@Override
@ -518,4 +590,8 @@ public class FeedUtils {
return dbHelper.getSocialFeed(feedId);
}
@Nullable
public static StarredCount getStarredFeedByTag(String feedId) {
return dbHelper.getStarredFeedByTag(feedId);
}
}

View file

@ -5,14 +5,10 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import butterknife.ButterKnife;
import butterknife.Bind;
import butterknife.OnClick;
import com.newsblur.R;
import com.newsblur.databinding.StateToggleBinding;
import com.newsblur.util.StateFilter;
import com.newsblur.util.UIUtils;
@ -27,52 +23,53 @@ public class StateToggleButton extends LinearLayout {
private int parentWidthPX = 0;
@Bind(R.id.toggle_all) ViewGroup allButton;
@Bind(R.id.toggle_all_icon) View allButtonIcon;
@Bind(R.id.toggle_all_text) View allButtonText;
@Bind(R.id.toggle_some) ViewGroup someButton;
@Bind(R.id.toggle_some_icon) View someButtonIcon;
@Bind(R.id.toggle_some_text) View someButtonText;
@Bind(R.id.toggle_focus) ViewGroup focusButton;
@Bind(R.id.toggle_focus_icon) View focusButtonIcon;
@Bind(R.id.toggle_focus_text) View focusButtonText;
@Bind(R.id.toggle_saved) ViewGroup savedButton;
@Bind(R.id.toggle_saved_icon) View savedButtonIcon;
@Bind(R.id.toggle_saved_text) View savedButtonText;
private StateToggleBinding binding;
public StateToggleButton(Context context, AttributeSet art) {
super(context, art);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.state_toggle, this);
ButterKnife.bind(this, view);
binding = StateToggleBinding.bind(view);
// smooth layout transitions are enabled in our layout XML; this smooths out toggle
// transitions on newer devices
allButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
someButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
focusButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
savedButton.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
binding.toggleAll.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
binding.toggleSome.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
binding.toggleFocus.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
binding.toggleSaved.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
setState(state);
binding.toggleAll.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setState(StateFilter.ALL);
}
});
binding.toggleSome.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setState(StateFilter.SOME);
}
});
binding.toggleFocus.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setState(StateFilter.BEST);
}
});
binding.toggleSaved.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setState(StateFilter.SAVED);
}
});
}
public void setStateListener(final StateChangedListener stateChangedListener) {
this.stateChangedListener = stateChangedListener;
}
@OnClick({R.id.toggle_all, R.id.toggle_some, R.id.toggle_focus, R.id.toggle_saved})
public void onClickToggle(View v) {
if (v.getId() == R.id.toggle_all) {
setState(StateFilter.ALL);
} else if (v.getId() == R.id.toggle_some) {
setState(StateFilter.SOME);
} else if (v.getId() == R.id.toggle_focus) {
setState(StateFilter.BEST);
} else if (v.getId() == R.id.toggle_saved) {
setState(StateFilter.SAVED);
}
}
public void setState(StateFilter state) {
this.state = state;
updateButtonStates();
@ -93,21 +90,21 @@ public class StateToggleButton extends LinearLayout {
if (widthDP > COLLAPSE_WIDTH_DP) compactMode = false;
}
allButtonText.setVisibility((!compactMode || state == StateFilter.ALL) ? View.VISIBLE : View.GONE);
allButton.setEnabled(state != StateFilter.ALL);
allButtonIcon.setAlpha(state == StateFilter.ALL ? 1.0f : 0.6f);
binding.toggleAllText.setVisibility((!compactMode || state == StateFilter.ALL) ? View.VISIBLE : View.GONE);
binding.toggleAll.setEnabled(state != StateFilter.ALL);
binding.toggleAllIcon.setAlpha(state == StateFilter.ALL ? 1.0f : 0.6f);
someButtonText.setVisibility((!compactMode || state == StateFilter.SOME) ? View.VISIBLE : View.GONE);
someButton.setEnabled(state != StateFilter.SOME);
someButtonIcon.setAlpha(state == StateFilter.SOME ? 1.0f : 0.6f);
binding.toggleSomeText.setVisibility((!compactMode || state == StateFilter.SOME) ? View.VISIBLE : View.GONE);
binding.toggleSome.setEnabled(state != StateFilter.SOME);
binding.toggleSomeIcon.setAlpha(state == StateFilter.SOME ? 1.0f : 0.6f);
focusButtonText.setVisibility((!compactMode || state == StateFilter.BEST) ? View.VISIBLE : View.GONE);
focusButton.setEnabled(state != StateFilter.BEST);
focusButtonIcon.setAlpha(state == StateFilter.BEST ? 1.0f : 0.6f);
binding.toggleFocusText.setVisibility((!compactMode || state == StateFilter.BEST) ? View.VISIBLE : View.GONE);
binding.toggleFocus.setEnabled(state != StateFilter.BEST);
binding.toggleFocusIcon.setAlpha(state == StateFilter.BEST ? 1.0f : 0.6f);
savedButtonText.setVisibility((!compactMode || state == StateFilter.SAVED) ? View.VISIBLE : View.GONE);
savedButton.setEnabled(state != StateFilter.SAVED);
savedButtonIcon.setAlpha(state == StateFilter.SAVED ? 1.0f : 0.6f);
binding.toggleSavedText.setVisibility((!compactMode || state == StateFilter.SAVED) ? View.VISIBLE : View.GONE);
binding.toggleSaved.setEnabled(state != StateFilter.SAVED);
binding.toggleSavedIcon.setAlpha(state == StateFilter.SAVED ? 1.0f : 0.6f);
}
public interface StateChangedListener {