diff --git a/clients/android/NewsBlur/AndroidManifest.xml b/clients/android/NewsBlur/AndroidManifest.xml
index 269e37a96..6c0853177 100644
--- a/clients/android/NewsBlur/AndroidManifest.xml
+++ b/clients/android/NewsBlur/AndroidManifest.xml
@@ -4,7 +4,6 @@
-
@@ -133,19 +132,15 @@
android:name=".activity.MuteConfig"
android:launchMode="singleTask"
android:label="@string/mute_sites"/>
-
-
-
-
-
-
-
-
+
+
+
-
+
+
diff --git a/clients/android/NewsBlur/res/layout/activity_feed_search.xml b/clients/android/NewsBlur/res/layout/activity_feed_search.xml
index 31fbdf06a..9508472dc 100644
--- a/clients/android/NewsBlur/res/layout/activity_feed_search.xml
+++ b/clients/android/NewsBlur/res/layout/activity_feed_search.xml
@@ -4,31 +4,97 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
-
+
diff --git a/clients/android/NewsBlur/res/layout/fragment_readingitem.xml b/clients/android/NewsBlur/res/layout/fragment_readingitem.xml
index ca2077ec8..75a093e6a 100644
--- a/clients/android/NewsBlur/res/layout/fragment_readingitem.xml
+++ b/clients/android/NewsBlur/res/layout/fragment_readingitem.xml
@@ -1,199 +1,197 @@
+ android:layout_height="match_parent">
-
+ android:layout_height="match_parent">
-
+ android:minHeight="6dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
-
+
+
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="40dp"
+ android:ellipsize="end"
+ android:lines="1"
+ android:textSize="12sp"
+ android:textStyle="bold" />
+
+
+
+
+
+
+
+
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp">
-
+
+
+
+
+
+
+
+
+
+ android:layout_gravity="bottom" />
-
+
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:paddingTop="5dp"
+ android:paddingBottom="5dp"
+ android:text="@string/orig_text_loading"
+ android:textSize="16sp"
+ android:textStyle="italic"
+ android:visibility="gone" />
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/clients/android/NewsBlur/res/layout/view_feed_search_row.xml b/clients/android/NewsBlur/res/layout/view_feed_search_row.xml
new file mode 100644
index 000000000..463aaadea
--- /dev/null
+++ b/clients/android/NewsBlur/res/layout/view_feed_search_row.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/clients/android/NewsBlur/res/menu/search.xml b/clients/android/NewsBlur/res/menu/search.xml
deleted file mode 100644
index bbe5300b0..000000000
--- a/clients/android/NewsBlur/res/menu/search.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/clients/android/NewsBlur/res/values/strings.xml b/clients/android/NewsBlur/res/values/strings.xml
index 4d174c6a4..ecfb73088 100644
--- a/clients/android/NewsBlur/res/values/strings.xml
+++ b/clients/android/NewsBlur/res/values/strings.xml
@@ -15,7 +15,7 @@
Retrieving feeds…
Next
- Search for feeds
+ Search for feeds
Loading…
Fetching story text…
@@ -56,7 +56,7 @@
TOP LEVEL
%1$s %2$s
- %1$s \n\n\"%2$s\" \n\n%3$s \n\n--\nShared with NewsBlur.com
+ %1$s \n\%2$s \n\n%3$s \n\n—\nShared with NewsBlur.com
Comment (Optional)
Share \"%s\" to your Blurblog?
SHARE
@@ -200,11 +200,9 @@
Hide Story Changes
Story Changes Loading ...
Search your feeds
- Search term or feed URL
Search for stories
Type a search term to begin
Add new feed
- Search
Adding feed…
Please enter folder name
+ Add new folder
diff --git a/clients/android/NewsBlur/res/xml/searchable.xml b/clients/android/NewsBlur/res/xml/searchable.xml
deleted file mode 100644
index 1db09b7e5..000000000
--- a/clients/android/NewsBlur/res/xml/searchable.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.java b/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.java
deleted file mode 100644
index 401cd9187..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.newsblur.activity;
-
-import java.util.List;
-
-import com.newsblur.R;
-import com.newsblur.domain.FeedResult;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public class FeedSearchResultAdapter extends ArrayAdapter{
-
- private LayoutInflater inflater;
- private Context context;
-
- public FeedSearchResultAdapter(Context context, int resource, int textViewResourceId, List items) {
- super(context, resource, textViewResourceId, items);
- this.context = context;
- inflater = ((Activity) context).getLayoutInflater();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View v;
- if (convertView != null) {
- v = convertView;
- } else {
- v = inflater.inflate(R.layout.row_feedresult, null);
- }
-
- FeedResult result = getItem(position);
- ImageView favicon = (ImageView) v.findViewById(R.id.row_result_feedicon);
- Bitmap bitmap = null;
- if (!TextUtils.isEmpty(result.favicon)) {
- final byte[] data = Base64.decode(result.favicon, Base64.DEFAULT);
- bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
- }
- if (bitmap == null) {
- bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.world);
- }
-
- favicon.setImageBitmap(bitmap);
-
- ((TextView) v.findViewById(R.id.row_result_title)).setText(result.label);
- ((TextView) v.findViewById(R.id.row_result_tagline)).setText(result.tagline);
- if (!TextUtils.isEmpty(result.url)) {
- ((TextView) v.findViewById(R.id.row_result_address)).setText(result.url);
- } else {
- v.findViewById(R.id.row_result_address).setVisibility(View.GONE);
- }
- ((TextView) v.findViewById(R.id.row_result_subscribercount)).setText(result.numberOfSubscriber + " subscribers");
-
-
- return v;
- }
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.kt b/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.kt
new file mode 100644
index 000000000..c20198de4
--- /dev/null
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/FeedSearchResultAdapter.kt
@@ -0,0 +1,94 @@
+package com.newsblur.activity
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.text.TextUtils
+import android.util.Base64
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.newsblur.R
+import com.newsblur.databinding.ViewFeedSearchRowBinding
+import com.newsblur.domain.FeedResult
+
+internal class FeedSearchAdapter(private val onClickListener: OnFeedSearchResultClickListener) : RecyclerView.Adapter() {
+
+ private val resultsList: MutableList = ArrayList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.view_feed_search_row, parent, false)
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val result = resultsList[position]
+ var bitmap: Bitmap? = null
+ if (!TextUtils.isEmpty(result.favicon)) {
+ val data = Base64.decode(result.favicon, Base64.DEFAULT)
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
+ }
+ bitmap?.let {
+ holder.binding.imgFeedIcon.setImageBitmap(bitmap)
+ }
+
+ holder.binding.textTitle.text = result.label
+ holder.binding.textTagline.text = result.tagline
+ val subscribersCountText = holder.binding.root.context.resources.getString(R.string.feed_subscribers, result.numberOfSubscriber)
+ holder.binding.textSubscriptionCount.text = subscribersCountText
+
+ if (!TextUtils.isEmpty(result.url)) {
+ holder.binding.rowResultAddress.text = result.url
+ holder.binding.rowResultAddress.visibility = View.VISIBLE
+ } else {
+ holder.binding.rowResultAddress.visibility = View.GONE
+ }
+
+ holder.itemView.setOnClickListener {
+ onClickListener.onFeedSearchResultClickListener(result)
+ }
+ }
+
+ override fun getItemCount(): Int = resultsList.size
+
+ fun replaceAll(results: Array) {
+ val newResultsList: List = results.toList()
+ val diffCallback = ResultDiffCallback(resultsList, newResultsList)
+ val diffResult = DiffUtil.calculateDiff(diffCallback)
+ resultsList.clear()
+ resultsList.addAll(newResultsList)
+ diffResult.dispatchUpdatesTo(this)
+ }
+
+ internal class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val binding: ViewFeedSearchRowBinding = ViewFeedSearchRowBinding.bind(itemView)
+ }
+
+ internal class ResultDiffCallback(private val oldList: List,
+ private val newList: List) : DiffUtil.Callback() {
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ val oldFeedResult = oldList[oldItemPosition]
+ val newFeedResult = newList[newItemPosition]
+ return oldFeedResult.label == newFeedResult.label &&
+ oldFeedResult.numberOfSubscriber == newFeedResult.numberOfSubscriber
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ val oldFeedResult = oldList[oldItemPosition]
+ val newFeedResult = newList[newItemPosition]
+ return oldFeedResult.label == newFeedResult.label
+ && oldFeedResult.tagline == newFeedResult.tagline
+ }
+
+ override fun getOldListSize(): Int = oldList.size
+
+ override fun getNewListSize(): Int = newList.size
+ }
+
+ interface OnFeedSearchResultClickListener {
+
+ fun onFeedSearchResultClickListener(result: FeedResult)
+ }
+}
\ No newline at end of file
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java b/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
index 7cf4c09fe..b935e772c 100644
--- a/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/NbActivity.java
@@ -3,7 +3,6 @@ package com.newsblur.activity;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.fragment.app.FragmentActivity;
import android.widget.Toast;
import com.newsblur.util.FeedUtils;
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/Reading.java b/clients/android/NewsBlur/src/com/newsblur/activity/Reading.java
index 9fbeb71ec..5ecb97ede 100644
--- a/clients/android/NewsBlur/src/com/newsblur/activity/Reading.java
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/Reading.java
@@ -16,8 +16,6 @@ import androidx.viewpager.widget.ViewPager;
import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.java b/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.java
deleted file mode 100644
index b78229573..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.newsblur.activity;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashSet;
-import java.util.Set;
-
-import android.app.SearchManager;
-import android.content.Intent;
-import android.os.Bundle;
-import androidx.fragment.app.DialogFragment;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.app.LoaderManager.LoaderCallbacks;
-import androidx.loader.content.Loader;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.newsblur.R;
-import com.newsblur.domain.FeedResult;
-import com.newsblur.fragment.AddFeedFragment;
-import com.newsblur.network.SearchAsyncTaskLoader;
-import com.newsblur.network.SearchLoaderResponse;
-import com.newsblur.util.UIUtils;
-
-// TODO: this activity's use of the inbuilt activity search facility as well as an improper use of a loader to
-// make network requests makes it easily lose state, lack non-legacy progress indication, and generally
-// buggy. a normal layout and a proper use of sync for search results should be implemented.
-public class SearchForFeeds extends NbActivity implements LoaderCallbacks, OnItemClickListener, AddFeedFragment.AddFeedProgressListener {
-
- private static final Set SUPPORTED_URL_PROTOCOLS = new HashSet();
- static {
- SUPPORTED_URL_PROTOCOLS.add("http");
- SUPPORTED_URL_PROTOCOLS.add("https");
- }
-
- private ListView resultsList;
- private Loader searchLoader;
- private FeedSearchResultAdapter adapter;
-
- @Override
- protected void onCreate(Bundle arg0) {
- super.onCreate(arg0);
-
- setContentView(R.layout.activity_feed_search);
- UIUtils.setupToolbar(this, R.drawable.logo, getString(R.string.title_feed_search), true);
-
- TextView emptyView = (TextView) findViewById(R.id.empty_view);
- resultsList = (ListView) findViewById(R.id.feed_result_list);
- resultsList.setEmptyView(emptyView);
- resultsList.setOnItemClickListener(this);
- resultsList.setItemsCanFocus(false);
- searchLoader = LoaderManager.getInstance(this).initLoader(0, new Bundle(), this);
-
- onSearchRequested();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.search, menu);
- return true;
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- String query = intent.getStringExtra(SearchManager.QUERY);
-
- // test to see if a feed URL was passed rather than a search term
- if (tryAddByURL(query)) { return; }
-
- Bundle bundle = new Bundle();
- bundle.putString(SearchAsyncTaskLoader.SEARCH_TERM, query);
- searchLoader = LoaderManager.getInstance(this).restartLoader(0, bundle, this);
-
- searchLoader.forceLoad();
- }
- }
-
- /**
- * See if the text entered in the query field was actually a URL so we can skip the
- * search step and just let users who know feed URLs directly subscribe.
- */
- private boolean tryAddByURL(String s) {
- URL u = null;
- try {
- u = new URL(s);
- } catch (MalformedURLException mue) {
- ; // this just signals that the string wasn't a URL, we will return
- }
- if (u == null) { return false; }
- if (! SUPPORTED_URL_PROTOCOLS.contains(u.getProtocol())) { return false; };
- if ((u.getHost() == null) || (u.getHost().trim().isEmpty())) { return false; }
-
- DialogFragment addFeedFragment = AddFeedFragment.newInstance(s, s);
- addFeedFragment.show(getSupportFragmentManager(), "dialog");
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.menu_search) {
- onSearchRequested();
- return true;
- } else if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public Loader onCreateLoader(int loaderId, Bundle bundle) {
- String searchTerm = bundle.getString(SearchAsyncTaskLoader.SEARCH_TERM);
- return new SearchAsyncTaskLoader(this, searchTerm);
- }
-
- @Override
- public void onLoadFinished(Loader loader, SearchLoaderResponse results) {
- if(!results.hasError()) {
- adapter = new FeedSearchResultAdapter(this, 0, 0, results.getResults());
- resultsList.setAdapter(adapter);
- } else {
- String message = results.getErrorMessage() == null ? "Error" : results.getErrorMessage();
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onLoaderReset(Loader loader) {
-
- }
-
- @Override
- public void onItemClick(AdapterView> arg0, View view, int position, long id) {
- FeedResult result = adapter.getItem(position);
- DialogFragment addFeedFragment = AddFeedFragment.newInstance(result.url, result.label);
- addFeedFragment.show(getSupportFragmentManager(), "dialog");
- }
-
- @Override
- public void addFeedStarted() {
- runOnUiThread(new Runnable() {
- public void run() {
- // TODO: this UI should offer some progress indication, since the add API call can block for several seconds
- }
- });
- }
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.kt b/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.kt
new file mode 100644
index 000000000..68ef31caa
--- /dev/null
+++ b/clients/android/NewsBlur/src/com/newsblur/activity/SearchForFeeds.kt
@@ -0,0 +1,149 @@
+package com.newsblur.activity
+
+import android.os.AsyncTask
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import androidx.fragment.app.DialogFragment
+import com.newsblur.activity.FeedSearchAdapter.OnFeedSearchResultClickListener
+import com.newsblur.databinding.ActivityFeedSearchBinding
+import com.newsblur.domain.FeedResult
+import com.newsblur.fragment.AddFeedFragment
+import com.newsblur.fragment.AddFeedFragment.AddFeedProgressListener
+import com.newsblur.network.APIManager
+import java.net.MalformedURLException
+import java.net.URL
+import java.util.*
+
+class SearchForFeeds : NbActivity(), OnFeedSearchResultClickListener, AddFeedProgressListener {
+
+ private val supportedUrlProtocols: MutableSet = HashSet(2)
+
+ init {
+ supportedUrlProtocols.add("http")
+ supportedUrlProtocols.add("https")
+ }
+
+ private lateinit var adapter: FeedSearchAdapter
+ private lateinit var binding: ActivityFeedSearchBinding
+ private lateinit var apiManager: APIManager
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityFeedSearchBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ setupViews()
+ setupListeners()
+ apiManager = APIManager(this)
+ binding.inputSearchQuery.requestFocus()
+ }
+
+ override fun onFeedSearchResultClickListener(result: FeedResult) {
+ showAddFeedDialog(result.url, result.label)
+ }
+
+ override fun addFeedStarted() {
+ // loading views handled by AddFeedFragment
+ }
+
+ private fun setupViews() {
+ setSupportActionBar(binding.toolbar)
+ supportActionBar?.setDisplayShowTitleEnabled(false)
+ supportActionBar?.setDisplayShowHomeEnabled(false)
+
+ adapter = FeedSearchAdapter(this)
+ binding.feedResultList.adapter = adapter
+ }
+
+ private fun setupListeners() {
+ binding.toolbarArrow.setOnClickListener { finish() }
+ binding.toolbarIcon.setOnClickListener { finish() }
+ binding.clearText.setOnClickListener { binding.inputSearchQuery.setText("") }
+
+ binding.inputSearchQuery.addTextChangedListener(object : TextWatcher {
+
+ private val handler = Handler(Looper.getMainLooper())
+ private var searchQueryRunnable: Runnable? = null
+
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(s: Editable) {
+ searchQueryRunnable?.let { handler.removeCallbacks(it) }
+ searchQueryRunnable = Runnable {
+ if (tryAddByURL(s.toString())) {
+ return@Runnable
+ }
+
+ syncClearIconVisibility(s)
+ if (s.isNotEmpty()) searchQuery(s)
+ else syncSearchResults(arrayOf())
+ }
+ handler.postDelayed(searchQueryRunnable!!, 350)
+ }
+ })
+ }
+
+ private fun searchQuery(query: Editable) {
+ object : AsyncTask?>() {
+ override fun onPreExecute() {
+ super.onPreExecute()
+ binding.loadingCircle.visibility = View.VISIBLE
+ binding.clearText.visibility = View.GONE
+ }
+
+ override fun doInBackground(vararg params: Void?): Array? {
+ return apiManager.searchForFeed(query.toString())
+ }
+
+ override fun onPostExecute(result: Array?) {
+ binding.loadingCircle.visibility = View.GONE
+ binding.clearText.visibility = View.VISIBLE
+ syncSearchResults(result ?: arrayOf())
+ }
+
+
+ }.execute()
+ }
+
+ private fun syncClearIconVisibility(query: Editable) {
+ binding.clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE
+ }
+
+ private fun syncSearchResults(results: Array) {
+ adapter.replaceAll(results)
+ }
+
+ /**
+ * See if the text entered in the query field was actually a URL so we can skip the
+ * search step and just let users who know feed URLs directly subscribe.
+ */
+ private fun tryAddByURL(s: String): Boolean {
+ var u: URL? = null
+ try {
+ u = URL(s)
+ } catch (mue: MalformedURLException) {
+ // this just signals that the string wasn't a URL, we will return
+ }
+ if (u == null) {
+ return false
+ }
+ if (!supportedUrlProtocols.contains(u.protocol)) {
+ return false
+ }
+ if (u.host == null || u.host.trim().isEmpty()) {
+ return false
+ }
+ showAddFeedDialog(s, s)
+ return true
+ }
+
+ private fun showAddFeedDialog(feedUrl: String, feedLabel: String) {
+ val addFeedFragment: DialogFragment = AddFeedFragment.newInstance(feedUrl, feedLabel)
+ addFeedFragment.show(supportFragmentManager, "dialog")
+ }
+}
\ No newline at end of file
diff --git a/clients/android/NewsBlur/src/com/newsblur/database/StoryViewAdapter.java b/clients/android/NewsBlur/src/com/newsblur/database/StoryViewAdapter.java
index 19b6dd1b9..beaea59f8 100644
--- a/clients/android/NewsBlur/src/com/newsblur/database/StoryViewAdapter.java
+++ b/clients/android/NewsBlur/src/com/newsblur/database/StoryViewAdapter.java
@@ -704,18 +704,19 @@ public class StoryViewAdapter extends RecyclerView.Adapter 10f) && // the gesture should not start too close to the left edge and
- ((e2.getX()-e1.getX()) > 50f) && // move horizontally to the right and
- (Math.abs(e1.getY()-e2.getY()) < 25f) // have minimal vertical travel, so we don't capture scrolling gestures
- ) {
+ float displayWidthPx = UIUtils.getDisplayWidthPx(context);
+ float edgeWithNavGesturesPaddingPx = UIUtils.dp2px(context, 40);
+ float rightEdgeNavGesturePaddingPx = displayWidthPx - edgeWithNavGesturesPaddingPx;
+ if (e1.getX() > edgeWithNavGesturesPaddingPx && // the gesture should not start too close to the left edge and
+ e2.getX() - e1.getX() > 50f && // move horizontally to the right and
+ Math.abs(distanceY) < 25f) { // have minimal vertical travel, so we don't capture scrolling gestures
vh.gestureL2R = true;
vh.gestureDebounce = true;
return true;
}
- if ((e1.getX() > 10f) && // the gesture should not start too close to the left edge and
- ((e1.getX()-e2.getX()) > 50f) && // move horizontally to the left and
- (Math.abs(e1.getY()-e2.getY()) < 25f) // have minimal vertical travel, so we don't capture scrolling gestures
- ) {
+ if (e1.getX() < rightEdgeNavGesturePaddingPx && // the gesture should not start too close to the right edge and
+ e1.getX() - e2.getX() > 50f && // move horizontally to the left and
+ Math.abs(distanceY) < 25f) { // have minimal vertical travel, so we don't capture scrolling gestures
vh.gestureR2L = true;
vh.gestureDebounce = true;
return true;
diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/ReadingItemFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/ReadingItemFragment.java
index 06bb87e57..26c7c5c4c 100644
--- a/clients/android/NewsBlur/src/com/newsblur/fragment/ReadingItemFragment.java
+++ b/clients/android/NewsBlur/src/com/newsblur/fragment/ReadingItemFragment.java
@@ -28,7 +28,6 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.WebView.HitTestResult;
-import android.widget.ImageView;
import android.widget.TextView;
import com.newsblur.R;
@@ -52,7 +51,6 @@ import com.newsblur.util.PrefsUtils;
import com.newsblur.util.StoryChangesState;
import com.newsblur.util.StoryUtils;
import com.newsblur.util.UIUtils;
-import com.newsblur.view.ReadingScrollView;
import java.util.HashMap;
import java.util.regex.Matcher;
@@ -66,12 +64,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
public static final String READING_FONT_CHANGED = "readingFontChanged";
public Story story;
private FeedSet fs;
- private LayoutInflater inflater;
private String feedColor, feedTitle, feedFade, feedBorder, feedIconUrl, faviconText;
private Classifier classifier;
private BroadcastReceiver textSizeReceiver, readingFontReceiver;
private boolean displayFeedDetails;
- private View view;
private UserDetails user;
private DefaultFeedView selectedFeedView;
private boolean textViewUnavailable;
@@ -163,7 +159,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
getActivity().unregisterReceiver(textSizeReceiver);
getActivity().unregisterReceiver(readingFontReceiver);
binding.readingWebview.setOnTouchListener(null);
- view.setOnTouchListener(null);
+ binding.getRoot().setOnTouchListener(null);
getActivity().getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(null);
super.onDestroy();
}
@@ -184,8 +180,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- this.inflater = inflater;
- view = inflater.inflate(R.layout.fragment_readingitem, null);
+ View view = inflater.inflate(R.layout.fragment_readingitem, container, false);
binding = FragmentReadingitemBinding.bind(view);
itemCommentBinding = IncludeReadingItemCommentBinding.bind(binding.getRoot());
@@ -196,7 +191,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
registerForContextMenu(binding.readingWebview);
binding.readingWebview.setCustomViewLayout(binding.customViewContainer);
- binding.readingWebview.setWebviewWrapperLayout(binding.readingScrollview);
+ binding.readingWebview.setWebviewWrapperLayout(binding.readingContainer);
binding.readingWebview.setBackgroundColor(Color.TRANSPARENT);
binding.readingWebview.fragment = this;
binding.readingWebview.activity = activity;
@@ -206,8 +201,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
updateSaveButton();
setupItemCommentsAndShares();
- ReadingScrollView scrollView = (ReadingScrollView) view.findViewById(R.id.reading_scrollview);
- scrollView.registerScrollChangeListener(activity);
+ binding.readingScrollview.registerScrollChangeListener(activity);
return view;
}
@@ -417,15 +411,10 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
}
private void setupItemCommentsAndShares() {
- new SetupCommentSectionTask(this, view, inflater, story).execute();
+ new SetupCommentSectionTask(this, binding.getRoot(), getLayoutInflater(), story).execute();
}
private void setupItemMetadata() {
- View feedHeader = view.findViewById(R.id.row_item_feed_header);
- View feedHeaderBorder = view.findViewById(R.id.item_feed_border);
- TextView itemDate = (TextView) view.findViewById(R.id.reading_item_date);
- ImageView feedIcon = (ImageView) view.findViewById(R.id.reading_feed_icon);
-
if ((feedColor == null) ||
(feedFade == null) ||
TextUtils.equals(feedColor, "null") ||
@@ -440,8 +429,8 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
Color.parseColor("#" + feedFade),
};
GradientDrawable gradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, colors);
- UIUtils.setViewBackground(feedHeader, gradient);
- feedHeaderBorder.setBackgroundColor(Color.parseColor("#" + feedBorder));
+ UIUtils.setViewBackground(binding.rowItemFeedHeader, gradient);
+ binding.itemFeedBorder.setBackgroundColor(Color.parseColor("#" + feedBorder));
if (TextUtils.equals(faviconText, "black")) {
binding.readingFeedTitle.setTextColor(UIUtils.getColor(getActivity(), R.color.text));
@@ -453,13 +442,13 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
if (!displayFeedDetails) {
binding.readingFeedTitle.setVisibility(View.GONE);
- feedIcon.setVisibility(View.GONE);
+ binding.readingFeedIcon.setVisibility(View.GONE);
} else {
- FeedUtils.iconLoader.displayImage(feedIconUrl, feedIcon, 0, false);
+ FeedUtils.iconLoader.displayImage(feedIconUrl, binding.readingFeedIcon, 0, false);
binding.readingFeedTitle.setText(feedTitle);
}
- itemDate.setText(StoryUtils.formatLongDate(getActivity(), story.timestamp));
+ binding.readingItemDate.setText(StoryUtils.formatLongDate(getActivity(), story.timestamp));
if (story.tags.length <= 0) {
binding.readingItemTags.setVisibility(View.GONE);
@@ -510,7 +499,7 @@ public class ReadingItemFragment extends NbFragment implements PopupMenu.OnMenuI
// 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
// is in full release.
- View v = inflater.inflate(R.layout.tag_view, null);
+ View v = getLayoutInflater().inflate(R.layout.tag_view, null);
TextView tagText = (TextView) v.findViewById(R.id.tag_text);
tagText.setText(tag);
diff --git a/clients/android/NewsBlur/src/com/newsblur/network/SearchAsyncTaskLoader.java b/clients/android/NewsBlur/src/com/newsblur/network/SearchAsyncTaskLoader.java
deleted file mode 100644
index bb80c2860..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/network/SearchAsyncTaskLoader.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.newsblur.network;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import androidx.loader.content.AsyncTaskLoader;
-
-import com.newsblur.domain.FeedResult;
-
-public class SearchAsyncTaskLoader extends AsyncTaskLoader {
-
- public static final String SEARCH_TERM = "searchTerm";
-
- private String searchTerm;
- private APIManager apiManager;
-
- public SearchAsyncTaskLoader(Context context, String searchTerm) {
- super(context);
- this.searchTerm = searchTerm;
- apiManager = new APIManager(context);
- }
-
- @Override
- public SearchLoaderResponse loadInBackground() {
- ArrayList list = new ArrayList();
- FeedResult[] results = apiManager.searchForFeed(searchTerm);
- if (results != null) {
- for (FeedResult result : results) {
- list.add(result);
- }
- }
- SearchLoaderResponse response = new SearchLoaderResponse(list);
- return response;
- }
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/network/SearchLoaderResponse.java b/clients/android/NewsBlur/src/com/newsblur/network/SearchLoaderResponse.java
deleted file mode 100644
index 3e2ede9f2..000000000
--- a/clients/android/NewsBlur/src/com/newsblur/network/SearchLoaderResponse.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.newsblur.network;
-
-import java.util.ArrayList;
-
-import com.newsblur.domain.FeedResult;
-
-public class SearchLoaderResponse extends BaseLoaderResponse {
-
- private ArrayList results;
-
- /**
- * Use to indicate there was a problem w/ the search
- *
- * @param errorMessage
- */
- public SearchLoaderResponse(String errorMessage) {
- super(errorMessage);
- results = new ArrayList(0);
- }
-
- public SearchLoaderResponse(ArrayList results) {
- this.results = results;
- }
-
- public ArrayList getResults() {
- return results;
- }
-
-
-}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
index b4f464a4b..e4785d984 100644
--- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
+++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java
@@ -258,8 +258,8 @@ public class FeedUtils {
Set impactedFeeds = dbHelper.setStoryReadState(story, read);
NbActivity.updateAllActivities(NbActivity.UPDATE_STORY);
- triggerSync(context);
NBSyncService.addRecountCandidates(impactedFeeds);
+ triggerSync(context);
}
/**
@@ -398,23 +398,24 @@ public class FeedUtils {
}
public static void sendStoryBrief(Story story, Context context) {
- if (story == null ) { return; }
- Intent intent = new Intent(android.content.Intent.ACTION_SEND);
+ if (story == null) return;
+ Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(Intent.EXTRA_SUBJECT, UIUtils.fromHtml(story.title).toString());
- intent.putExtra(Intent.EXTRA_TEXT, String.format(context.getResources().getString(R.string.send_brief), new Object[]{UIUtils.fromHtml(story.title), story.permalink}));
+ intent.putExtra(Intent.EXTRA_SUBJECT, story.title);
+ intent.putExtra(Intent.EXTRA_TEXT, String.format(context.getResources().getString(R.string.send_brief), story.title, story.permalink));
context.startActivity(Intent.createChooser(intent, "Send using"));
}
public static void sendStoryFull(Story story, Context context) {
- if (story == null ) { return; }
- String body = getStoryContent(story.storyHash);
- Intent intent = new Intent(android.content.Intent.ACTION_SEND);
+ if (story == null) return;
+ String body = getStoryText(story.storyHash);
+ if (TextUtils.isEmpty(body)) body = getStoryContent(story.storyHash);
+ Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(Intent.EXTRA_SUBJECT, UIUtils.fromHtml(story.title).toString());
- intent.putExtra(Intent.EXTRA_TEXT, String.format(context.getResources().getString(R.string.send_full), new Object[]{story.permalink, UIUtils.fromHtml(story.title), UIUtils.fromHtml(body)}));
+ intent.putExtra(Intent.EXTRA_SUBJECT, story.title);
+ intent.putExtra(Intent.EXTRA_TEXT, String.format(context.getResources().getString(R.string.send_full), story.title, story.permalink, UIUtils.fromHtml(body)));
context.startActivity(Intent.createChooser(intent, "Send using"));
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/UIUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/UIUtils.java
index d4ddf68cc..88d5f4420 100644
--- a/clients/android/NewsBlur/src/com/newsblur/util/UIUtils.java
+++ b/clients/android/NewsBlur/src/com/newsblur/util/UIUtils.java
@@ -4,7 +4,6 @@ import java.io.File;
import java.util.Map;
import static android.graphics.Bitmap.Config.ARGB_8888;
-import static com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS;
import static com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
import static com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP;
@@ -182,6 +181,10 @@ public class UIUtils {
return ((float) px) / context.getResources().getDisplayMetrics().density;
}
+ public static float getDisplayWidthPx(Context context) {
+ return context.getResources().getDisplayMetrics().widthPixels;
+ }
+
/**
* Sets the alpha of a view, totally hiding the view if the alpha is so low
* as to be invisible, but also obeying intended visibility.
@@ -218,7 +221,7 @@ public class UIUtils {
// enabled scrolling app bar only for reading
if (activity instanceof Reading) {
AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
- p.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_ENTER_ALWAYS | SCROLL_FLAG_SNAP);
+ p.setScrollFlags(SCROLL_FLAG_SCROLL | SCROLL_FLAG_SNAP);
toolbar.setLayoutParams(p);
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/view/SelectOnlyEditText.java b/clients/android/NewsBlur/src/com/newsblur/view/SelectOnlyEditText.java
index fd5a7d181..960a5973b 100644
--- a/clients/android/NewsBlur/src/com/newsblur/view/SelectOnlyEditText.java
+++ b/clients/android/NewsBlur/src/com/newsblur/view/SelectOnlyEditText.java
@@ -8,9 +8,11 @@ import android.view.MenuItem;
import android.widget.EditText;
import android.widget.Toast;
+import androidx.appcompat.widget.AppCompatEditText;
+
import com.newsblur.R;
-public class SelectOnlyEditText extends EditText {
+public class SelectOnlyEditText extends AppCompatEditText {
private Context context;
private boolean forceSelection = false;
@@ -31,11 +33,6 @@ public class SelectOnlyEditText extends EditText {
this.context = context;
}
- public SelectOnlyEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- this.context = context;
- }
-
public void disableActionMenu() {
this.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
@@ -61,6 +58,7 @@ public class SelectOnlyEditText extends EditText {
@Override
protected void onSelectionChanged(int start, int end) {
+ super.onSelectionChanged(start, end);
if (forceSelection && (start == end)) {
selectAll();
if (context != null) {
diff --git a/clients/android/NewsBlur/src/com/newsblur/widget/WidgetUtils.java b/clients/android/NewsBlur/src/com/newsblur/widget/WidgetUtils.java
index d1a1a70ca..95614f45f 100644
--- a/clients/android/NewsBlur/src/com/newsblur/widget/WidgetUtils.java
+++ b/clients/android/NewsBlur/src/com/newsblur/widget/WidgetUtils.java
@@ -8,7 +8,6 @@ import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
-import com.newsblur.R;
import com.newsblur.util.Log;
import com.newsblur.util.PrefsUtils;