diff options
Diffstat (limited to 'org.fox.ttrss/src/main/java/org/fox')
38 files changed, 4900 insertions, 4803 deletions
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java index e472a6b0..688656e9 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiCommon.java @@ -47,15 +47,19 @@ public class ApiCommon { public interface ApiCaller { void setStatusCode(int statusCode); + void setLastError(ApiError lastError); + void setLastErrorMessage(String message); + void notifyProgress(int progress); } public enum ApiError { SUCCESS, UNKNOWN_ERROR, HTTP_UNAUTHORIZED, HTTP_FORBIDDEN, HTTP_NOT_FOUND, HTTP_BAD_REQUEST, HTTP_SERVER_ERROR, HTTP_OTHER_ERROR, SSL_REJECTED, SSL_HOSTNAME_REJECTED, PARSE_ERROR, IO_ERROR, OTHER_ERROR, API_DISABLED, - API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD } + API_UNKNOWN, LOGIN_FAILED, INVALID_URL, API_INCORRECT_USAGE, NETWORK_UNAVAILABLE, API_UNKNOWN_METHOD + } public static int getErrorMessage(ApiError error) { switch (error) { @@ -298,15 +302,18 @@ public class ApiCommon { this.progressListener = progressListener; } - @Override public MediaType contentType() { + @Override + public MediaType contentType() { return responseBody.contentType(); } - @Override public long contentLength() { + @Override + public long contentLength() { return responseBody.contentLength(); } - @Override public BufferedSource source() { + @Override + public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } @@ -316,7 +323,9 @@ public class ApiCommon { private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; - @Override public long read(Buffer sink, long byteCount) throws IOException { + + @Override + public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); long fullLength = responseBody.contentLength(); if (bytesRead == -1) { // this source is exhausted @@ -338,9 +347,9 @@ public class ApiCommon { return String.format(Locale.ENGLISH, "Tiny Tiny RSS (Android) %1$s (%2$d) %3$s", - packageInfo.versionName, - packageInfo.versionCode, - System.getProperty("http.agent")); + packageInfo.versionName, + packageInfo.versionCode, + System.getProperty("http.agent")); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java index bc219dd7..c213dd86 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiRequest.java @@ -10,52 +10,52 @@ import com.google.gson.JsonElement; import java.util.HashMap; -public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonElement> implements ApiCommon.ApiCaller { - - protected int m_apiStatusCode = 0; - - private final Context m_context; - protected String m_lastErrorMessage; - - protected ApiError m_lastError; - - public ApiRequest(Context context) { - m_context = context; - m_lastError = ApiError.UNKNOWN_ERROR; - } - - @SuppressLint("NewApi") - @SuppressWarnings("unchecked") - public void execute(HashMap<String,String> map) { - super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, map); - } - - public int getErrorMessage() { - return ApiCommon.getErrorMessage(m_lastError); - } - - @Override - protected JsonElement doInBackground(HashMap<String, String>... params) { - return ApiCommon.performRequest(m_context, params[0], this); - } - - @Override - public void setStatusCode(int statusCode) { - m_apiStatusCode = statusCode; - } - - @Override - public void setLastError(ApiError lastError) { - m_lastError = lastError; - } - - @Override - public void setLastErrorMessage(String message) { - m_lastErrorMessage = message; - } - - @Override - public void notifyProgress(int progress) { - publishProgress(progress); - } +public class ApiRequest extends AsyncTask<HashMap<String, String>, Integer, JsonElement> implements ApiCommon.ApiCaller { + + protected int m_apiStatusCode = 0; + + private final Context m_context; + protected String m_lastErrorMessage; + + protected ApiError m_lastError; + + public ApiRequest(Context context) { + m_context = context; + m_lastError = ApiError.UNKNOWN_ERROR; + } + + @SuppressLint("NewApi") + @SuppressWarnings("unchecked") + public void execute(HashMap<String, String> map) { + super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, map); + } + + public int getErrorMessage() { + return ApiCommon.getErrorMessage(m_lastError); + } + + @Override + protected JsonElement doInBackground(HashMap<String, String>... params) { + return ApiCommon.performRequest(m_context, params[0], this); + } + + @Override + public void setStatusCode(int statusCode) { + m_apiStatusCode = statusCode; + } + + @Override + public void setLastError(ApiError lastError) { + m_lastError = lastError; + } + + @Override + public void setLastErrorMessage(String message) { + m_lastErrorMessage = message; + } + + @Override + public void notifyProgress(int progress) { + publishProgress(progress); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java index e6613f03..9ae2ffc6 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/Application.java @@ -20,102 +20,104 @@ import java.util.List; public class Application extends android.app.Application { - private static Application m_singleton; - - private String m_sessionId; - private int m_apiLevel; - public LinkedHashMap<String, String> m_customSortModes = new LinkedHashMap<>(); - ConnectivityManager m_cmgr; - ArticleModel m_articleModel; - - public static Application getInstance(){ - return m_singleton; - } - - public static List<Article> getArticles() { - return getInstance().m_articleModel.getArticles().getValue(); - } - - public static ArticleModel getArticlesModel() { - return getInstance().m_articleModel; - } - - @Override - public final void onCreate() { - super.onCreate(); - - DynamicColors.applyToActivitiesIfAvailable(this); - - m_singleton = this; - m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - m_articleModel = new ArticleModel(this); - } - - public String getSessionId() { - return m_sessionId; - } - - public void setSessionId(String sessionId) { - m_sessionId = sessionId; - } - - public int getApiLevel() { - return m_apiLevel; - } - - public void setApiLevel(int apiLevel) { - m_apiLevel = apiLevel; - } - - public void save(Bundle out) { - - out.setClassLoader(getClass().getClassLoader()); - out.putString("gs:sessionId", m_sessionId); - out.putInt("gs:apiLevel", m_apiLevel); - out.putSerializable("gs:customSortTypes", m_customSortModes); - } - - /** @noinspection unchecked*/ + private static Application m_singleton; + + private String m_sessionId; + private int m_apiLevel; + public LinkedHashMap<String, String> m_customSortModes = new LinkedHashMap<>(); + ConnectivityManager m_cmgr; + ArticleModel m_articleModel; + + public static Application getInstance() { + return m_singleton; + } + + public static List<Article> getArticles() { + return getInstance().m_articleModel.getArticles().getValue(); + } + + public static ArticleModel getArticlesModel() { + return getInstance().m_articleModel; + } + + @Override + public final void onCreate() { + super.onCreate(); + + DynamicColors.applyToActivitiesIfAvailable(this); + + m_singleton = this; + m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + m_articleModel = new ArticleModel(this); + } + + public String getSessionId() { + return m_sessionId; + } + + public void setSessionId(String sessionId) { + m_sessionId = sessionId; + } + + public int getApiLevel() { + return m_apiLevel; + } + + public void setApiLevel(int apiLevel) { + m_apiLevel = apiLevel; + } + + public void save(Bundle out) { + + out.setClassLoader(getClass().getClassLoader()); + out.putString("gs:sessionId", m_sessionId); + out.putInt("gs:apiLevel", m_apiLevel); + out.putSerializable("gs:customSortTypes", m_customSortModes); + } + + /** + * @noinspection unchecked + */ public void load(Bundle in) { - if (in != null) { - m_sessionId = in.getString("gs:sessionId"); - m_apiLevel = in.getInt("gs:apiLevel"); - - HashMap<String, String> tmp = (HashMap<String, String>) in.getSerializable("gs:customSortTypes"); - - m_customSortModes.clear(); - m_customSortModes.putAll(tmp); - } - } - - public boolean isWifiConnected() { - NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - - if (wifi != null) - return wifi.isConnected(); - - return false; - } - - @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(base); - - if (!BuildConfig.DEBUG) - ACRA.init(this, new CoreConfigurationBuilder() - .withBuildConfigClass(BuildConfig.class) - .withReportFormat(StringFormat.KEY_VALUE_LIST) - .withPluginConfigurations( - new DialogConfigurationBuilder() - .withText(getString(R.string.crash_dialog_text_email)) - .withResTheme(R.style.Theme_AppCompat_Dialog) - .build(), - new MailSenderConfigurationBuilder() - .withMailTo("cthulhoo+ttrss-acra@gmail.com") - .withReportAsFile(true) - .withReportFileName("crash.txt") - .build() - ) - .build()); - } + if (in != null) { + m_sessionId = in.getString("gs:sessionId"); + m_apiLevel = in.getInt("gs:apiLevel"); + + HashMap<String, String> tmp = (HashMap<String, String>) in.getSerializable("gs:customSortTypes"); + + m_customSortModes.clear(); + m_customSortModes.putAll(tmp); + } + } + + public boolean isWifiConnected() { + NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + + if (wifi != null) + return wifi.isConnected(); + + return false; + } + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + + if (!BuildConfig.DEBUG) + ACRA.init(this, new CoreConfigurationBuilder() + .withBuildConfigClass(BuildConfig.class) + .withReportFormat(StringFormat.KEY_VALUE_LIST) + .withPluginConfigurations( + new DialogConfigurationBuilder() + .withText(getString(R.string.crash_dialog_text_email)) + .withResTheme(R.style.Theme_AppCompat_Dialog) + .build(), + new MailSenderConfigurationBuilder() + .withMailTo("cthulhoo+ttrss-acra@gmail.com") + .withReportAsFile(true) + .withReportFileName("crash.txt") + .build() + ) + .build()); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java index 2c11b929..70f0f232 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleFragment.java @@ -34,49 +34,49 @@ import java.text.SimpleDateFormat; import java.util.Date; // TODO: add ability to update already rendered contents from article somehow (to refresh note, etc) -public class ArticleFragment extends androidx.fragment.app.Fragment { - private final String TAG = this.getClass().getSimpleName(); +public class ArticleFragment extends androidx.fragment.app.Fragment { + private final String TAG = this.getClass().getSimpleName(); - private SharedPreferences m_prefs; + private SharedPreferences m_prefs; protected Article m_article; - private DetailActivity m_activity; + private DetailActivity m_activity; private WebView m_web; //protected View m_fab; protected int m_articleFontSize; protected int m_articleSmallFontSize; public void initialize(Article article) { - m_article = article; - } + m_article = article; + } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { - if (v.getId() == R.id.article_content) { - HitTestResult result = ((WebView)v).getHitTestResult(); + if (v.getId() == R.id.article_content) { + HitTestResult result = ((WebView) v).getHitTestResult(); - if (result.getType() == HitTestResult.IMAGE_TYPE || result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { + if (result.getType() == HitTestResult.IMAGE_TYPE || result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { - menu.setHeaderTitle(result.getExtra()); - getActivity().getMenuInflater().inflate(R.menu.content_gallery_entry, menu); + menu.setHeaderTitle(result.getExtra()); + getActivity().getMenuInflater().inflate(R.menu.content_gallery_entry, menu); - /* FIXME I have no idea how to do this correctly ;( */ + /* FIXME I have no idea how to do this correctly ;( */ - m_activity.setLastContentImageHitTestUrl(result.getExtra()); + m_activity.setLastContentImageHitTestUrl(result.getExtra()); - } else { - menu.setHeaderTitle(m_article.title); - getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); - } - } else { - menu.setHeaderTitle(m_article.title); - getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); - } + } else { + menu.setHeaderTitle(m_article.title); + getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); + } + } else { + menu.setHeaderTitle(m_article.title); + getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); + } - super.onCreateContextMenu(menu, v, menuInfo); + super.onCreateContextMenu(menu, v, menuInfo); - } + } @Override public void onCreate(Bundle savedInstanceState) { @@ -87,15 +87,15 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { } } - @SuppressLint({"NewApi", "SimpleDateFormat"}) - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, final Bundle savedInstanceState) { + @SuppressLint({"NewApi", "SimpleDateFormat"}) + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, final Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.fragment_article, container, false); + final View view = inflater.inflate(R.layout.fragment_article, container, false); - // couldn't reinitialize state properly, might as well bail out - if (m_article == null) { - m_activity.finish(); + // couldn't reinitialize state properly, might as well bail out + if (m_article == null) { + m_activity.finish(); } m_articleFontSize = m_prefs.getInt("article_font_size_sp_int", 16); @@ -186,7 +186,7 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { dv.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_articleSmallFontSize); Date d = new Date(m_article.updated * 1000L); - long half_a_year_ago = System.currentTimeMillis()/1000L - 182*24*60*60; + long half_a_year_ago = System.currentTimeMillis() / 1000L - 182 * 24 * 60 * 60; DateFormat df; if (m_article.updated < half_a_year_ago) df = new SimpleDateFormat("MMM dd, yyyy"); @@ -218,22 +218,23 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { m_web = view.findViewById(R.id.article_content); m_web.setWebViewClient(new WebViewClient() { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - try { - m_activity.openUri(Uri.parse(url)); + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + try { + m_activity.openUri(Uri.parse(url)); - return true; + return true; - } catch (Exception e){ - e.printStackTrace(); - } + } catch (Exception e) { + e.printStackTrace(); + } - return false; - } }); + return false; + } + }); m_web.setOnLongClickListener(v -> { - HitTestResult result = ((WebView)v).getHitTestResult(); + HitTestResult result = ((WebView) v).getHitTestResult(); if (result.getType() == HitTestResult.IMAGE_TYPE || result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { registerForContextMenu(m_web); @@ -248,7 +249,7 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { renderContent(savedInstanceState); return view; - } + } protected void renderContent(Bundle savedInstanceState) { if (!isAdded() || m_web == null) return; @@ -263,13 +264,13 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { String textColor = String.format("#%06X", (0xFFFFFF & tvTextColor.data)); - String cssOverride = "body { color : "+textColor+"; }"; + String cssOverride = "body { color : " + textColor + "; }"; TypedValue tvColorPrimary = new TypedValue(); getActivity().getTheme().resolveAttribute(R.attr.colorPrimary, tvColorPrimary, true); String linkHexColor = String.format("#%06X", (0xFFFFFF & tvColorPrimary.data)); - cssOverride += " a:link {color: "+linkHexColor+";} a:visited { color: "+linkHexColor+";}"; + cssOverride += " a:link {color: " + linkHexColor + ";} a:visited { color: " + linkHexColor + ";}"; String articleContent = m_article.content; @@ -295,7 +296,7 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { "<meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\">" + "<meta name=\"viewport\" content=\"width=device-width, user-scalable=no\" />" + "<style type=\"text/css\">" + - "body { padding : 0px; margin : "+margin8dp+"px; line-height : 1.3; word-wrap: break-word; }" + + "body { padding : 0px; margin : " + margin8dp + "px; line-height : 1.3; word-wrap: break-word; }" + "h1, h2, h3, h4, h5, h6 { line-height: 1; text-align: initial; }" + "img, video, iframe { max-width : 100%; width : auto; height : auto; }" + " table { width : 100%; }" + @@ -374,14 +375,14 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { if (m_web != null) m_web.onResume(); } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_activity = (DetailActivity)activity; + m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); + m_activity = (DetailActivity) activity; - } + } @Override public void onSaveInstanceState(Bundle out) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java index 98d9e8ba..9f4d8e1f 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticleModel.java @@ -31,7 +31,8 @@ import java.util.stream.Collectors; public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCaller { private final String TAG = this.getClass().getSimpleName(); - @NonNull private final MutableLiveData<List<Article>> m_articles = new MutableLiveData<>(new ArrayList<Article>()); + @NonNull + private final MutableLiveData<List<Article>> m_articles = new MutableLiveData<>(new ArrayList<Article>()); private SharedPreferences m_prefs; private final int m_responseCode = 0; protected String m_responseMessage; @@ -92,7 +93,9 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle return m_activeArticle; } - /** returns null if there's none or it is invalid (missing in list) */ + /** + * returns null if there's none or it is invalid (missing in list) + */ public Article getActiveArticle() { List<Article> articles = m_articles.getValue(); @@ -159,7 +162,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle } } - public enum ArticlesSelection { ALL, NONE, UNREAD } + public enum ArticlesSelection {ALL, NONE, UNREAD} public void setSelection(@NonNull ArticlesSelection select) { List<Article> articles = m_articles.getValue(); @@ -188,7 +191,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle final boolean allowForceUpdate = org.fox.ttrss.Application.getInstance().getApiLevel() >= 9 && !m_feed.is_cat && m_feed.id > 0 && !m_append && skip == 0; - HashMap<String,String> params = new HashMap<>(); + HashMap<String, String> params = new HashMap<>(); params.put("op", "getHeadlines"); params.put("sid", org.fox.ttrss.Application.getInstance().getSessionId()); @@ -259,10 +262,12 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle Log.d(TAG, this + " firstID=" + m_firstId + " firstIdChanged=" + m_firstIdChanged); - Type listType = new TypeToken<List<Article>>() {}.getType(); + Type listType = new TypeToken<List<Article>>() { + }.getType(); articlesJson = new Gson().fromJson(content.get(1), listType); } else { - Type listType = new TypeToken<List<Article>>() {}.getType(); + Type listType = new TypeToken<List<Article>>() { + }.getType(); articlesJson = new Gson().fromJson(content, listType); } @@ -287,7 +292,7 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle } if (m_amountLoaded < Integer.parseInt(m_prefs.getString("headlines_request_size", "15"))) { - Log.d(TAG, this + " amount loaded "+m_amountLoaded+" < request size, disabling lazy load"); + Log.d(TAG, this + " amount loaded " + m_amountLoaded + " < request size, disabling lazy load"); m_lazyLoadEnabled = false; } @@ -415,11 +420,15 @@ public class ArticleModel extends AndroidViewModel implements ApiCommon.ApiCalle public List<Article> getUnread(List<Article> articles) { - return articles.stream().filter(a -> { return a.unread; }).collect(Collectors.toList()); + return articles.stream().filter(a -> { + return a.unread; + }).collect(Collectors.toList()); } public List<Article> getSelected() { - return m_articles.getValue().stream().filter(a -> { return a.selected; }).collect(Collectors.toList()); + return m_articles.getValue().stream().filter(a -> { + return a.selected; + }).collect(Collectors.toList()); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java index 7632ea19..c2eeba49 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ArticlePager.java @@ -21,144 +21,144 @@ import java.util.ArrayList; public class ArticlePager extends androidx.fragment.app.Fragment { - private final String TAG = this.getClass().getSimpleName(); - private PagerAdapter m_adapter; - private HeadlinesEventListener m_listener; - private OnlineActivity m_activity; - private Feed m_feed; - private ViewPager2 m_pager; + private final String TAG = this.getClass().getSimpleName(); + private PagerAdapter m_adapter; + private HeadlinesEventListener m_listener; + private OnlineActivity m_activity; + private Feed m_feed; + private ViewPager2 m_pager; - private static class PagerAdapter extends DiffFragmentStateAdapter<Article> { - - public PagerAdapter(@NonNull Fragment fragment) { - super(fragment, new ArticleDiffItemCallback()); - } - - private void syncToSharedArticles() { - submitList(new ArrayList<>(Application.getArticles())); - } - - @Override - @NonNull - public Fragment createFragment(int position) { - Article article = getItem(position); + private static class PagerAdapter extends DiffFragmentStateAdapter<Article> { - ArticleFragment af = new ArticleFragment(); - af.initialize(article); + public PagerAdapter(@NonNull Fragment fragment) { + super(fragment, new ArticleDiffItemCallback()); + } - return af; - } - } - - public void initialize(int articleId, Feed feed) { - m_feed = feed; - } + private void syncToSharedArticles() { + submitList(new ArrayList<>(Application.getArticles())); + } - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); + @Override + @NonNull + public Fragment createFragment(int position) { + Article article = getItem(position); - out.putParcelable("m_feed", m_feed); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (savedInstanceState != null) { - m_feed = savedInstanceState.getParcelable("m_feed"); - } - - setRetainInstance(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - - m_adapter = new PagerAdapter(this); - m_adapter.submitList(Application.getArticles()); - - ArticleModel model = Application.getArticlesModel(); - - // deal with further updates - model.getArticles().observe(getActivity(), articles -> { - Log.d(TAG, "observed article list size=" + articles.size()); - m_adapter.submitList(articles); - }); - - model.getActive().observe(getActivity(), (activeArticle) -> { - Log.d(TAG, "observed active article=" + activeArticle); - - if (activeArticle != null) { - int position = model.getArticles().getValue().indexOf(activeArticle); - - if (position != -1 && position != m_pager.getCurrentItem()) - m_pager.setCurrentItem(position, false); - } - }); - - m_pager = view.findViewById(R.id.article_pager); - - m_pager.setAdapter(m_adapter); - m_pager.setOffscreenPageLimit(3); - - m_pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - Log.d(TAG, "onPageSelected: " + position); - - // wtf - if (position != -1) { - Article article = Application.getArticles().get(position); - - m_listener.onArticleSelected(article); - } - } - }); - - return view; - } - - @Override - public void onAttach(@NonNull Activity activity) { - super.onAttach(activity); - - m_listener = (HeadlinesEventListener)activity; - m_activity = (OnlineActivity)activity; - } + ArticleFragment af = new ArticleFragment(); + af.initialize(article); - @SuppressLint("NewApi") - @Override - public void onResume() { - super.onResume(); + return af; + } + } - m_activity.invalidateOptionsMenu(); - } + public void initialize(int articleId, Feed feed) { + m_feed = feed; + } - public void switchToArticle(boolean next) { - int position = m_pager.getCurrentItem(); + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); - if (position != -1) { + out.putParcelable("m_feed", m_feed); + } - if (next) - position++; - else - position--; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - try { - Article targetArticle = Application.getArticles().get(position); + if (savedInstanceState != null) { + m_feed = savedInstanceState.getParcelable("m_feed"); + } - Application.getArticlesModel().setActive(targetArticle); + setRetainInstance(true); + } - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - } - } + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - public void syncToSharedArticles() { - if (m_adapter != null) - m_adapter.syncToSharedArticles(); - } + m_adapter = new PagerAdapter(this); + m_adapter.submitList(Application.getArticles()); + + ArticleModel model = Application.getArticlesModel(); + + // deal with further updates + model.getArticles().observe(getActivity(), articles -> { + Log.d(TAG, "observed article list size=" + articles.size()); + m_adapter.submitList(articles); + }); + + model.getActive().observe(getActivity(), (activeArticle) -> { + Log.d(TAG, "observed active article=" + activeArticle); + + if (activeArticle != null) { + int position = model.getArticles().getValue().indexOf(activeArticle); + + if (position != -1 && position != m_pager.getCurrentItem()) + m_pager.setCurrentItem(position, false); + } + }); + + m_pager = view.findViewById(R.id.article_pager); + + m_pager.setAdapter(m_adapter); + m_pager.setOffscreenPageLimit(3); + + m_pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + Log.d(TAG, "onPageSelected: " + position); + + // wtf + if (position != -1) { + Article article = Application.getArticles().get(position); + + m_listener.onArticleSelected(article); + } + } + }); + + return view; + } + + @Override + public void onAttach(@NonNull Activity activity) { + super.onAttach(activity); + + m_listener = (HeadlinesEventListener) activity; + m_activity = (OnlineActivity) activity; + } + + @SuppressLint("NewApi") + @Override + public void onResume() { + super.onResume(); + + m_activity.invalidateOptionsMenu(); + } + + public void switchToArticle(boolean next) { + int position = m_pager.getCurrentItem(); + + if (position != -1) { + + if (next) + position++; + else + position--; + + try { + Article targetArticle = Application.getArticles().get(position); + + Application.getArticlesModel().setActive(targetArticle); + + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + } + } + + public void syncToSharedArticles() { + if (m_adapter != null) + m_adapter.syncToSharedArticles(); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java index 03bc6b53..69eed3bf 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/CommonActivity.java @@ -61,402 +61,402 @@ import java.util.Arrays; import java.util.List; public class CommonActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener { - private final String TAG = this.getClass().getSimpleName(); - - public final static String FRAG_HEADLINES = "headlines"; - public final static String FRAG_ARTICLE = "article"; - public final static String FRAG_FEEDS = "feeds"; - public final static String FRAG_CATS = "cats"; - - public final static String THEME_DEFAULT = "THEME_FOLLOW_DEVICE"; - - public final static String NOTIFICATION_CHANNEL_NORMAL = "channel_normal"; - public final static String NOTIFICATION_CHANNEL_PRIORITY = "channel_priority"; - - public static final int EXCERPT_MAX_LENGTH = 256; - public static final int LABEL_BASE_INDEX = -1024; - - public static final int PENDING_INTENT_CHROME_SHARE = 1; - - private boolean m_smallScreenMode = true; - protected String m_theme; - private boolean m_needRestart; - - private static String s_customTabPackageName; - - static final String STABLE_PACKAGE = "com.android.chrome"; - static final String BETA_PACKAGE = "com.chrome.beta"; - static final String DEV_PACKAGE = "com.chrome.dev"; - static final String LOCAL_PACKAGE = "com.google.android.apps.chrome"; - private static final String ACTION_CUSTOM_TABS_CONNECTION = - "android.support.customtabs.action.CustomTabsService"; - - private static String getCustomTabPackageName(Context context) { - if (s_customTabPackageName != null) return s_customTabPackageName; - - PackageManager pm = context.getPackageManager(); - // Get default VIEW intent handler. - Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); - ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0); - String defaultViewHandlerPackageName = null; - if (defaultViewHandlerInfo != null) { - defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName; - } - - // Get all apps that can handle VIEW intents. - List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0); - List<String> packagesSupportingCustomTabs = new ArrayList<>(); - for (ResolveInfo info : resolvedActivityList) { - Intent serviceIntent = new Intent(); - serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION); - serviceIntent.setPackage(info.activityInfo.packageName); - if (pm.resolveService(serviceIntent, 0) != null) { - packagesSupportingCustomTabs.add(info.activityInfo.packageName); - } - } - - // Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents - // and service calls. - if (packagesSupportingCustomTabs.isEmpty()) { - s_customTabPackageName = null; - } else if (packagesSupportingCustomTabs.size() == 1) { - s_customTabPackageName = packagesSupportingCustomTabs.get(0); - } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName) - && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) { - s_customTabPackageName = defaultViewHandlerPackageName; - } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) { - s_customTabPackageName = STABLE_PACKAGE; - } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) { - s_customTabPackageName = BETA_PACKAGE; - } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) { - s_customTabPackageName = DEV_PACKAGE; - } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) { - s_customTabPackageName = LOCAL_PACKAGE; - } - - return s_customTabPackageName; - } - - protected CustomTabsClient m_customTabClient; - protected CustomTabsServiceConnection m_customTabServiceConnection = new CustomTabsServiceConnection() { - @Override - public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) { - m_customTabClient = customTabsClient; - - m_customTabClient.warmup(0); - } - - @Override - public void onServiceDisconnected(ComponentName componentName) { - m_customTabClient = null; - } - }; - - protected SharedPreferences m_prefs; - - protected void setSmallScreen(boolean smallScreen) { - Log.d(TAG, "m_smallScreenMode=" + smallScreen); - m_smallScreenMode = smallScreen; - } - - public boolean getUnreadOnly() { - return m_prefs.getBoolean("show_unread_only", true); - } + private final String TAG = this.getClass().getSimpleName(); + + public final static String FRAG_HEADLINES = "headlines"; + public final static String FRAG_ARTICLE = "article"; + public final static String FRAG_FEEDS = "feeds"; + public final static String FRAG_CATS = "cats"; + + public final static String THEME_DEFAULT = "THEME_FOLLOW_DEVICE"; + + public final static String NOTIFICATION_CHANNEL_NORMAL = "channel_normal"; + public final static String NOTIFICATION_CHANNEL_PRIORITY = "channel_priority"; + + public static final int EXCERPT_MAX_LENGTH = 256; + public static final int LABEL_BASE_INDEX = -1024; + + public static final int PENDING_INTENT_CHROME_SHARE = 1; + + private boolean m_smallScreenMode = true; + protected String m_theme; + private boolean m_needRestart; + + private static String s_customTabPackageName; + + static final String STABLE_PACKAGE = "com.android.chrome"; + static final String BETA_PACKAGE = "com.chrome.beta"; + static final String DEV_PACKAGE = "com.chrome.dev"; + static final String LOCAL_PACKAGE = "com.google.android.apps.chrome"; + private static final String ACTION_CUSTOM_TABS_CONNECTION = + "android.support.customtabs.action.CustomTabsService"; + + private static String getCustomTabPackageName(Context context) { + if (s_customTabPackageName != null) return s_customTabPackageName; + + PackageManager pm = context.getPackageManager(); + // Get default VIEW intent handler. + Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); + ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0); + String defaultViewHandlerPackageName = null; + if (defaultViewHandlerInfo != null) { + defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName; + } + + // Get all apps that can handle VIEW intents. + List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0); + List<String> packagesSupportingCustomTabs = new ArrayList<>(); + for (ResolveInfo info : resolvedActivityList) { + Intent serviceIntent = new Intent(); + serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION); + serviceIntent.setPackage(info.activityInfo.packageName); + if (pm.resolveService(serviceIntent, 0) != null) { + packagesSupportingCustomTabs.add(info.activityInfo.packageName); + } + } + + // Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents + // and service calls. + if (packagesSupportingCustomTabs.isEmpty()) { + s_customTabPackageName = null; + } else if (packagesSupportingCustomTabs.size() == 1) { + s_customTabPackageName = packagesSupportingCustomTabs.get(0); + } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName) + && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) { + s_customTabPackageName = defaultViewHandlerPackageName; + } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) { + s_customTabPackageName = STABLE_PACKAGE; + } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) { + s_customTabPackageName = BETA_PACKAGE; + } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) { + s_customTabPackageName = DEV_PACKAGE; + } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) { + s_customTabPackageName = LOCAL_PACKAGE; + } + + return s_customTabPackageName; + } + + protected CustomTabsClient m_customTabClient; + protected CustomTabsServiceConnection m_customTabServiceConnection = new CustomTabsServiceConnection() { + @Override + public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) { + m_customTabClient = customTabsClient; + + m_customTabClient.warmup(0); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + m_customTabClient = null; + } + }; + + protected SharedPreferences m_prefs; + + protected void setSmallScreen(boolean smallScreen) { + Log.d(TAG, "m_smallScreenMode=" + smallScreen); + m_smallScreenMode = smallScreen; + } + + public boolean getUnreadOnly() { + return m_prefs.getBoolean("show_unread_only", true); + } // not the same as isSmallScreen() which is mostly about layout being loaded public boolean isTablet() { return getResources().getConfiguration().smallestScreenWidthDp >= 600; } - public void setUnreadOnly(boolean unread) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putBoolean("show_unread_only", unread); - editor.apply(); - } + public void setUnreadOnly(boolean unread) { + SharedPreferences.Editor editor = m_prefs.edit(); + editor.putBoolean("show_unread_only", unread); + editor.apply(); + } - public void toast(int msgId) { - toast(getString(msgId)); - } + public void toast(int msgId) { + toast(getString(msgId)); + } - public void toast(String msg) { - Snackbar.make(findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG) - .setAction(R.string.dialog_close, v -> { + public void toast(String msg) { + Snackbar.make(findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG) + .setAction(R.string.dialog_close, v -> { }) - .show(); - } + .show(); + } + + @Override + public void onResume() { + super.onResume(); - @Override - public void onResume() { - super.onResume(); + if (m_needRestart) { + Log.d(TAG, "restart requested"); - if (m_needRestart) { - Log.d(TAG, "restart requested"); - - finish(); - startActivity(getIntent()); - } - } + finish(); + startActivity(getIntent()); + } + } - @Override - public void onDestroy() { + @Override + public void onDestroy() { - if (m_customTabServiceConnection != null) { - unbindService(m_customTabServiceConnection); - } + if (m_customTabServiceConnection != null) { + unbindService(m_customTabServiceConnection); + } - super.onDestroy(); - } + super.onDestroy(); + } - @Override - public void onCreate(Bundle savedInstanceState) { - EdgeToEdge.enable(this); + @Override + public void onCreate(Bundle savedInstanceState) { + EdgeToEdge.enable(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationManager nmgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationManager nmgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - // todo: human readable names + // todo: human readable names - NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_PRIORITY, - NOTIFICATION_CHANNEL_PRIORITY, - NotificationManager.IMPORTANCE_HIGH); - channel.setShowBadge(false); - channel.setSound(null, null); - nmgr.createNotificationChannel(channel); + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_PRIORITY, + NOTIFICATION_CHANNEL_PRIORITY, + NotificationManager.IMPORTANCE_HIGH); + channel.setShowBadge(false); + channel.setSound(null, null); + nmgr.createNotificationChannel(channel); - channel = new NotificationChannel(NOTIFICATION_CHANNEL_NORMAL, - NOTIFICATION_CHANNEL_NORMAL, - NotificationManager.IMPORTANCE_DEFAULT); - channel.setShowBadge(false); - channel.setSound(null, null); - nmgr.createNotificationChannel(channel); - } + channel = new NotificationChannel(NOTIFICATION_CHANNEL_NORMAL, + NOTIFICATION_CHANNEL_NORMAL, + NotificationManager.IMPORTANCE_DEFAULT); + channel.setShowBadge(false); + channel.setSound(null, null); + nmgr.createNotificationChannel(channel); + } - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); + m_prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); - m_prefs.registerOnSharedPreferenceChangeListener(this); + m_prefs.registerOnSharedPreferenceChangeListener(this); - if (m_prefs.getBoolean("window_secure_mode", false)) - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + if (m_prefs.getBoolean("window_secure_mode", false)) + getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - setupWidgetUpdates(this); + setupWidgetUpdates(this); if (savedInstanceState == null) { - m_theme = m_prefs.getString("theme", CommonActivity.THEME_DEFAULT); - } else { - m_theme = savedInstanceState.getString("m_theme"); - } - - String customTabPackageName = getCustomTabPackageName(this); - - boolean customTabServiceBound = CustomTabsClient.bindCustomTabsService(this, customTabPackageName != null ? - customTabPackageName : "com.android.chrome", m_customTabServiceConnection); - - Log.d(TAG, "customTabServiceBound=" + customTabServiceBound + "; package name=" + customTabPackageName); - - super.onCreate(savedInstanceState); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putString("m_theme", m_theme); - } - - public boolean isSmallScreen() { - return m_smallScreenMode; - } - - @SuppressWarnings("deprecation") - public boolean isPortrait() { - Display display = getWindowManager().getDefaultDisplay(); - - int width = display.getWidth(); - int height = display.getHeight(); - - return width < height; - } - - @SuppressLint({ "NewApi", "ServiceCast" }) - @SuppressWarnings("deprecation") - public void copyToClipboard(String str) { + m_theme = m_prefs.getString("theme", CommonActivity.THEME_DEFAULT); + } else { + m_theme = savedInstanceState.getString("m_theme"); + } + + String customTabPackageName = getCustomTabPackageName(this); + + boolean customTabServiceBound = CustomTabsClient.bindCustomTabsService(this, customTabPackageName != null ? + customTabPackageName : "com.android.chrome", m_customTabServiceConnection); + + Log.d(TAG, "customTabServiceBound=" + customTabServiceBound + "; package name=" + customTabPackageName); + + super.onCreate(savedInstanceState); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + out.putString("m_theme", m_theme); + } + + public boolean isSmallScreen() { + return m_smallScreenMode; + } + + @SuppressWarnings("deprecation") + public boolean isPortrait() { + Display display = getWindowManager().getDefaultDisplay(); + + int width = display.getWidth(); + int height = display.getHeight(); + + return width < height; + } + + @SuppressLint({"NewApi", "ServiceCast"}) + @SuppressWarnings("deprecation") + public void copyToClipboard(String str) { android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getSystemService(CLIPBOARD_SERVICE); clipboard.setText(str); Snackbar.make(findViewById(android.R.id.content), R.string.text_copied_to_clipboard, Snackbar.LENGTH_SHORT) - .setAction(R.string.dialog_close, v -> { + .setAction(R.string.dialog_close, v -> { }) - .show(); - } - - protected void setAppTheme(SharedPreferences prefs) { - String theme = prefs.getString("theme", CommonActivity.THEME_DEFAULT); - - Log.d(TAG, "setting theme to: " + theme); - - if ("THEME_DARK".equals(theme)) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - } else if ("THEME_LIGHT".equals(theme)) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - } else { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - } - - if (prefs.getBoolean("enable_dynamic_colors", false)) - setTheme(R.style.AppTheme_Dynamic); - else - setTheme(R.style.AppTheme); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Log.d(TAG, "onSharedPreferenceChanged:" + key); - - if ("theme".equals(key)) { - setAppTheme(sharedPreferences); - } - - String[] filter = new String[] { "enable_dynamic_colors", "enable_cats", "widget_update_interval", - "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size", - "force_phone_layout", "open_on_startup", "window_secure_mode", "enable_icon_tinting" }; - - m_needRestart = Arrays.asList(filter).contains(key); - } - - private CustomTabsSession getCustomTabSession() { - return m_customTabClient.newSession(new CustomTabsCallback() { - @Override - public void onNavigationEvent(int navigationEvent, Bundle extras) { - super.onNavigationEvent(navigationEvent, extras); - } - }); - } - - protected Intent getShareIntent(String text, String subject) { - Intent shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_TEXT, text); - - if (subject != null) { - shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - } - - return shareIntent; - } - - protected void shareText(String text) { - startActivity(Intent.createChooser(getShareIntent(text, null), text)); - } - - protected void shareText(String text, String subject) { - startActivity(Intent.createChooser(getShareIntent(text, subject), text)); - } - - protected void shareImageFromUri(String url) { - Glide.with(this) - .asBitmap() - .load(url) - .skipMemoryCache(false) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(new SimpleTarget<Bitmap>() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { - Log.d(TAG, "image resource ready: " + resource); - - if (resource != null) { - File shareFolder = new File(getCacheDir(), "shared"); - - try { - shareFolder.mkdirs(); - - File file = new File(shareFolder, "shared.png"); - - FileOutputStream stream = new FileOutputStream(file); - resource.compress(Bitmap.CompressFormat.PNG, 90, stream); - stream.flush(); - stream.close(); - - Uri shareUri = FileProvider.getUriForFile(CommonActivity.this, - "org.fox.ttrss.SharedFileProvider", file); - - Intent intent = new Intent(android.content.Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_STREAM, shareUri); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setType("image/png"); - - startActivity(intent); - - } catch (Exception e) { - e.printStackTrace(); - toast(e.getMessage()); - } - - } else { - toast(getString(R.string.img_share_failed_to_load)); - } - } - }); - } - - private void openUriWithCustomTab(Uri uri) { - if (m_customTabClient != null) { - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getCustomTabSession()); - - builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); - builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); - - builder.setShowTitle(true); - - Intent shareIntent = getShareIntent(uri.toString(), null); - - PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), - CommonActivity.PENDING_INTENT_CHROME_SHARE, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + .show(); + } + + protected void setAppTheme(SharedPreferences prefs) { + String theme = prefs.getString("theme", CommonActivity.THEME_DEFAULT); + + Log.d(TAG, "setting theme to: " + theme); + + if ("THEME_DARK".equals(theme)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + } else if ("THEME_LIGHT".equals(theme)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + } + + if (prefs.getBoolean("enable_dynamic_colors", false)) + setTheme(R.style.AppTheme_Dynamic); + else + setTheme(R.style.AppTheme); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Log.d(TAG, "onSharedPreferenceChanged:" + key); + + if ("theme".equals(key)) { + setAppTheme(sharedPreferences); + } + + String[] filter = new String[]{"enable_dynamic_colors", "enable_cats", "widget_update_interval", + "headlines_swipe_to_dismiss", "headlines_mark_read_scroll", "headlines_request_size", + "force_phone_layout", "open_on_startup", "window_secure_mode", "enable_icon_tinting"}; + + m_needRestart = Arrays.asList(filter).contains(key); + } + + private CustomTabsSession getCustomTabSession() { + return m_customTabClient.newSession(new CustomTabsCallback() { + @Override + public void onNavigationEvent(int navigationEvent, Bundle extras) { + super.onNavigationEvent(navigationEvent, extras); + } + }); + } - builder.setActionButton(BitmapFactory.decodeResource(getResources(), R.drawable.baseline_share_24), - getString(R.string.share_article), pendingIntent); - - CustomTabsIntent intent = builder.build(); - - try { - intent.launchUrl(this, uri); - } catch (Exception e) { - e.printStackTrace(); - toast(e.getMessage()); - } - } - } - - // uses chrome custom tabs when available - public void openUri(Uri uri) { - boolean enableCustomTabs = m_prefs.getBoolean("enable_custom_tabs", true); - final boolean askEveryTime = m_prefs.getBoolean("custom_tabs_ask_always", true); + protected Intent getShareIntent(String text, String subject) { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + shareIntent.putExtra(Intent.EXTRA_TEXT, text); - if (uri.getScheme() == null) { - try { - uri = Uri.parse("https:" + uri); - } catch (Exception e) { - e.printStackTrace(); - } - } + if (subject != null) { + shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + } - final Uri finalUri = uri; + return shareIntent; + } + + protected void shareText(String text) { + startActivity(Intent.createChooser(getShareIntent(text, null), text)); + } + + protected void shareText(String text, String subject) { + startActivity(Intent.createChooser(getShareIntent(text, subject), text)); + } + + protected void shareImageFromUri(String url) { + Glide.with(this) + .asBitmap() + .load(url) + .skipMemoryCache(false) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { + Log.d(TAG, "image resource ready: " + resource); + + if (resource != null) { + File shareFolder = new File(getCacheDir(), "shared"); + + try { + shareFolder.mkdirs(); + + File file = new File(shareFolder, "shared.png"); + + FileOutputStream stream = new FileOutputStream(file); + resource.compress(Bitmap.CompressFormat.PNG, 90, stream); + stream.flush(); + stream.close(); + + Uri shareUri = FileProvider.getUriForFile(CommonActivity.this, + "org.fox.ttrss.SharedFileProvider", file); + + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, shareUri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType("image/png"); + + startActivity(intent); + + } catch (Exception e) { + e.printStackTrace(); + toast(e.getMessage()); + } + + } else { + toast(getString(R.string.img_share_failed_to_load)); + } + } + }); + } + + private void openUriWithCustomTab(Uri uri) { + if (m_customTabClient != null) { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getCustomTabSession()); + + builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); + builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); + + builder.setShowTitle(true); + + Intent shareIntent = getShareIntent(uri.toString(), null); - Log.d(TAG, "openUri=" + uri + "; enableCustomTabs=" + enableCustomTabs + "; customTabClient=" + m_customTabClient); + PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), + CommonActivity.PENDING_INTENT_CHROME_SHARE, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - if (enableCustomTabs && m_customTabClient != null) { + builder.setActionButton(BitmapFactory.decodeResource(getResources(), R.drawable.baseline_share_24), + getString(R.string.share_article), pendingIntent); - if (askEveryTime) { + CustomTabsIntent intent = builder.build(); - View dialogView = View.inflate(this, R.layout.dialog_open_link_askcb, null); - final CheckBox askEveryTimeCB = dialogView.findViewById(R.id.open_link_ask_checkbox); + try { + intent.launchUrl(this, uri); + } catch (Exception e) { + e.printStackTrace(); + toast(e.getMessage()); + } + } + } + + // uses chrome custom tabs when available + public void openUri(Uri uri) { + boolean enableCustomTabs = m_prefs.getBoolean("enable_custom_tabs", true); + final boolean askEveryTime = m_prefs.getBoolean("custom_tabs_ask_always", true); + + if (uri.getScheme() == null) { + try { + uri = Uri.parse("https:" + uri); + } catch (Exception e) { + e.printStackTrace(); + } + } + + final Uri finalUri = uri; + + Log.d(TAG, "openUri=" + uri + "; enableCustomTabs=" + enableCustomTabs + "; customTabClient=" + m_customTabClient); + + if (enableCustomTabs && m_customTabClient != null) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setView(dialogView) - .setMessage(uri.toString()) - .setPositiveButton(R.string.quick_preview, + if (askEveryTime) { + + View dialogView = View.inflate(this, R.layout.dialog_open_link_askcb, null); + final CheckBox askEveryTimeCB = dialogView.findViewById(R.id.open_link_ask_checkbox); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setView(dialogView) + .setMessage(uri.toString()) + .setPositiveButton(R.string.quick_preview, (dialog, which) -> { if (!askEveryTimeCB.isChecked()) { @@ -468,7 +468,7 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc openUriWithCustomTab(finalUri); }) - .setNegativeButton(R.string.open_with, + .setNegativeButton(R.string.open_with, (dialog, which) -> { if (!askEveryTimeCB.isChecked()) { @@ -502,97 +502,97 @@ public class CommonActivity extends AppCompatActivity implements SharedPreferenc } });*/ - Dialog dlg = builder.create(); - dlg.show(); - - } else { - openUriWithCustomTab(uri); - } - - } else { - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - - try { - startActivity(intent); - } catch (Exception e) { - toast(e.getMessage()); - } - } - } - - public static void setupWidgetUpdates(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - int updateInterval = Integer.parseInt(prefs.getString("widget_update_interval", "15")) * 60 * 1000; - - Log.d("setupWidgetUpdates", "interval= " + updateInterval); - - AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE); - - Intent intentUpdate = new Intent(context, SmallWidgetProvider.class); - intentUpdate.setAction(SmallWidgetProvider.ACTION_REQUEST_UPDATE); - - PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(context, - 0, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - alarmManager.cancel(pendingIntentAlarm); - - alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + updateInterval, - updateInterval, - pendingIntentAlarm); - - } - - public void displayImageCaption(String url, String htmlContent) { - // Android doesn't give us an easy way to access title tags; - // we'll use Jsoup on the body text to grab the title text - // from the first image tag with this url. This will show - // the wrong text if an image is used multiple times. - Document doc = Jsoup.parse(htmlContent); - Elements es = doc.getElementsByAttributeValue("src", url); - if (!es.isEmpty()) { - if (es.get(0).hasAttr("title")) { - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setCancelable(true) - .setMessage(es.get(0).attr("title")) - .setPositiveButton(R.string.dialog_close, (dialog, which) -> dialog.cancel() - ); - - Dialog dialog = builder.create(); - dialog.show(); - - } else { - toast(R.string.no_caption_to_display); - } - } else { - toast(R.string.no_caption_to_display); - } - } - - @Override - public void onTrimMemory(int level) { - super.onTrimMemory(level); - Log.d(TAG, "onTrimMemory called"); - Glide.get(this).trimMemory(level); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - Log.d(TAG, "onLowMemory called"); - Glide.get(this).clearMemory(); - } - - public static void requestWidgetUpdate(Context context) { - JobIntentService.enqueueWork(context.getApplicationContext(), WidgetUpdateService.class, 0, new Intent()); - } - - static public int dpToPx(Context context, int dp) { - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + Dialog dlg = builder.create(); + dlg.show(); + + } else { + openUriWithCustomTab(uri); + } + + } else { + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + + try { + startActivity(intent); + } catch (Exception e) { + toast(e.getMessage()); + } + } + } + + public static void setupWidgetUpdates(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + int updateInterval = Integer.parseInt(prefs.getString("widget_update_interval", "15")) * 60 * 1000; + + Log.d("setupWidgetUpdates", "interval= " + updateInterval); + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); + + Intent intentUpdate = new Intent(context, SmallWidgetProvider.class); + intentUpdate.setAction(SmallWidgetProvider.ACTION_REQUEST_UPDATE); + + PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(context, + 0, intentUpdate, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + alarmManager.cancel(pendingIntentAlarm); + + alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, + SystemClock.elapsedRealtime() + updateInterval, + updateInterval, + pendingIntentAlarm); + + } + + public void displayImageCaption(String url, String htmlContent) { + // Android doesn't give us an easy way to access title tags; + // we'll use Jsoup on the body text to grab the title text + // from the first image tag with this url. This will show + // the wrong text if an image is used multiple times. + Document doc = Jsoup.parse(htmlContent); + Elements es = doc.getElementsByAttributeValue("src", url); + if (!es.isEmpty()) { + if (es.get(0).hasAttr("title")) { + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setCancelable(true) + .setMessage(es.get(0).attr("title")) + .setPositiveButton(R.string.dialog_close, (dialog, which) -> dialog.cancel() + ); + + Dialog dialog = builder.create(); + dialog.show(); + + } else { + toast(R.string.no_caption_to_display); + } + } else { + toast(R.string.no_caption_to_display); + } + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + Log.d(TAG, "onTrimMemory called"); + Glide.get(this).trimMemory(level); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + Log.d(TAG, "onLowMemory called"); + Glide.get(this).clearMemory(); + } + + public static void requestWidgetUpdate(Context context) { + JobIntentService.enqueueWork(context.getApplicationContext(), WidgetUpdateService.class, 0, new Intent()); + } + + static public int dpToPx(Context context, int dp) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); - } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java index 422e377c..c9a246bb 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DetailActivity.java @@ -22,124 +22,124 @@ import org.fox.ttrss.types.Article; import org.fox.ttrss.types.Feed; public class DetailActivity extends OnlineActivity implements HeadlinesEventListener { - private final String TAG = this.getClass().getSimpleName(); - protected BottomAppBar m_bottomAppBar; + private final String TAG = this.getClass().getSimpleName(); + protected BottomAppBar m_bottomAppBar; - protected SharedPreferences m_prefs; + protected SharedPreferences m_prefs; @SuppressLint("NewApi") - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); + @Override + public void onCreate(Bundle savedInstanceState) { + m_prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); - setAppTheme(m_prefs); + setAppTheme(m_prefs); - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); - if (m_prefs.getBoolean("force_phone_layout", false)) { - setContentView(R.layout.activity_detail_phone); - } else { - setContentView(R.layout.activity_detail); - } + if (m_prefs.getBoolean("force_phone_layout", false)) { + setContentView(R.layout.activity_detail_phone); + } else { + setContentView(R.layout.activity_detail); + } - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); - setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); - - Application.getInstance().load(savedInstanceState); + setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); + + Application.getInstance().load(savedInstanceState); - View headlines = findViewById(R.id.headlines_fragment); + View headlines = findViewById(R.id.headlines_fragment); - if (headlines != null) - headlines.setVisibility(isPortrait() ? View.GONE : View.VISIBLE); + if (headlines != null) + headlines.setVisibility(isPortrait() ? View.GONE : View.VISIBLE); - if (!isPortrait() && !isSmallScreen()) { - enableActionModeObserver(); - } + if (!isPortrait() && !isSmallScreen()) { + enableActionModeObserver(); + } - m_loadingProgress = findViewById(R.id.loading_progress); + m_loadingProgress = findViewById(R.id.loading_progress); - m_bottomAppBar = findViewById(R.id.detail_bottom_appbar); + m_bottomAppBar = findViewById(R.id.detail_bottom_appbar); - if (m_bottomAppBar != null) { - m_bottomAppBar.setOnMenuItemClickListener(item -> { + if (m_bottomAppBar != null) { + m_bottomAppBar.setOnMenuItemClickListener(item -> { Article activeArticle = Application.getArticlesModel().getActiveArticle(); - if (activeArticle != null) { - int itemId = item.getItemId(); + if (activeArticle != null) { + int itemId = item.getItemId(); - if (itemId == R.id.article_set_labels) { - editArticleLabels(activeArticle); + if (itemId == R.id.article_set_labels) { + editArticleLabels(activeArticle); - return true; - } else if (itemId == R.id.toggle_attachments) { - displayAttachments(activeArticle); + return true; + } else if (itemId == R.id.toggle_attachments) { + displayAttachments(activeArticle); - return true; - } else if (itemId == R.id.article_edit_note) { - editArticleNote(activeArticle); + return true; + } else if (itemId == R.id.article_edit_note) { + editArticleNote(activeArticle); - return true; - } else if (itemId == R.id.article_set_score) { - setArticleScore(activeArticle); + return true; + } else if (itemId == R.id.article_set_score) { + setArticleScore(activeArticle); - return true; - } else if (itemId == R.id.toggle_unread) { - Article articleClone = new Article(activeArticle); - articleClone.unread = !articleClone.unread; + return true; + } else if (itemId == R.id.toggle_unread) { + Article articleClone = new Article(activeArticle); + articleClone.unread = !articleClone.unread; - saveArticleUnread(articleClone); - } - } + saveArticleUnread(articleClone); + } + } return false; }); - } + } - FloatingActionButton fab = findViewById(R.id.detail_fab); + FloatingActionButton fab = findViewById(R.id.detail_fab); - if (fab != null) { - if (m_prefs.getBoolean("enable_article_fab", true)) { - fab.show(); + if (fab != null) { + if (m_prefs.getBoolean("enable_article_fab", true)) { + fab.show(); - fab.setOnClickListener(view -> { - Article activeArticle = Application.getArticlesModel().getActiveArticle(); + fab.setOnClickListener(view -> { + Article activeArticle = Application.getArticlesModel().getActiveArticle(); - if (activeArticle != null) - openUri(Uri.parse(activeArticle.link)); + if (activeArticle != null) + openUri(Uri.parse(activeArticle.link)); }); - } else { - fab.hide(); - } - } + } else { + fab.hide(); + } + } if (savedInstanceState == null) { - Intent i = getIntent(); - - if (i.getExtras() != null) { - boolean shortcutMode = i.getBooleanExtra("shortcut_mode", false); - - Log.d(TAG, "is_shortcut_mode: " + shortcutMode); - - Feed tmpFeed; - - if (shortcutMode) { - int feedId = i.getIntExtra("feed_id", 0); - boolean isCat = i.getBooleanExtra("feed_is_cat", false); - String feedTitle = i.getStringExtra("feed_title"); - - tmpFeed = new Feed(feedId, feedTitle, isCat); - } else { - tmpFeed = i.getParcelableExtra("feed"); - } - - final Feed feed = tmpFeed; - final int openedArticleId = i.getIntExtra("openedArticleId", 0); - final String searchQuery = i.getStringExtra("searchQuery"); + Intent i = getIntent(); + + if (i.getExtras() != null) { + boolean shortcutMode = i.getBooleanExtra("shortcut_mode", false); + + Log.d(TAG, "is_shortcut_mode: " + shortcutMode); + + Feed tmpFeed; + + if (shortcutMode) { + int feedId = i.getIntExtra("feed_id", 0); + boolean isCat = i.getBooleanExtra("feed_is_cat", false); + String feedTitle = i.getStringExtra("feed_title"); + + tmpFeed = new Feed(feedId, feedTitle, isCat); + } else { + tmpFeed = i.getParcelableExtra("feed"); + } + + final Feed feed = tmpFeed; + final int openedArticleId = i.getIntExtra("openedArticleId", 0); + final String searchQuery = i.getStringExtra("searchQuery"); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); @@ -148,72 +148,72 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - ArticlePager ap = new ArticlePager(); - ap.initialize(openedArticleId, feed); + ArticlePager ap = new ArticlePager(); + ap.initialize(openedArticleId, feed); - ft.replace(R.id.article_fragment, ap, FRAG_ARTICLE); + ft.replace(R.id.article_fragment, ap, FRAG_ARTICLE); - ft.commit(); + ft.commit(); - if (feed != null) - setTitle(feed.title); + if (feed != null) + setTitle(feed.title); - initBottomBarMenu(); - } - } - } + initBottomBarMenu(); + } + } + } - @Override - public void invalidateOptionsMenu() { - super.invalidateOptionsMenu(); + @Override + public void invalidateOptionsMenu() { + super.invalidateOptionsMenu(); - initBottomBarMenu(); - } + initBottomBarMenu(); + } - protected void initBottomBarMenu() { - if (m_bottomAppBar != null) { - Menu menu = m_bottomAppBar.getMenu(); + protected void initBottomBarMenu() { + if (m_bottomAppBar != null) { + Menu menu = m_bottomAppBar.getMenu(); - menu.findItem(R.id.article_set_labels).setEnabled(getApiLevel() >= 1); - menu.findItem(R.id.article_edit_note).setEnabled(getApiLevel() >= 1); + menu.findItem(R.id.article_set_labels).setEnabled(getApiLevel() >= 1); + menu.findItem(R.id.article_edit_note).setEnabled(getApiLevel() >= 1); - Article activeArticle = Application.getArticlesModel().getActiveArticle(); + Article activeArticle = Application.getArticlesModel().getActiveArticle(); - if (activeArticle != null) { - if (activeArticle.score > 0) { - menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_up_24); - } else if (activeArticle.score < 0) { - menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_down_24); - } else { - menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_flat_24); - } + if (activeArticle != null) { + if (activeArticle.score > 0) { + menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_up_24); + } else if (activeArticle.score < 0) { + menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_down_24); + } else { + menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_flat_24); + } - menu.findItem(R.id.toggle_unread).setIcon(activeArticle.unread ? R.drawable.baseline_email_24 : - R.drawable.baseline_drafts_24); + menu.findItem(R.id.toggle_unread).setIcon(activeArticle.unread ? R.drawable.baseline_email_24 : + R.drawable.baseline_drafts_24); - menu.findItem(R.id.toggle_attachments).setVisible(activeArticle.attachments != null && !activeArticle.attachments.isEmpty()); - } - } - } + menu.findItem(R.id.toggle_attachments).setVisible(activeArticle.attachments != null && !activeArticle.attachments.isEmpty()); + } + } + } - @Override - protected void loginSuccess(boolean refresh) { - Log.d(TAG, "loginSuccess"); + @Override + protected void loginSuccess(boolean refresh) { + Log.d(TAG, "loginSuccess"); - invalidateOptionsMenu(); - - if (refresh) refresh(); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); + invalidateOptionsMenu(); - Application.getInstance().save(out); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { + if (refresh) refresh(); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + Application.getInstance().save(out); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { onBackPressed(); return true; @@ -221,82 +221,82 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId()); return super.onOptionsItemSelected(item); } - - @Override - public void onResume() { - super.onResume(); - } - @Override - protected void initMenu() { - super.initMenu(); + @Override + public void onResume() { + super.onResume(); + } + + @Override + protected void initMenu() { + super.initMenu(); - if (m_menu != null && getSessionId() != null) { - m_menu.setGroupVisible(R.id.menu_group_feeds, false); + if (m_menu != null && getSessionId() != null) { + m_menu.setGroupVisible(R.id.menu_group_feeds, false); - m_menu.setGroupVisible(R.id.menu_group_headlines, !isPortrait() && !isSmallScreen()); + m_menu.setGroupVisible(R.id.menu_group_headlines, !isPortrait() && !isSmallScreen()); - m_menu.findItem(R.id.catchup_above).setVisible(!isSmallScreen()); + m_menu.findItem(R.id.catchup_above).setVisible(!isSmallScreen()); - ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - m_menu.setGroupVisible(R.id.menu_group_article, af != null); + ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - m_menu.findItem(R.id.search).setVisible(false); - } - } + m_menu.setGroupVisible(R.id.menu_group_article, af != null); + + m_menu.findItem(R.id.search).setVisible(false); + } + } - @Override - public void onArticleSelected(Article article) { + @Override + public void onArticleSelected(Article article) { - Article articleClone = new Article(article); + Article articleClone = new Article(article); - if (articleClone.unread) { - articleClone.unread = false; - saveArticleUnread(articleClone); - } + if (articleClone.unread) { + articleClone.unread = false; + saveArticleUnread(articleClone); + } - Application.getArticlesModel().setActive(articleClone); - } + Application.getArticlesModel().setActive(articleClone); + } - @Override - public void onHeadlinesLoaded(boolean appended) { - setLoadingVisible(false); + @Override + public void onHeadlinesLoaded(boolean appended) { + setLoadingVisible(false); - ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - if (ap != null) { + if (ap != null) { ap.syncToSharedArticles(); } - } + } - @Override - public void onHeadlinesLoadingProgress(int progress) { - setLoadingVisible(progress < 100); - setLoadingProgress(progress); - } + @Override + public void onHeadlinesLoadingProgress(int progress) { + setLoadingVisible(progress < 100); + setLoadingProgress(progress); + } - @Override - public void onBackPressed() { + @Override + public void onBackPressed() { Intent resultIntent = new Intent(); setResult(Activity.RESULT_OK, resultIntent); - try { - super.onBackPressed(); - } catch (IllegalStateException e) { - // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - e.printStackTrace(); - } + try { + super.onBackPressed(); + } catch (IllegalStateException e) { + // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState + e.printStackTrace(); + } } - @Override - public void onPause() { - super.onPause(); + @Override + public void onPause() { + super.onPause(); - if (isFinishing()) { - overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); - } + if (isFinishing()) { + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); + } - } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/DummyFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/DummyFragment.java index 702913e2..95c87d8a 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/DummyFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/DummyFragment.java @@ -8,11 +8,11 @@ import android.view.ViewGroup; import androidx.fragment.app.Fragment; public class DummyFragment extends Fragment { - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_dummy, container, false); - - return view; - } + View view = inflater.inflate(R.layout.fragment_dummy, container, false); + + return view; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java index 93324f6e..974f5955 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsFragment.java @@ -47,522 +47,524 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; public class FeedsFragment extends Fragment implements OnSharedPreferenceChangeListener { - private final String TAG = this.getClass().getSimpleName(); - protected SharedPreferences m_prefs; - protected MasterActivity m_activity; - protected Feed m_rootFeed; - private Feed m_selectedFeed; - protected SwipeRefreshLayout m_swipeLayout; + private final String TAG = this.getClass().getSimpleName(); + protected SharedPreferences m_prefs; + protected MasterActivity m_activity; + protected Feed m_rootFeed; + private Feed m_selectedFeed; + protected SwipeRefreshLayout m_swipeLayout; private boolean m_enableParentBtn = false; - protected FeedsAdapter m_adapter; - private RecyclerView m_list; - private RecyclerView.LayoutManager m_layoutManager; - private LinearProgressIndicator m_loadingProgress; + protected FeedsAdapter m_adapter; + private RecyclerView m_list; + private RecyclerView.LayoutManager m_layoutManager; + private LinearProgressIndicator m_loadingProgress; - public void initialize(@NonNull Feed rootFeed, boolean enableParentBtn) { - Log.d(TAG, "initialize, feed=" + rootFeed); + public void initialize(@NonNull Feed rootFeed, boolean enableParentBtn) { + Log.d(TAG, "initialize, feed=" + rootFeed); m_rootFeed = rootFeed; - m_enableParentBtn = enableParentBtn; - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item - .getMenuInfo(); - - // all onContextItemSelected are invoked in sequence so we might get a context menu for headlines, etc - try { - if (info != null) { - final Feed feed = m_adapter.getCurrentList().get(info.position); - - Log.d(TAG, "context for feed=" + feed.id); - - int itemId = item.getItemId(); - if (itemId == R.id.feed_browse_headlines) { - Feed tmpFeed = new Feed(feed); - - if (!neverOpenHeadlines(feed)) - tmpFeed.always_open_headlines = true; - - m_activity.onFeedSelected(tmpFeed); - return true; - } else if (itemId == R.id.feed_browse_feeds) { - m_activity.onFeedSelected(feed); - return true; - } else if (itemId == R.id.feed_unsubscribe) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) - .setMessage(getString(R.string.unsubscribe_from_prompt, feed.title)) - .setPositiveButton(R.string.unsubscribe, - (dialog, which) -> m_activity.unsubscribeFeed(feed)) - .setNegativeButton(R.string.dialog_cancel, - (dialog, which) -> { - - }); - - Dialog dlg = builder.create(); - dlg.show(); - - return true; - } else if (itemId == R.id.feed_catchup) { - m_activity.catchupDialog(feed); - return true; - } - } - - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - m_activity.getMenuInflater().inflate(R.menu.context_feed, menu); - + m_enableParentBtn = enableParentBtn; + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item + .getMenuInfo(); + + // all onContextItemSelected are invoked in sequence so we might get a context menu for headlines, etc + try { + if (info != null) { + final Feed feed = m_adapter.getCurrentList().get(info.position); + + Log.d(TAG, "context for feed=" + feed.id); + + int itemId = item.getItemId(); + if (itemId == R.id.feed_browse_headlines) { + Feed tmpFeed = new Feed(feed); + + if (!neverOpenHeadlines(feed)) + tmpFeed.always_open_headlines = true; + + m_activity.onFeedSelected(tmpFeed); + return true; + } else if (itemId == R.id.feed_browse_feeds) { + m_activity.onFeedSelected(feed); + return true; + } else if (itemId == R.id.feed_unsubscribe) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) + .setMessage(getString(R.string.unsubscribe_from_prompt, feed.title)) + .setPositiveButton(R.string.unsubscribe, + (dialog, which) -> m_activity.unsubscribeFeed(feed)) + .setNegativeButton(R.string.dialog_cancel, + (dialog, which) -> { + + }); + + Dialog dlg = builder.create(); + dlg.show(); + + return true; + } else if (itemId == R.id.feed_catchup) { + m_activity.catchupDialog(feed); + return true; + } + } + + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + + Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); + return super.onContextItemSelected(item); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + + m_activity.getMenuInflater().inflate(R.menu.context_feed, menu); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; Feed feed = m_adapter.getCurrentList().get(info.position); - - menu.setHeaderTitle(feed.title); - if (!feed.is_cat) - menu.findItem(R.id.feed_browse_feeds).setVisible(false); + menu.setHeaderTitle(feed.title); + + if (!feed.is_cat) + menu.findItem(R.id.feed_browse_feeds).setVisible(false); + + if (neverOpenHeadlines(feed)) + menu.findItem(R.id.feed_browse_headlines).setVisible(false); + + if (feed.id <= 0 || feed.is_cat) + menu.findItem(R.id.feed_unsubscribe).setVisible(false); + + super.onCreateContextMenu(menu, v, menuInfo); + + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState != null) { + m_rootFeed = savedInstanceState.getParcelable("m_feed"); + m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed"); + m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn"); + } + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + out.putParcelable("m_feed", m_rootFeed); + out.putParcelable("m_selectedFeed", m_selectedFeed); + out.putBoolean("m_enableParentBtn", m_enableParentBtn); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.fragment_feeds, container, false); + + m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); + + m_swipeLayout.setOnRefreshListener(this::refresh); + + m_loadingProgress = view.findViewById(R.id.loading_progress); + + m_list = view.findViewById(R.id.feeds); + registerForContextMenu(m_list); + + m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext()); + m_list.setLayoutManager(m_layoutManager); + m_list.setItemAnimator(new DefaultItemAnimator()); + + m_adapter = new FeedsAdapter(); + m_list.setAdapter(m_adapter); + + TextView login = view.findViewById(R.id.drawer_header_login); + + if (login != null) { + login.setText(m_prefs.getString("login", "")); + } + + TextView server = view.findViewById(R.id.drawer_header_server); + + if (server != null) { + try { + server.setText(new URL(m_prefs.getString("ttrss_url", "")).getHost()); + } catch (MalformedURLException e) { + server.setText(""); + } + } + + View settingsBtn = view.findViewById(R.id.drawer_settings_btn); + + if (settingsBtn != null) { + settingsBtn.setOnClickListener(v -> { + Intent intent = new Intent(getActivity(), + PreferencesActivity.class); + + startActivityForResult(intent, 0); + }); + } + + FeedsModel model = getModel(); + + model.getUpdatesData().observe(m_activity, lastUpdate -> { + Log.d(TAG, "observed update=" + lastUpdate); + }); + + model.getLoadingProgress().observe(m_activity, progress -> { + Log.d(TAG, "observed feeds loading progress=" + progress); + + if (isAdded() && m_loadingProgress != null) { + m_loadingProgress.setVisibility(progress < 100 ? View.VISIBLE : View.GONE); + m_loadingProgress.setProgress(progress); + } + }); + + model.getIsLoading().observe(m_activity, isLoading -> { + Log.d(TAG, "observed isLoading=" + isLoading); + + if (isAdded()) { + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(isLoading); + + if (m_loadingProgress != null && !isLoading) + m_loadingProgress.setVisibility(View.GONE); + } + }); + + model.getFeeds().observe(getActivity(), feeds -> { + Log.d(TAG, "observed feeds size=" + feeds.size()); + + if (isAdded()) { + onFeedsLoaded(feeds); + + if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(true); + } else { + if (model.getLastErrorMessage() != null) { + m_activity.toast(getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); + } else { + m_activity.toast(model.getErrorMessage()); + } + } + } + } + }); + + return view; + } + + protected FeedsModel getModel() { + return new ViewModelProvider(this).get(FeedsModel.class); + } + + protected void onFeedsLoaded(List<Feed> loadedFeeds) { + List<Feed> feedsWork = new ArrayList<>(); + + if (m_enableParentBtn) { + feedsWork.add(0, new Feed(Feed.TYPE_GOBACK)); + + if (m_rootFeed.id >= 0 && !loadedFeeds.isEmpty()) { + Feed feed = new Feed(m_rootFeed.id, m_rootFeed.title, true); - if (neverOpenHeadlines(feed)) - menu.findItem(R.id.feed_browse_headlines).setVisible(false); + feed.unread = loadedFeeds.stream().map(a -> a.unread).reduce(0, Integer::sum); + feed.always_open_headlines = true; - if (feed.id <= 0 || feed.is_cat) - menu.findItem(R.id.feed_unsubscribe).setVisible(false); + feedsWork.add(1, feed); + } + } else if (m_rootFeed.id == Feed.ALL_ARTICLES) { + // if all articles feed is requested as a root element (no parent button) let's filter + // labels out so this is at least somewhat readable, instead we'll insert a link to this category to the top + loadedFeeds = loadedFeeds.stream().filter(a -> a.id >= -10).collect(Collectors.toList()); - super.onCreateContextMenu(menu, v, menuInfo); - - } + loadedFeeds.add(0, new Feed(Feed.CAT_LABELS, getString(R.string.cat_labels), true)); + } + + feedsWork.addAll(loadedFeeds); + + feedsWork.add(new Feed(Feed.TYPE_DIVIDER)); + feedsWork.add(new Feed(Feed.TYPE_TOGGLE_UNREAD, getString(R.string.unread_only), true)); + + m_adapter.submitList(feedsWork); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); + m_prefs.registerOnSharedPreferenceChangeListener(this); + + m_activity = (MasterActivity) activity; + } + + @Override + public void onResume() { + super.onResume(); + + Log.d(TAG, "onResume"); + + setSelectedFeed(m_activity.getActiveFeed()); + + refresh(); + } + + public void refresh() { + if (!isAdded()) + return; + + getModel().startLoading(m_rootFeed); + } + + private class FeedViewHolder extends RecyclerView.ViewHolder { + + private View view; + private ImageView icon; + private TextView title; + private TextView unreadCounter; + private MaterialSwitch rowSwitch; + + public FeedViewHolder(@NonNull View itemView) { + super(itemView); + + view = itemView; + icon = itemView.findViewById(R.id.icon); + title = itemView.findViewById(R.id.title); + unreadCounter = itemView.findViewById(R.id.unread_counter); + rowSwitch = itemView.findViewById(R.id.row_switch); + } + } + + private class FeedDiffUtilItemCallback extends DiffUtil.ItemCallback<Feed> { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + public boolean areItemsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { + return oldItem.id == newItem.id; + } + + @Override + public boolean areContentsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { + return oldItem.id == newItem.id && + oldItem.is_cat == newItem.is_cat && + oldItem.title.equals(newItem.title) && + oldItem.unread == newItem.unread && + oldItem.update_interval != newItem.update_interval && + oldItem.last_error.equals(newItem.last_error); + } + } - if (savedInstanceState != null) { - m_rootFeed = savedInstanceState.getParcelable("m_feed"); - m_selectedFeed = savedInstanceState.getParcelable("m_selectedFeed"); - m_enableParentBtn = savedInstanceState.getBoolean("m_enableParentBtn"); - } - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putParcelable("m_feed", m_rootFeed); - out.putParcelable("m_selectedFeed", m_selectedFeed); - out.putBoolean("m_enableParentBtn", m_enableParentBtn); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - View view = inflater.inflate(R.layout.fragment_feeds, container, false); - - m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); - - m_swipeLayout.setOnRefreshListener(this::refresh); - - m_loadingProgress = view.findViewById(R.id.loading_progress); + protected class FeedsAdapter extends ListAdapter<Feed, FeedViewHolder> { + public static final int VIEW_NORMAL = 0; + public static final int VIEW_SELECTED = 1; + public static final int VIEW_GOBACK = 2; + public static final int VIEW_TOGGLE_UNREAD = 4; + public static final int VIEW_DIVIDER = 5; - m_list = view.findViewById(R.id.feeds); - registerForContextMenu(m_list); + protected FeedsAdapter() { + super(new FeedDiffUtilItemCallback()); + } - m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext()); - m_list.setLayoutManager(m_layoutManager); - m_list.setItemAnimator(new DefaultItemAnimator()); - - m_adapter = new FeedsAdapter(); - m_list.setAdapter(m_adapter); - - TextView login = view.findViewById(R.id.drawer_header_login); - - if (login != null) { - login.setText(m_prefs.getString("login", "")); - } + @NonNull + @Override + public FeedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + int layoutId = R.layout.feeds_row; + + switch (viewType) { + case VIEW_SELECTED: + layoutId = R.layout.feeds_row_selected; + break; + case VIEW_GOBACK: + layoutId = R.layout.feeds_row_goback; + break; + case VIEW_TOGGLE_UNREAD: + layoutId = R.layout.feeds_row_toggle; + break; + case VIEW_DIVIDER: + layoutId = R.layout.feeds_row_divider; + break; + } + + return new FeedViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)); + } - TextView server = view.findViewById(R.id.drawer_header_server); + @Override + public void onBindViewHolder(@NonNull FeedViewHolder holder, int position) { + Feed feed = getItem(position); + + if (holder.icon != null) { + holder.icon.setImageResource(getIconForFeed(feed)); + } + + if (holder.title != null) { + holder.title.setText(feed.title); + + if (feed.always_open_headlines || (!feed.is_cat && feed.id == -4)) { + holder.title.setTypeface(null, Typeface.BOLD); + } else { + holder.title.setTypeface(null, Typeface.NORMAL); + } + + TypedValue tv = new TypedValue(); + m_activity.getTheme().resolveAttribute(feed.last_error.isEmpty() ? R.attr.colorOnSurface : R.attr.colorError, tv, true); + + holder.title.setTextColor(ColorStateList.valueOf(ContextCompat.getColor(m_activity, tv.resourceId))); + + holder.title.setAlpha(feed.update_interval == -1 ? 0.5f : 1f); + } + + if (holder.unreadCounter != null) { + holder.unreadCounter.setText(String.valueOf(feed.unread)); + holder.unreadCounter.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); + } + + // there's only one kind of row with checkbox atm + if (holder.rowSwitch != null) { + holder.rowSwitch.setChecked(m_activity.getUnreadOnly()); + + holder.rowSwitch.setOnCheckedChangeListener((button, isChecked) -> { + m_activity.setUnreadOnly(isChecked); + }); + } + + holder.view.setOnLongClickListener(view -> { + if (feed.id != Feed.TYPE_TOGGLE_UNREAD && feed.id != Feed.TYPE_DIVIDER && feed.id != Feed.TYPE_GOBACK && feed.id != Feed.ALL_ARTICLES) { + m_list.showContextMenuForChild(view); + } + return true; + }); + + // default open handler (i.e. tap) + holder.view.setOnClickListener(view -> { + if (feed.id == Feed.TYPE_GOBACK) { + m_activity.getSupportFragmentManager().popBackStack(); + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD || feed.id == Feed.TYPE_DIVIDER) { + // + } else { + Feed tmpFeed = new Feed(feed); + + if (!neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) + tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); + + m_activity.onFeedSelected(tmpFeed); + } + }); + } - if (server != null) { - try { - server.setText(new URL(m_prefs.getString("ttrss_url", "")).getHost()); - } catch (MalformedURLException e) { - server.setText(""); - } - } + @Override + public int getItemViewType(int position) { + Feed feed = getItem(position); + + if (feed.id == Feed.TYPE_GOBACK) { + return VIEW_GOBACK; + } else if (feed.id == Feed.TYPE_DIVIDER) { + return VIEW_DIVIDER; + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { + return VIEW_TOGGLE_UNREAD; + } else if (feed.equals(m_selectedFeed)) { + return VIEW_SELECTED; + } else { + return VIEW_NORMAL; + } + } - View settingsBtn = view.findViewById(R.id.drawer_settings_btn); + public int getPositionOf(Feed feed) { + if (feed != null) { + List<Feed> feeds = getCurrentList(); - if (settingsBtn != null) { - settingsBtn.setOnClickListener(v -> { - Intent intent = new Intent(getActivity(), - PreferencesActivity.class); - - startActivityForResult(intent, 0); - }); - } - - FeedsModel model = getModel(); - - model.getUpdatesData().observe(m_activity, lastUpdate -> { - Log.d(TAG, "observed update=" + lastUpdate); - }); - - model.getLoadingProgress().observe(m_activity, progress -> { - Log.d(TAG, "observed feeds loading progress=" + progress); - - if (isAdded() && m_loadingProgress != null) { - m_loadingProgress.setVisibility(progress < 100 ? View.VISIBLE : View.GONE); - m_loadingProgress.setProgress(progress); - } - }); + return IntStream.range(0, feeds.size()) + .sequential() + .filter(i -> { + Feed f = feeds.get(i); - model.getIsLoading().observe(m_activity, isLoading -> { - Log.d(TAG, "observed isLoading=" + isLoading); + return f.id == feed.id && f.is_cat == feed.is_cat; + }) + .findFirst() + .orElse(-1); + } - if (isAdded()) { - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(isLoading); + return -1; + } + } + + /** + * we always show Labels and Special contents, regardless of the setting + */ + private boolean neverOpenHeadlines(Feed feed) { + return feed.id == Feed.CAT_SPECIAL || feed.id == Feed.CAT_LABELS; + } + + protected int getIconForFeed(Feed feed) { + if (feed.id == Feed.TYPE_GOBACK) { + return R.drawable.baseline_arrow_back_24; + } else if (feed.id == Feed.CAT_LABELS && feed.is_cat) { + return R.drawable.baseline_label_24; + } else if (feed.id == Feed.CAT_SPECIAL && feed.is_cat) { + return R.drawable.baseline_folder_special_24; + } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { + return R.drawable.baseline_filter_alt_24; + } else if (feed.id == Feed.ARCHIVED && !feed.is_cat) { + return R.drawable.baseline_archive_24; + } else if (feed.id == Feed.MARKED && !feed.is_cat) { + return R.drawable.baseline_star_24; + } else if (feed.id == Feed.PUBLISHED && !feed.is_cat) { + return R.drawable.rss; + } else if (feed.id == Feed.FRESH && !feed.is_cat) { + return R.drawable.baseline_local_fire_department_24; + } else if (feed.id == Feed.ALL_ARTICLES && !feed.is_cat) { + return R.drawable.baseline_inbox_24; + } else if (feed.id == Feed.RECENTLY_READ && !feed.is_cat) { + return R.drawable.baseline_restore_24; + } else if (feed.is_cat) { + return R.drawable.baseline_folder_open_24; + } else if (feed.id < -10 && !feed.is_cat) { + return R.drawable.baseline_label_24; + } else { + return R.drawable.rss; + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + + // Can't access ViewModels from detached fragment (= backstack) + if (isAdded()) { + String[] filter = new String[]{"sort_feeds_by_unread", "show_unread_only"}; + + if (Arrays.asList(filter).contains(key)) + refresh(); + } + } - if (m_loadingProgress != null && !isLoading) - m_loadingProgress.setVisibility(View.GONE); - } - }); - - model.getFeeds().observe(getActivity(), feeds -> { - Log.d(TAG, "observed feeds size=" + feeds.size()); - - if (isAdded()) { - onFeedsLoaded(feeds); - - if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { - if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(true); - } else { - if (model.getLastErrorMessage() != null) { - m_activity.toast(getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); - } else { - m_activity.toast(model.getErrorMessage()); - } - } - } - } - }); - - return view; - } - - protected FeedsModel getModel() { - return new ViewModelProvider(this).get(FeedsModel.class); - } - - protected void onFeedsLoaded(List<Feed> loadedFeeds) { - List<Feed> feedsWork = new ArrayList<>(); - - if (m_enableParentBtn) { - feedsWork.add(0, new Feed(Feed.TYPE_GOBACK)); - - if (m_rootFeed.id >= 0 && !loadedFeeds.isEmpty()) { - Feed feed = new Feed(m_rootFeed.id, m_rootFeed.title, true); - - feed.unread = loadedFeeds.stream().map(a -> a.unread).reduce(0, Integer::sum); - feed.always_open_headlines = true; - - feedsWork.add(1, feed); - } - } else if (m_rootFeed.id == Feed.ALL_ARTICLES) { - // if all articles feed is requested as a root element (no parent button) let's filter - // labels out so this is at least somewhat readable, instead we'll insert a link to this category to the top - loadedFeeds = loadedFeeds.stream().filter(a -> a.id >= -10).collect(Collectors.toList()); - - loadedFeeds.add(0, new Feed(Feed.CAT_LABELS, getString(R.string.cat_labels), true)); - } - - feedsWork.addAll(loadedFeeds); - - feedsWork.add(new Feed(Feed.TYPE_DIVIDER)); - feedsWork.add(new Feed(Feed.TYPE_TOGGLE_UNREAD, getString(R.string.unread_only), true)); - - m_adapter.submitList(feedsWork); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_prefs.registerOnSharedPreferenceChangeListener(this); - - m_activity = (MasterActivity)activity; - } - - @Override - public void onResume() { - super.onResume(); - - Log.d(TAG, "onResume"); - - setSelectedFeed(m_activity.getActiveFeed()); - - refresh(); - } - - public void refresh() { - if (!isAdded()) - return; - - getModel().startLoading(m_rootFeed); - } - - private class FeedViewHolder extends RecyclerView.ViewHolder { - - private View view; - private ImageView icon; - private TextView title; - private TextView unreadCounter; - private MaterialSwitch rowSwitch; - - public FeedViewHolder(@NonNull View itemView) { - super(itemView); - - view = itemView; - icon = itemView.findViewById(R.id.icon); - title = itemView.findViewById(R.id.title); - unreadCounter = itemView.findViewById(R.id.unread_counter); - rowSwitch = itemView.findViewById(R.id.row_switch); - } - } - - private class FeedDiffUtilItemCallback extends DiffUtil.ItemCallback<Feed> { - - @Override - public boolean areItemsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { - return oldItem.id == newItem.id; - } - - @Override - public boolean areContentsTheSame(@NonNull Feed oldItem, @NonNull Feed newItem) { - return oldItem.id == newItem.id && - oldItem.is_cat == newItem.is_cat && - oldItem.title.equals(newItem.title) && - oldItem.unread == newItem.unread && - oldItem.update_interval != newItem.update_interval && - oldItem.last_error.equals(newItem.last_error); - } - } - - protected class FeedsAdapter extends ListAdapter<Feed, FeedViewHolder> { - public static final int VIEW_NORMAL = 0; - public static final int VIEW_SELECTED = 1; - public static final int VIEW_GOBACK = 2; - public static final int VIEW_TOGGLE_UNREAD = 4; - public static final int VIEW_DIVIDER = 5; - - protected FeedsAdapter() { - super(new FeedDiffUtilItemCallback()); - } - - @NonNull - @Override - public FeedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - int layoutId = R.layout.feeds_row; - - switch (viewType) { - case VIEW_SELECTED: - layoutId = R.layout.feeds_row_selected; - break; - case VIEW_GOBACK: - layoutId = R.layout.feeds_row_goback; - break; - case VIEW_TOGGLE_UNREAD: - layoutId = R.layout.feeds_row_toggle; - break; - case VIEW_DIVIDER: - layoutId = R.layout.feeds_row_divider; - break; - } - - return new FeedViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull FeedViewHolder holder, int position) { - Feed feed = getItem(position); - - if (holder.icon != null) { - holder.icon.setImageResource(getIconForFeed(feed)); - } - - if (holder.title != null) { - holder.title.setText(feed.title); - - if (feed.always_open_headlines || (!feed.is_cat && feed.id == -4)) { - holder.title.setTypeface(null, Typeface.BOLD); - } else { - holder.title.setTypeface(null, Typeface.NORMAL); - } - - TypedValue tv = new TypedValue(); - m_activity.getTheme().resolveAttribute(feed.last_error.isEmpty() ? R.attr.colorOnSurface : R.attr.colorError, tv, true); - - holder.title.setTextColor(ColorStateList.valueOf(ContextCompat.getColor(m_activity, tv.resourceId))); - - holder.title.setAlpha(feed.update_interval == -1 ? 0.5f : 1f); - } - - if (holder.unreadCounter != null) { - holder.unreadCounter.setText(String.valueOf(feed.unread)); - holder.unreadCounter.setVisibility((feed.unread > 0) ? View.VISIBLE : View.INVISIBLE); - } - - // there's only one kind of row with checkbox atm - if (holder.rowSwitch != null) { - holder.rowSwitch.setChecked(m_activity.getUnreadOnly()); - - holder.rowSwitch.setOnCheckedChangeListener((button, isChecked) -> { - m_activity.setUnreadOnly(isChecked); - }); - } - - holder.view.setOnLongClickListener(view -> { - if (feed.id != Feed.TYPE_TOGGLE_UNREAD && feed.id != Feed.TYPE_DIVIDER && feed.id != Feed.TYPE_GOBACK && feed.id != Feed.ALL_ARTICLES) { - m_list.showContextMenuForChild(view); - } - return true; - }); - - // default open handler (i.e. tap) - holder.view.setOnClickListener(view -> { - if (feed.id == Feed.TYPE_GOBACK) { - m_activity.getSupportFragmentManager().popBackStack(); - } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD || feed.id == Feed.TYPE_DIVIDER) { - // - } else { - Feed tmpFeed = new Feed(feed); - - if (!neverOpenHeadlines(feed) && !tmpFeed.always_open_headlines) - tmpFeed.always_open_headlines = m_prefs.getBoolean("browse_cats_like_feeds", false); - - m_activity.onFeedSelected(tmpFeed); - } - }); - } - - @Override - public int getItemViewType(int position) { - Feed feed = getItem(position); - - if (feed.id == Feed.TYPE_GOBACK) { - return VIEW_GOBACK; - } else if (feed.id == Feed.TYPE_DIVIDER) { - return VIEW_DIVIDER; - } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { - return VIEW_TOGGLE_UNREAD; - } else if (feed.equals(m_selectedFeed)) { - return VIEW_SELECTED; - } else { - return VIEW_NORMAL; - } - } - - public int getPositionOf(Feed feed) { - if (feed != null) { - List<Feed> feeds = getCurrentList(); - - return IntStream.range(0, feeds.size()) - .sequential() - .filter(i -> { - Feed f = feeds.get(i); - - return f.id == feed.id && f.is_cat == feed.is_cat; - }) - .findFirst() - .orElse(-1); - } - - return -1; - } - } - - /** we always show Labels and Special contents, regardless of the setting */ - private boolean neverOpenHeadlines(Feed feed) { - return feed.id == Feed.CAT_SPECIAL || feed.id == Feed.CAT_LABELS; - } - - protected int getIconForFeed(Feed feed) { - if (feed.id == Feed.TYPE_GOBACK) { - return R.drawable.baseline_arrow_back_24; - } else if (feed.id == Feed.CAT_LABELS && feed.is_cat) { - return R.drawable.baseline_label_24; - } else if (feed.id == Feed.CAT_SPECIAL && feed.is_cat) { - return R.drawable.baseline_folder_special_24; - } else if (feed.id == Feed.TYPE_TOGGLE_UNREAD) { - return R.drawable.baseline_filter_alt_24; - } else if (feed.id == Feed.ARCHIVED && !feed.is_cat) { - return R.drawable.baseline_archive_24; - } else if (feed.id == Feed.MARKED && !feed.is_cat) { - return R.drawable.baseline_star_24; - } else if (feed.id == Feed.PUBLISHED && !feed.is_cat) { - return R.drawable.rss; - } else if (feed.id == Feed.FRESH && !feed.is_cat) { - return R.drawable.baseline_local_fire_department_24; - } else if (feed.id == Feed.ALL_ARTICLES && !feed.is_cat) { - return R.drawable.baseline_inbox_24; - } else if (feed.id == Feed.RECENTLY_READ && !feed.is_cat) { - return R.drawable.baseline_restore_24; - } else if (feed.is_cat) { - return R.drawable.baseline_folder_open_24; - } else if (feed.id < -10 && !feed.is_cat) { - return R.drawable.baseline_label_24; - } else { - return R.drawable.rss; - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - - // Can't access ViewModels from detached fragment (= backstack) - if (isAdded()) { - String[] filter = new String[] { "sort_feeds_by_unread", "show_unread_only" }; - - if (Arrays.asList(filter).contains(key)) - refresh(); - } - } - - public void setSelectedFeed(Feed feed) { + public void setSelectedFeed(Feed feed) { if (m_adapter != null) { - int oldPosition = -1; + int oldPosition = -1; - if (m_selectedFeed != null) - oldPosition = m_adapter.getPositionOf(m_selectedFeed); + if (m_selectedFeed != null) + oldPosition = m_adapter.getPositionOf(m_selectedFeed); - int newPosition = m_adapter.getPositionOf(feed); + int newPosition = m_adapter.getPositionOf(feed); - m_selectedFeed = feed; + m_selectedFeed = feed; - if (oldPosition != -1) - m_adapter.notifyItemChanged(oldPosition); + if (oldPosition != -1) + m_adapter.notifyItemChanged(oldPosition); - if (newPosition != -1) - m_adapter.notifyItemChanged(newPosition); + if (newPosition != -1) + m_adapter.notifyItemChanged(newPosition); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsModel.java index d9340e33..c6c68281 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/FeedsModel.java @@ -94,12 +94,12 @@ public class FeedsModel extends AndroidViewModel implements ApiCommon.ApiCaller m_isLoading.postValue(true); m_executor.execute(() -> { - final HashMap<String,String> params = new HashMap<>(); + final HashMap<String, String> params = new HashMap<>(); params.put("op", "getFeeds"); params.put("cat_id", String.valueOf(m_feed.id)); params.put("include_nested", "true"); - params.put("sid", ((org.fox.ttrss.Application)getApplication()).getSessionId()); + params.put("sid", ((org.fox.ttrss.Application) getApplication()).getSessionId()); final JsonElement result = ApiCommon.performRequest(getApplication(), params, this); @@ -112,18 +112,18 @@ public class FeedsModel extends AndroidViewModel implements ApiCommon.ApiCaller JsonArray content = result.getAsJsonArray(); if (content != null) { - Type listType = new TypeToken<List<Feed>>() {}.getType(); + Type listType = new TypeToken<List<Feed>>() { + }.getType(); List<Feed> feedsJson = new Gson().fromJson(content, listType); // seems to be necessary evil because of deserialization feedsJson = feedsJson.stream().peek(Feed::fixNullFields).collect(Collectors.toList()); - - if (unreadOnly && m_feed.id != Feed.CAT_SPECIAL) - feedsJson = feedsJson.stream() - .filter(f -> f.unread > 0) - .collect(Collectors.toList()); + if (unreadOnly && m_feed.id != Feed.CAT_SPECIAL) + feedsJson = feedsJson.stream() + .filter(f -> f.unread > 0) + .collect(Collectors.toList()); sortFeeds(feedsJson, m_feed, null); @@ -148,7 +148,9 @@ public class FeedsModel extends AndroidViewModel implements ApiCommon.ApiCaller return m_lastUpdate; } - public LiveData<Boolean> getIsLoading() { return m_isLoading; } + public LiveData<Boolean> getIsLoading() { + return m_isLoading; + } public LiveData<List<Feed>> getFeeds() { return m_feeds; @@ -185,8 +187,7 @@ public class FeedsModel extends AndroidViewModel implements ApiCommon.ApiCaller return a.order_id - b.order_id; else return a.title.toUpperCase().compareTo(b.title.toUpperCase()); - else - if (a.id < CommonActivity.LABEL_BASE_INDEX && b.id < CommonActivity.LABEL_BASE_INDEX) + else if (a.id < CommonActivity.LABEL_BASE_INDEX && b.id < CommonActivity.LABEL_BASE_INDEX) return a.title.toUpperCase().compareTo(b.title.toUpperCase()); else return a.id - b.id; @@ -260,6 +261,6 @@ public class FeedsModel extends AndroidViewModel implements ApiCommon.ApiCaller } catch (IllegalArgumentException e) { // } - } + } }
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java index 750f85cd..61fd139e 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java @@ -115,7 +115,7 @@ public class GalleryModel extends AndroidViewModel { m_items.postValue(checkList); } else { - if (!isDataUri(src)) { + if (!isDataUri(src)) { checkList.add(new GalleryEntry(src, GalleryEntry.GalleryEntryType.TYPE_VIDEO, poster)); m_items.postValue(checkList); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java index 4c8ad1ed..b95e26f5 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java @@ -220,7 +220,7 @@ public class GalleryVideoFragment extends GalleryBaseFragment { android.view.ViewGroup.LayoutParams lp = surfaceView.getLayoutParams(); lp.width = (int) containerWidth; lp.height = (int) ((videoHeight / videoWidth) * containerWidth); - if(lp.height > containerHeight) { + if (lp.height > containerHeight) { lp.width = (int) ((videoWidth / videoHeight) * containerHeight); lp.height = (int) containerHeight; } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java index 52498083..0a838251 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesEventListener.java @@ -3,7 +3,9 @@ package org.fox.ttrss; import org.fox.ttrss.types.Article; public interface HeadlinesEventListener { - void onArticleSelected(Article article); - void onHeadlinesLoaded(boolean appended); - void onHeadlinesLoadingProgress(int progress); + void onArticleSelected(Article article); + + void onHeadlinesLoaded(boolean appended); + + void onHeadlinesLoadingProgress(int progress); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java index a0de816f..297ed9ed 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java @@ -90,48 +90,48 @@ import java.util.TimeZone; public class HeadlinesFragment extends androidx.fragment.app.Fragment { - private boolean m_isLazyLoading; + private boolean m_isLazyLoading; - public void notifyItemChanged(int position) { - if (m_adapter != null) - m_adapter.notifyItemChanged(position); - } + public void notifyItemChanged(int position) { + if (m_adapter != null) + m_adapter.notifyItemChanged(position); + } public static final int FLAVOR_IMG_MIN_SIZE = 128; - private final String TAG = this.getClass().getSimpleName(); + private final String TAG = this.getClass().getSimpleName(); - private Feed m_feed; + private Feed m_feed; - private String m_searchQuery = ""; + private String m_searchQuery = ""; - private SharedPreferences m_prefs; + private SharedPreferences m_prefs; - private ArticleListAdapter m_adapter; - private final List<Article> m_readArticles = new ArrayList<>(); - private HeadlinesEventListener m_listener; - private OnlineActivity m_activity; - private SwipeRefreshLayout m_swipeLayout; + private ArticleListAdapter m_adapter; + private final List<Article> m_readArticles = new ArrayList<>(); + private HeadlinesEventListener m_listener; + private OnlineActivity m_activity; + private SwipeRefreshLayout m_swipeLayout; private boolean m_compactLayoutMode = false; private RecyclerView m_list; - private LinearLayoutManager m_layoutManager; - private HeadlinesFragmentModel m_headlinesFragmentModel; + private LinearLayoutManager m_layoutManager; + private HeadlinesFragmentModel m_headlinesFragmentModel; - private MediaPlayer m_mediaPlayer; - private TextureView m_activeTexture; + private MediaPlayer m_mediaPlayer; + private TextureView m_activeTexture; - public void initialize(Feed feed) { - m_feed = feed; - } + public void initialize(Feed feed) { + m_feed = feed; + } - public void initialize(Feed feed, boolean compactMode) { - m_feed = feed; - m_compactLayoutMode = compactMode; - } + public void initialize(Feed feed, boolean compactMode) { + m_feed = feed; + m_compactLayoutMode = compactMode; + } - public boolean onArticleMenuItemSelected(MenuItem item, Article article, int position) { + public boolean onArticleMenuItemSelected(MenuItem item, Article article, int position) { - if (article == null) return false; + if (article == null) return false; int itemId = item.getItemId(); if (itemId == R.id.article_set_labels) { @@ -141,7 +141,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.editArticleNote(article); return true; } else if (itemId == R.id.headlines_article_unread) { - Article articleClone = new Article(article); + Article articleClone = new Article(article); articleClone.unread = !articleClone.unread; m_activity.saveArticleUnread(articleClone); @@ -154,42 +154,42 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_activity.openUri(Uri.parse(article.link)); if (article.unread) { - Article articleClone = new Article(article); - articleClone.unread = !articleClone.unread; + Article articleClone = new Article(article); + articleClone.unread = !articleClone.unread; - m_activity.saveArticleUnread(articleClone); - } + m_activity.saveArticleUnread(articleClone); + } return true; } else if (itemId == R.id.headlines_share_article) { m_activity.shareArticle(article); return true; } else if (itemId == R.id.catchup_above) { - m_activity.confirmCatchupAbove(article); + m_activity.confirmCatchupAbove(article); return true; } Log.d(TAG, "onArticleMenuItemSelected, unhandled id=" + item.getItemId()); return false; } - // all onContextItemSelected are invoked in sequence so we might get a context menu for headlines, etc - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); + // all onContextItemSelected are invoked in sequence so we might get a context menu for headlines, etc + public boolean onContextItemSelected(MenuItem item) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo) item + .getMenuInfo(); - if (info != null) { - try { - Article article = Application.getArticles().get(info.position); + if (info != null) { + try { + Article article = Application.getArticles().get(info.position); - if (!onArticleMenuItemSelected(item, article, info.position)) - return super.onContextItemSelected(item); - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - } + if (!onArticleMenuItemSelected(item, article, info.position)) + return super.onContextItemSelected(item); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } + Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); + return super.onContextItemSelected(item); + } public HeadlinesFragment() { super(); @@ -200,899 +200,909 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { setReenterTransition(fade); } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { - getActivity().getMenuInflater().inflate(R.menu.context_headlines, menu); + getActivity().getMenuInflater().inflate(R.menu.context_headlines, menu); - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - Article article = m_adapter.getCurrentList().get(info.position); + Article article = m_adapter.getCurrentList().get(info.position); - menu.setHeaderTitle(article.title); + menu.setHeaderTitle(article.title); - menu.findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); - menu.findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); + menu.findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); + menu.findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); - super.onCreateContextMenu(menu, v, menuInfo); - } + super.onCreateContextMenu(menu, v, menuInfo); + } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - m_feed = savedInstanceState.getParcelable("m_feed"); - m_searchQuery = savedInstanceState.getString("m_searchQuery"); - m_compactLayoutMode = savedInstanceState.getBoolean("m_compactLayoutMode"); - } + if (savedInstanceState != null) { + m_feed = savedInstanceState.getParcelable("m_feed"); + m_searchQuery = savedInstanceState.getString("m_searchQuery"); + m_compactLayoutMode = savedInstanceState.getBoolean("m_compactLayoutMode"); + } - setRetainInstance(true); + setRetainInstance(true); - Glide.get(getContext()).clearMemory(); - } + Glide.get(getContext()).clearMemory(); + } - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); - out.putParcelable("m_feed", m_feed); - out.putString("m_searchQuery", m_searchQuery); - out.putBoolean("m_compactLayoutMode", m_compactLayoutMode); - } + out.putParcelable("m_feed", m_feed); + out.putString("m_searchQuery", m_searchQuery); + out.putBoolean("m_compactLayoutMode", m_compactLayoutMode); + } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Log.d(TAG, "onCreateView"); + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Log.d(TAG, "onCreateView"); - m_headlinesFragmentModel = new ViewModelProvider(this).get(HeadlinesFragmentModel.class); + m_headlinesFragmentModel = new ViewModelProvider(this).get(HeadlinesFragmentModel.class); - String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); + String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); if ("HL_COMPACT".equals(headlineMode) || "HL_COMPACT_NOIMAGES".equals(headlineMode)) m_compactLayoutMode = true; - DisplayMetrics metrics = new DisplayMetrics(); - getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); + DisplayMetrics metrics = new DisplayMetrics(); + getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); - View view = inflater.inflate(R.layout.fragment_headlines, container, false); + View view = inflater.inflate(R.layout.fragment_headlines, container, false); - m_swipeLayout = view.findViewById(R.id.headlines_swipe_container); + m_swipeLayout = view.findViewById(R.id.headlines_swipe_container); - // see below re: viewpager2 - if (!(m_activity instanceof DetailActivity)) - m_swipeLayout.setOnRefreshListener(() -> refresh(false)); - else - m_swipeLayout.setEnabled(false); + // see below re: viewpager2 + if (!(m_activity instanceof DetailActivity)) + m_swipeLayout.setOnRefreshListener(() -> refresh(false)); + else + m_swipeLayout.setEnabled(false); - m_list = view.findViewById(R.id.headlines_list); - registerForContextMenu(m_list); + m_list = view.findViewById(R.id.headlines_list); + registerForContextMenu(m_list); - m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext()); - m_list.setLayoutManager(m_layoutManager); - m_list.setItemAnimator(new DefaultItemAnimator()); + m_layoutManager = new LinearLayoutManager(m_activity.getApplicationContext()); + m_list.setLayoutManager(m_layoutManager); + m_list.setItemAnimator(new DefaultItemAnimator()); - m_adapter = new ArticleListAdapter(); - m_list.setAdapter(m_adapter); + m_adapter = new ArticleListAdapter(); + m_list.setAdapter(m_adapter); - if (savedInstanceState == null && Application.getArticles().isEmpty()) { - refresh(false); - } + if (savedInstanceState == null && Application.getArticles().isEmpty()) { + refresh(false); + } - // we disable this because default implementationof viewpager2 does not support removing/reordering/changing items - // https://stackoverflow.com/questions/69368198/delete-item-in-android-viewpager2 - if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) && !(m_activity instanceof DetailActivity)) { + // we disable this because default implementationof viewpager2 does not support removing/reordering/changing items + // https://stackoverflow.com/questions/69368198/delete-item-in-android-viewpager2 + if (m_prefs.getBoolean("headlines_swipe_to_dismiss", true) && !(m_activity instanceof DetailActivity)) { - ItemTouchHelper swipeHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { + ItemTouchHelper swipeHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { - @Override - public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { - return false; - } + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } - @Override - public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + @Override + public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - int position = viewHolder.getBindingAdapterPosition(); + int position = viewHolder.getBindingAdapterPosition(); - try { - Article article = Application.getArticles().get(position); + try { + Article article = Application.getArticles().get(position); - if (article == null || article.id < 0) - return 0; - } catch (IndexOutOfBoundsException e) { - return 0; - } + if (article == null || article.id < 0) + return 0; + } catch (IndexOutOfBoundsException e) { + return 0; + } - return super.getSwipeDirs(recyclerView, viewHolder); - } + return super.getSwipeDirs(recyclerView, viewHolder); + } - @Override - public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - final int adapterPosition = viewHolder.getBindingAdapterPosition(); + final int adapterPosition = viewHolder.getBindingAdapterPosition(); try { - final Article article = Application.getArticles().get(adapterPosition); - final boolean wasUnread; + final Article article = Application.getArticles().get(adapterPosition); + final boolean wasUnread; - if (article != null && article.id > 0) { - if (article.unread) { - wasUnread = true; + if (article != null && article.id > 0) { + if (article.unread) { + wasUnread = true; - article.unread = false; - m_activity.saveArticleUnread(article); - } else { - wasUnread = false; - } + article.unread = false; + m_activity.saveArticleUnread(article); + } else { + wasUnread = false; + } - List<Article> tmpRemove = new ArrayList<>(Application.getArticles()); - tmpRemove.remove(adapterPosition); + List<Article> tmpRemove = new ArrayList<>(Application.getArticles()); + tmpRemove.remove(adapterPosition); - Application.getArticlesModel().update(tmpRemove); + Application.getArticlesModel().update(tmpRemove); - Snackbar.make(m_list, R.string.headline_undo_row_prompt, Snackbar.LENGTH_LONG) - .setAction(getString(R.string.headline_undo_row_button), v -> { + Snackbar.make(m_list, R.string.headline_undo_row_prompt, Snackbar.LENGTH_LONG) + .setAction(getString(R.string.headline_undo_row_button), v -> { if (wasUnread) { article.unread = true; m_activity.saveArticleUnread(article); } - List<Article> tmpInsert = new ArrayList<>(Application.getArticles()); - tmpInsert.add(adapterPosition, article); + List<Article> tmpInsert = new ArrayList<>(Application.getArticles()); + tmpInsert.add(adapterPosition, article); - Application.getArticlesModel().update(tmpInsert); + Application.getArticlesModel().update(tmpInsert); }).show(); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - }); + } - swipeHelper.attachToRecyclerView(m_list); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); - } + swipeHelper.attachToRecyclerView(m_list); - m_list.setOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { - super.onScrollStateChanged(recyclerView, newState); + } - ArticleModel model = Application.getArticlesModel(); + m_list.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); - if (newState == RecyclerView.SCROLL_STATE_IDLE) { - if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - Log.d(TAG, "marking articles as read, count=" + m_readArticles.size()); + ArticleModel model = Application.getArticlesModel(); - // since we clear the list after we send the batch to mark as read, we need to pass a cloned arraylist here, - // otherwise nothing would get marked as read when async operation completes - m_activity.setArticlesUnread(new ArrayList<>(m_readArticles), Article.UPDATE_SET_FALSE); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + if (!m_readArticles.isEmpty() && !m_isLazyLoading && !model.isLoading() && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + Log.d(TAG, "marking articles as read, count=" + m_readArticles.size()); - m_readArticles.clear(); + // since we clear the list after we send the batch to mark as read, we need to pass a cloned arraylist here, + // otherwise nothing would get marked as read when async operation completes + m_activity.setArticlesUnread(new ArrayList<>(m_readArticles), Article.UPDATE_SET_FALSE); - new Handler().postDelayed(() -> m_activity.refresh(false), 100); - } - } - } + m_readArticles.clear(); - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); + new Handler().postDelayed(() -> m_activity.refresh(false), 100); + } + } + } - int firstVisibleItem = m_layoutManager.findFirstVisibleItemPosition(); - int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); - // Log.d(TAG, "onScrolled: FVI=" + firstVisibleItem + " LVI=" + lastVisibleItem); + int firstVisibleItem = m_layoutManager.findFirstVisibleItemPosition(); + int lastVisibleItem = m_layoutManager.findLastVisibleItemPosition(); - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - for (int i = 0; i < firstVisibleItem; i++) { - try { - Article article = Application.getArticles().get(i); + // Log.d(TAG, "onScrolled: FVI=" + firstVisibleItem + " LVI=" + lastVisibleItem); - if (article.unread && !m_readArticles.contains(article)) - m_readArticles.add(new Article(article)); + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + for (int i = 0; i < firstVisibleItem; i++) { + try { + Article article = Application.getArticles().get(i); - } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); - } - } + if (article.unread && !m_readArticles.contains(article)) + m_readArticles.add(new Article(article)); - // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); - } + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + } - ArticleModel model = Application.getArticlesModel(); + // Log.d(TAG, "pending to auto mark as read count=" + m_readArticles.size()); + } - if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.isLazyLoadEnabled() && - lastVisibleItem >= Application.getArticles().size() - 5) { + ArticleModel model = Application.getArticlesModel(); - Log.d(TAG, "attempting to lazy load more articles..."); + if (dy > 0 && !m_isLazyLoading && !model.isLoading() && model.isLazyLoadEnabled() && + lastVisibleItem >= Application.getArticles().size() - 5) { - m_isLazyLoading = true; + Log.d(TAG, "attempting to lazy load more articles..."); - // this has to be dispatched delayed, consequent adapter updates are forbidden in scroll handler - new Handler().postDelayed(() -> refresh(true), 250); - } - } - }); + m_isLazyLoading = true; - ArticleModel model = Application.getArticlesModel(); + // this has to be dispatched delayed, consequent adapter updates are forbidden in scroll handler + new Handler().postDelayed(() -> refresh(true), 250); + } + } + }); - model.getIsLoading().observe(getActivity(), isLoading -> { - Log.d(TAG, "observed headlines isLoading=" + isLoading + " lazyLoadEnabled=" + model.isLazyLoadEnabled()); + ArticleModel model = Application.getArticlesModel(); - if (m_swipeLayout != null) - m_swipeLayout.setRefreshing(isLoading); - }); + model.getIsLoading().observe(getActivity(), isLoading -> { + Log.d(TAG, "observed headlines isLoading=" + isLoading + " lazyLoadEnabled=" + model.isLazyLoadEnabled()); - // this gets notified on loading % - model.getLoadingProgress().observe(getActivity(), progress -> { - Log.d(TAG, "observed headlines loading progress=" + progress); + if (m_swipeLayout != null) + m_swipeLayout.setRefreshing(isLoading); + }); - m_listener.onHeadlinesLoadingProgress(progress); - }); + // this gets notified on loading % + model.getLoadingProgress().observe(getActivity(), progress -> { + Log.d(TAG, "observed headlines loading progress=" + progress); - // this gets notified if active article changes - model.getActive().observe(getActivity(), (activeArticle) -> { - Log.d(TAG, "observed active article=" + activeArticle); + m_listener.onHeadlinesLoadingProgress(progress); + }); - if (activeArticle != null) { - scrollToArticle(activeArticle); - } - }); + // this gets notified if active article changes + model.getActive().observe(getActivity(), (activeArticle) -> { + Log.d(TAG, "observed active article=" + activeArticle); - // this gets notified on network update - model.getUpdatesData().observe(getActivity(), lastUpdate -> { - if (lastUpdate > 0) { - List<Article> tmp = new ArrayList<>(model.getArticles().getValue()); + if (activeArticle != null) { + scrollToArticle(activeArticle); + } + }); - Log.d(TAG, "observed headlines last update=" + lastUpdate + " article count=" + tmp.size()); + // this gets notified on network update + model.getUpdatesData().observe(getActivity(), lastUpdate -> { + if (lastUpdate > 0) { + List<Article> tmp = new ArrayList<>(model.getArticles().getValue()); - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + Log.d(TAG, "observed headlines last update=" + lastUpdate + " article count=" + tmp.size()); - final boolean appended = model.getAppend(); + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - m_adapter.submitList(tmp, () -> { - if (!appended) - m_list.scrollToPosition(0); + final boolean appended = model.getAppend(); - m_isLazyLoading = false; + m_adapter.submitList(tmp, () -> { + if (!appended) + m_list.scrollToPosition(0); - m_listener.onHeadlinesLoaded(appended); - }); + m_isLazyLoading = false; - if (model.getFirstIdChanged()) - Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) - .setAction(R.string.reload, v -> refresh(false)).show(); + m_listener.onHeadlinesLoaded(appended); + }); - if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { + if (model.getFirstIdChanged()) + Snackbar.make(getView(), R.string.headlines_row_top_changed, Snackbar.LENGTH_LONG) + .setAction(R.string.reload, v -> refresh(false)).show(); - m_isLazyLoading = false; + if (model.getLastError() != null && model.getLastError() != ApiCommon.ApiError.SUCCESS) { - if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { - m_activity.login(); - return; - } + m_isLazyLoading = false; - m_listener.onHeadlinesLoaded(appended); + if (model.getLastError() == ApiCommon.ApiError.LOGIN_FAILED) { + m_activity.login(); + return; + } - if (model.getLastErrorMessage() != null) { - m_activity.toast(m_activity.getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); - } else { - m_activity.toast(model.getErrorMessage()); - } - } - } - }); + m_listener.onHeadlinesLoaded(appended); - // loaded articles might get modified for all sorts of reasons - model.getArticles().observe(getActivity(), articles -> { - Log.d(TAG, "observed headlines article list size=" + articles.size()); + if (model.getLastErrorMessage() != null) { + m_activity.toast(m_activity.getString(model.getErrorMessage()) + "\n" + model.getLastErrorMessage()); + } else { + m_activity.toast(model.getErrorMessage()); + } + } + } + }); - List<Article> tmp = new ArrayList<>(articles); + // loaded articles might get modified for all sorts of reasons + model.getArticles().observe(getActivity(), articles -> { + Log.d(TAG, "observed headlines article list size=" + articles.size()); - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + List<Article> tmp = new ArrayList<>(articles); - m_adapter.submitList(tmp); - }); + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - return view; - } + m_adapter.submitList(tmp); + }); - @Override - public void onResume() { - super.onResume(); + return view; + } - Log.d(TAG, "onResume"); + @Override + public void onResume() { + super.onResume(); - syncToSharedArticles(); + Log.d(TAG, "onResume"); - m_activity.invalidateOptionsMenu(); - } + syncToSharedArticles(); - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_activity = (OnlineActivity) activity; - m_listener = (HeadlinesEventListener) activity; - } + m_activity.invalidateOptionsMenu(); + } - public void refresh(final boolean append) { - ArticleModel model = Application.getArticlesModel(); + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); + m_activity = (OnlineActivity) activity; + m_listener = (HeadlinesEventListener) activity; + } - // we do not support non-append refreshes while in DetailActivity because of viewpager2 - if (m_activity instanceof DetailActivity && !append) - return; + public void refresh(final boolean append) { + ArticleModel model = Application.getArticlesModel(); - if (!append) { - model.setActive(null); - model.setSelection(ArticleModel.ArticlesSelection.NONE); - } + // we do not support non-append refreshes while in DetailActivity because of viewpager2 + if (m_activity instanceof DetailActivity && !append) + return; - model.startLoading(append, m_feed, m_activity.getResizeWidth()); - } + if (!append) { + model.setActive(null); + model.setSelection(ArticleModel.ArticlesSelection.NONE); + } - static class ArticleViewHolder extends RecyclerView.ViewHolder { - public View view; + model.startLoading(append, m_feed, m_activity.getResizeWidth()); + } - public TextView titleView; - public TextView feedTitleView; - public MaterialButton markedView; - public MaterialButton scoreView; - public MaterialButton publishedView; - public TextView excerptView; - public ImageView flavorImageView; - public ImageView flavorVideoKindView; - public TextView authorView; - public TextView dateView; - public CheckBox selectionBoxView; - public MaterialButton menuButtonView; - public ViewGroup flavorImageHolder; - public ProgressBar flavorImageLoadingBar; + static class ArticleViewHolder extends RecyclerView.ViewHolder { + public View view; + + public TextView titleView; + public TextView feedTitleView; + public MaterialButton markedView; + public MaterialButton scoreView; + public MaterialButton publishedView; + public TextView excerptView; + public ImageView flavorImageView; + public ImageView flavorVideoKindView; + public TextView authorView; + public TextView dateView; + public CheckBox selectionBoxView; + public MaterialButton menuButtonView; + public ViewGroup flavorImageHolder; + public ProgressBar flavorImageLoadingBar; public View headlineFooter; public ImageView textImage; public ImageView textChecked; - public View headlineHeader; - public View flavorImageOverflow; - public TextureView flavorVideoView; - public MaterialButton attachmentsView; - public TextView linkHost; - - public ArticleViewHolder(View v) { - super(v); - - view = v; - - titleView = v.findViewById(R.id.title); - - feedTitleView = v.findViewById(R.id.feed_title); - markedView = v.findViewById(R.id.marked); - scoreView = v.findViewById(R.id.score); - publishedView = v.findViewById(R.id.published); - excerptView = v.findViewById(R.id.excerpt); - flavorImageView = v.findViewById(R.id.flavor_image); - flavorVideoKindView = v.findViewById(R.id.flavor_video_kind); - authorView = v.findViewById(R.id.author); - dateView = v.findViewById(R.id.date); - selectionBoxView = v.findViewById(R.id.selected); - menuButtonView = v.findViewById(R.id.article_menu_button); - flavorImageHolder = v.findViewById(R.id.flavor_image_holder); - flavorImageLoadingBar = v.findViewById(R.id.flavor_image_progressbar); - textImage = v.findViewById(R.id.text_image); - textChecked = v.findViewById(R.id.text_checked); - headlineHeader = v.findViewById(R.id.headline_header); - flavorImageOverflow = v.findViewById(R.id.gallery_overflow); - flavorVideoView = v.findViewById(R.id.flavor_video); - attachmentsView = v.findViewById(R.id.attachments); - linkHost = v.findViewById(R.id.link_host); - } - } - - private static class FlavorProgressTarget<Z> extends ProgressTarget<String, Z> { - private final ArticleViewHolder holder; - public FlavorProgressTarget(Target<Z> target, String model, ArticleViewHolder holder) { - super(target); - setModel(model); - this.holder = holder; - } - - @Override public float getGranualityPercentage() { - return 0.1f; // this matches the format string for #text below - } - - @Override protected void onConnecting() { - holder.flavorImageHolder.setVisibility(View.VISIBLE); - - holder.flavorImageLoadingBar.setIndeterminate(true); - holder.flavorImageLoadingBar.setVisibility(View.VISIBLE); - } - @Override protected void onDownloading(long bytesRead, long expectedLength) { - holder.flavorImageHolder.setVisibility(View.VISIBLE); - - holder.flavorImageLoadingBar.setIndeterminate(false); - holder.flavorImageLoadingBar.setProgress((int)(100 * bytesRead / expectedLength)); - } - @Override protected void onDownloaded() { - holder.flavorImageHolder.setVisibility(View.VISIBLE); - - holder.flavorImageLoadingBar.setIndeterminate(true); - } - @Override protected void onDelivered() { - holder.flavorImageHolder.setVisibility(View.VISIBLE); - - holder.flavorImageLoadingBar.setVisibility(View.INVISIBLE); - } - } - - private class ArticleListAdapter extends ListAdapter<Article, ArticleViewHolder> { - public static final int VIEW_NORMAL = 0; - public static final int VIEW_AMR_FOOTER = 1; + public View headlineHeader; + public View flavorImageOverflow; + public TextureView flavorVideoView; + public MaterialButton attachmentsView; + public TextView linkHost; + + public ArticleViewHolder(View v) { + super(v); + + view = v; + + titleView = v.findViewById(R.id.title); + + feedTitleView = v.findViewById(R.id.feed_title); + markedView = v.findViewById(R.id.marked); + scoreView = v.findViewById(R.id.score); + publishedView = v.findViewById(R.id.published); + excerptView = v.findViewById(R.id.excerpt); + flavorImageView = v.findViewById(R.id.flavor_image); + flavorVideoKindView = v.findViewById(R.id.flavor_video_kind); + authorView = v.findViewById(R.id.author); + dateView = v.findViewById(R.id.date); + selectionBoxView = v.findViewById(R.id.selected); + menuButtonView = v.findViewById(R.id.article_menu_button); + flavorImageHolder = v.findViewById(R.id.flavor_image_holder); + flavorImageLoadingBar = v.findViewById(R.id.flavor_image_progressbar); + textImage = v.findViewById(R.id.text_image); + textChecked = v.findViewById(R.id.text_checked); + headlineHeader = v.findViewById(R.id.headline_header); + flavorImageOverflow = v.findViewById(R.id.gallery_overflow); + flavorVideoView = v.findViewById(R.id.flavor_video); + attachmentsView = v.findViewById(R.id.attachments); + linkHost = v.findViewById(R.id.link_host); + } + } + + private static class FlavorProgressTarget<Z> extends ProgressTarget<String, Z> { + private final ArticleViewHolder holder; + + public FlavorProgressTarget(Target<Z> target, String model, ArticleViewHolder holder) { + super(target); + setModel(model); + this.holder = holder; + } + + @Override + public float getGranualityPercentage() { + return 0.1f; // this matches the format string for #text below + } + + @Override + protected void onConnecting() { + holder.flavorImageHolder.setVisibility(View.VISIBLE); + + holder.flavorImageLoadingBar.setIndeterminate(true); + holder.flavorImageLoadingBar.setVisibility(View.VISIBLE); + } + + @Override + protected void onDownloading(long bytesRead, long expectedLength) { + holder.flavorImageHolder.setVisibility(View.VISIBLE); + + holder.flavorImageLoadingBar.setIndeterminate(false); + holder.flavorImageLoadingBar.setProgress((int) (100 * bytesRead / expectedLength)); + } + + @Override + protected void onDownloaded() { + holder.flavorImageHolder.setVisibility(View.VISIBLE); + + holder.flavorImageLoadingBar.setIndeterminate(true); + } + + @Override + protected void onDelivered() { + holder.flavorImageHolder.setVisibility(View.VISIBLE); + + holder.flavorImageLoadingBar.setVisibility(View.INVISIBLE); + } + } + + private class ArticleListAdapter extends ListAdapter<Article, ArticleViewHolder> { + public static final int VIEW_NORMAL = 0; + public static final int VIEW_AMR_FOOTER = 1; private final ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT; private final TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round(); - private final ColorStateList m_cslTertiary; - private final ColorStateList m_cslPrimary; - private final int m_colorSurfaceContainerLowest; - private final int m_colorSurface; - private final int m_colorPrimary; - private final int m_colorTertiary; - private final int m_colorSecondary; - private final int m_colorOnSurface; - private final int m_colorTertiaryContainer; - private final int m_colorOnTertiaryContainer; - - boolean m_flavorImageEnabled; - private final int m_screenWidth; - private final int m_screenHeight; - - private final int m_headlineSmallFontSize; - private final int m_headlineFontSize; - private final boolean m_enableIconTinting; - - private final ConnectivityManager m_cmgr; - - private boolean canShowFlavorImage() { - if (m_flavorImageEnabled) { - if (m_prefs.getBoolean("headline_images_wifi_only", false)) { - // why do i have to get this service every time instead of using a member variable :( - NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - - if (wifi != null) - return wifi.isConnected(); - - } else { - return true; - } - } + private final ColorStateList m_cslTertiary; + private final ColorStateList m_cslPrimary; + private final int m_colorSurfaceContainerLowest; + private final int m_colorSurface; + private final int m_colorPrimary; + private final int m_colorTertiary; + private final int m_colorSecondary; + private final int m_colorOnSurface; + private final int m_colorTertiaryContainer; + private final int m_colorOnTertiaryContainer; + + boolean m_flavorImageEnabled; + private final int m_screenWidth; + private final int m_screenHeight; + + private final int m_headlineSmallFontSize; + private final int m_headlineFontSize; + private final boolean m_enableIconTinting; + + private final ConnectivityManager m_cmgr; + + private boolean canShowFlavorImage() { + if (m_flavorImageEnabled) { + if (m_prefs.getBoolean("headline_images_wifi_only", false)) { + // why do i have to get this service every time instead of using a member variable :( + NetworkInfo wifi = m_cmgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + + if (wifi != null) + return wifi.isConnected(); + + } else { + return true; + } + } - return false; - } + return false; + } - private int colorFromAttr(int attr) { - TypedValue tv = new TypedValue(); - m_activity.getTheme().resolveAttribute(attr, tv, true); - return ContextCompat.getColor(m_activity, tv.resourceId); - } + private int colorFromAttr(int attr) { + TypedValue tv = new TypedValue(); + m_activity.getTheme().resolveAttribute(attr, tv, true); + return ContextCompat.getColor(m_activity, tv.resourceId); + } - public ArticleListAdapter() { - super(new ArticleDiffItemCallback()); + public ArticleListAdapter() { + super(new ArticleDiffItemCallback()); - Display display = m_activity.getWindowManager().getDefaultDisplay(); - Point size = new Point(); - display.getSize(size); - m_screenHeight = size.y; - m_screenWidth = size.x; + Display display = m_activity.getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + m_screenHeight = size.y; + m_screenWidth = size.x; - String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); - m_flavorImageEnabled = "HL_DEFAULT".equals(headlineMode) || "HL_COMPACT".equals(headlineMode); + String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); + m_flavorImageEnabled = "HL_DEFAULT".equals(headlineMode) || "HL_COMPACT".equals(headlineMode); - m_colorPrimary = colorFromAttr(R.attr.colorPrimary); - m_colorSecondary = colorFromAttr(R.attr.colorSecondary); - m_colorTertiary = colorFromAttr(R.attr.colorTertiary); + m_colorPrimary = colorFromAttr(R.attr.colorPrimary); + m_colorSecondary = colorFromAttr(R.attr.colorSecondary); + m_colorTertiary = colorFromAttr(R.attr.colorTertiary); - m_cslTertiary = ColorStateList.valueOf(m_colorTertiary); - m_cslPrimary = ColorStateList.valueOf(m_colorPrimary); + m_cslTertiary = ColorStateList.valueOf(m_colorTertiary); + m_cslPrimary = ColorStateList.valueOf(m_colorPrimary); - m_colorSurfaceContainerLowest = colorFromAttr(R.attr.colorSurfaceContainerLowest); - m_colorSurface = colorFromAttr(R.attr.colorSurface); - m_colorOnSurface = colorFromAttr(R.attr.colorOnSurface); + m_colorSurfaceContainerLowest = colorFromAttr(R.attr.colorSurfaceContainerLowest); + m_colorSurface = colorFromAttr(R.attr.colorSurface); + m_colorOnSurface = colorFromAttr(R.attr.colorOnSurface); - m_colorTertiaryContainer = colorFromAttr(R.attr.colorTertiaryContainer); - m_colorOnTertiaryContainer = colorFromAttr(R.attr.colorOnTertiaryContainer); + m_colorTertiaryContainer = colorFromAttr(R.attr.colorTertiaryContainer); + m_colorOnTertiaryContainer = colorFromAttr(R.attr.colorOnTertiaryContainer); - m_headlineFontSize = m_prefs.getInt("headlines_font_size_sp_int", 13); - m_headlineSmallFontSize = Math.max(10, Math.min(18, m_headlineFontSize - 2)); + m_headlineFontSize = m_prefs.getInt("headlines_font_size_sp_int", 13); + m_headlineSmallFontSize = Math.max(10, Math.min(18, m_headlineFontSize - 2)); - m_enableIconTinting = m_prefs.getBoolean("enable_icon_tinting", true); + m_enableIconTinting = m_prefs.getBoolean("enable_icon_tinting", true); - m_cmgr = (ConnectivityManager) m_activity.getSystemService(Context.CONNECTIVITY_SERVICE); - } + m_cmgr = (ConnectivityManager) m_activity.getSystemService(Context.CONNECTIVITY_SERVICE); + } - @NonNull - @Override - public ArticleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + @NonNull + @Override + public ArticleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row; + int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row; if (viewType == VIEW_AMR_FOOTER) { layoutId = R.layout.headlines_footer; } - View v = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); - - ArticleViewHolder holder = new ArticleViewHolder(v); - - // set on click handlers once when view is created - - holder.view.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(view); - - if (position != -1) { - Article article = m_adapter.getItem(position); - - m_listener.onArticleSelected(article); - } - }); - - holder.view.setOnLongClickListener(view -> { - m_list.showContextMenuForChild(view); - return true; - }); - - // block footer clicks to make button/selection clicking easier - if (holder.headlineFooter != null) { - holder.headlineFooter.setOnClickListener(view -> { - // - }); - } - - if (holder.attachmentsView != null) { - holder.attachmentsView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = m_adapter.getItem(position); - m_activity.displayAttachments(article); - } - }); - } - - if (holder.flavorImageView != null) { - holder.flavorImageView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = m_adapter.getItem(position); - openGalleryForType(article, holder, holder.flavorImageView); - } - }); - } - - if (holder.flavorImageOverflow != null) { - holder.flavorImageOverflow.setOnClickListener(view -> { - PopupMenu popup = new PopupMenu(getContext(), holder.flavorImageOverflow); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.content_gallery_entry, popup.getMenu()); - - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = m_adapter.getItem(position); - - popup.setOnMenuItemClickListener(item -> { - - Uri mediaUri = Uri.parse(article.flavorStreamUri != null ? article.flavorStreamUri : - article.flavorImageUri); - - int itemId = item.getItemId(); - if (itemId == R.id.article_img_open) { - m_activity.openUri(mediaUri); - return true; - } else if (itemId == R.id.article_img_copy) { - m_activity.copyToClipboard(mediaUri.toString()); - return true; - } else if (itemId == R.id.article_img_share) { - m_activity.shareImageFromUri(mediaUri.toString()); - return true; - } else if (itemId == R.id.article_img_share_url) { - m_activity.shareText(mediaUri.toString()); - return true; - } else if (itemId == R.id.article_img_view_caption) { - m_activity.displayImageCaption(article.flavorImageUri, article.content); - return true; - } - return false; - }); - - popup.show(); - } - }); - - holder.flavorImageView.setOnLongClickListener(view -> { - m_list.showContextMenuForChild(holder.view); - return true; - }); - } - - if (holder.menuButtonView != null) { - holder.menuButtonView.setOnClickListener(view -> { - - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - PopupMenu popup = new PopupMenu(getContext(), view); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.context_headlines, popup.getMenu()); - - popup.getMenu().findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); - popup.getMenu().findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); - - popup.setOnMenuItemClickListener(item -> onArticleMenuItemSelected(item, - getItem(position), - m_list.getChildAdapterPosition(holder.view))); - - popup.show(); - } - }); - } - - if (holder.markedView != null) { - holder.markedView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = new Article(getItem(position)); - article.marked = !article.marked; - - m_activity.saveArticleMarked(article); - } - }); - } - - if (holder.selectionBoxView != null) { - holder.selectionBoxView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = new Article(getItem(position)); - - CheckBox cb = (CheckBox) view; - - article.selected = cb.isChecked(); - - Application.getArticlesModel().update(article); - } - }); - } - - if (holder.publishedView != null) { - holder.publishedView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = new Article(getItem(position)); - article.published = !article.published; - - m_activity.saveArticlePublished(article); - } - }); - } - - if (holder.textImage != null) { - holder.textImage.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = new Article(getItem(position)); - article.selected = !article.selected; - - Application.getArticlesModel().update(article); - } - }); - - holder.textImage.setOnLongClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); - - if (position != -1) { - Article article = getItem(position); - - openGalleryForType(article, holder, holder.textImage); - } - - return true; - }); - } + View v = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); - if (holder.scoreView != null) { - if (m_activity.getApiLevel() >= 16) { - holder.scoreView.setOnClickListener(view -> { - int position = m_list.getChildAdapterPosition(holder.view); + ArticleViewHolder holder = new ArticleViewHolder(v); - if (position != -1) { + // set on click handlers once when view is created - final Article articleClone = new Article(getItem(position)); - final EditText edit = new EditText(getActivity()); + holder.view.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(view); - edit.setText(String.valueOf(articleClone.score)); + if (position != -1) { + Article article = m_adapter.getItem(position); - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) - .setTitle(R.string.score_for_this_article) - .setPositiveButton(R.string.set_score, - (dialog, which) -> { - try { - articleClone.score = Integer.parseInt(edit.getText().toString()); - m_activity.saveArticleScore(articleClone); + m_listener.onArticleSelected(article); + } + }); - } catch (NumberFormatException e) { - m_activity.toast(R.string.score_invalid); - e.printStackTrace(); - } - }) - .setNegativeButton(getString(R.string.cancel), - (dialog, which) -> { - }).setView(edit); - - Dialog dialog = builder.create(); - dialog.show(); - } - }); - } else { - holder.scoreView.setVisibility(View.GONE); - } - } - - return holder; - } - - @Override public void onViewRecycled(@NonNull ArticleViewHolder holder){ - super.onViewRecycled(holder); - - if (holder.flavorImageView != null) - Glide.with(HeadlinesFragment.this).clear(holder.flavorImageView); - } - - @Override - // https://stackoverflow.com/questions/33176336/need-an-example-about-recyclerview-adapter-notifyitemchangedint-position-objec/50085835#50085835 - public void onBindViewHolder(@NonNull final ArticleViewHolder holder, final int position, final List<Object> payloads) { - if (!payloads.isEmpty()) { - Log.d(TAG, "onBindViewHolder, payloads=" + payloads + " position=" + position); - - final Article article = getItem(position); - - for (final Object pobject : payloads) { - ArticleDiffItemCallback.ChangePayload payload = (ArticleDiffItemCallback.ChangePayload) pobject; - - switch (payload) { - case UNREAD: - case ACTIVE: - updateUnreadView(article, holder); - break; - case MARKED: - updateMarkedView(article, holder); - break; - case SELECTED: - updateSelectedView(article, holder); - updateTextImage(article, holder); - break; - case PUBLISHED: - updatePublishedView(article, holder); - break; - case SCORE: - updateScoreView(article, holder); - break; - } - } - } else { - super.onBindViewHolder(holder, position, payloads); - } - } - - private void updateUnreadView(final Article article, final ArticleViewHolder holder) { - if (m_compactLayoutMode) { - holder.view.setBackgroundColor(article.unread ? m_colorSurfaceContainerLowest : 0); - } else { - MaterialCardView card = (MaterialCardView) holder.view; - - card.setCardBackgroundColor(article.unread ? m_colorSurfaceContainerLowest : m_colorSurface); - } - - if (holder.titleView != null) { - holder.titleView.setTypeface(null, article.unread ? Typeface.BOLD : Typeface.NORMAL); - holder.titleView.setTextColor(article.unread ? m_colorOnSurface : m_colorPrimary); - } - - updateActiveView(article, holder); - } - - private void updateActiveView(final Article article, final ArticleViewHolder holder) { - if (m_activity instanceof DetailActivity) { - if (article.active) { - holder.view.setBackgroundColor(m_colorTertiaryContainer); - - if (holder.titleView != null) { - holder.titleView.setTextColor(m_colorOnTertiaryContainer); - } - } - - if (holder.excerptView != null) { - holder.excerptView.setTextColor(article.active ? m_colorOnTertiaryContainer : m_colorOnSurface); - } - - if (holder.feedTitleView != null) { - holder.feedTitleView.setTextColor(article.active ? m_colorOnTertiaryContainer : m_colorSecondary); - } - } - } - - @Override - public void onBindViewHolder(@NonNull final ArticleViewHolder holder, int position) { - Article article = getItem(position); - - if (article.id == Article.TYPE_AMR_FOOTER && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - WindowManager wm = (WindowManager) m_activity.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - int screenHeight = (int)(display.getHeight() * 1.5); - - holder.view.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, screenHeight)); - } - - // nothing else of interest for those below anyway - if (article.id < 0) return; - - updateUnreadView(article, holder); - updateTextImage(article, holder); - updateTitleView(article, holder); - updateMarkedView(article, holder); - updateScoreView(article, holder); - updatePublishedView(article, holder); - updateAttachmentsView(article, holder); - updateLinkHost(article, holder); - updateExcerptView(article, holder); - updateAuthorView(article, holder); - updateDateView(article, holder); - updateSelectedView(article, holder); - - if (!m_compactLayoutMode && holder.flavorImageHolder != null) { - - // reset our view to default in case of recycling - holder.flavorImageLoadingBar.setVisibility(View.GONE); - holder.flavorImageLoadingBar.setIndeterminate(false); - - holder.flavorImageView.setVisibility(View.GONE); - holder.flavorVideoKindView.setVisibility(View.GONE); - holder.flavorImageOverflow.setVisibility(View.GONE); - holder.flavorVideoView.setVisibility(View.GONE); - holder.flavorImageHolder.setVisibility(View.GONE); - - if (canShowFlavorImage() && article.flavorImageUri != null && holder.flavorImageView != null) { - int maxImageHeight = (int) (m_screenHeight * 0.5f); - - // we also downsample below using glide to save RAM - holder.flavorImageView.setMaxHeight(maxImageHeight); - - if (m_headlinesFragmentModel.getFlavorImageSizes().containsKey(article.flavorImageUri)) { - Size size = m_headlinesFragmentModel.getFlavorImageSizes().get(article.flavorImageUri); - - if (BuildConfig.DEBUG) - Log.d(TAG, "using cached resource size for " + article.flavorImageUri + " " + size.getWidth() + "x" + size.getHeight()); - - if (size.getWidth() > FLAVOR_IMG_MIN_SIZE && size.getHeight() > FLAVOR_IMG_MIN_SIZE) { - loadFlavorImage(article, holder, maxImageHeight); - } - - } else { - if (BuildConfig.DEBUG) - Log.d(TAG, "checking resource size for " + article.flavorImageUri); - checkImageAndLoad(article, holder, maxImageHeight); - } - } + holder.view.setOnLongClickListener(view -> { + m_list.showContextMenuForChild(view); + return true; + }); + + // block footer clicks to make button/selection clicking easier + if (holder.headlineFooter != null) { + holder.headlineFooter.setOnClickListener(view -> { + // + }); + } + + if (holder.attachmentsView != null) { + holder.attachmentsView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = m_adapter.getItem(position); + m_activity.displayAttachments(article); + } + }); + } + + if (holder.flavorImageView != null) { + holder.flavorImageView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = m_adapter.getItem(position); + openGalleryForType(article, holder, holder.flavorImageView); + } + }); + } + + if (holder.flavorImageOverflow != null) { + holder.flavorImageOverflow.setOnClickListener(view -> { + PopupMenu popup = new PopupMenu(getContext(), holder.flavorImageOverflow); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.content_gallery_entry, popup.getMenu()); + + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = m_adapter.getItem(position); + + popup.setOnMenuItemClickListener(item -> { + + Uri mediaUri = Uri.parse(article.flavorStreamUri != null ? article.flavorStreamUri : + article.flavorImageUri); + + int itemId = item.getItemId(); + if (itemId == R.id.article_img_open) { + m_activity.openUri(mediaUri); + return true; + } else if (itemId == R.id.article_img_copy) { + m_activity.copyToClipboard(mediaUri.toString()); + return true; + } else if (itemId == R.id.article_img_share) { + m_activity.shareImageFromUri(mediaUri.toString()); + return true; + } else if (itemId == R.id.article_img_share_url) { + m_activity.shareText(mediaUri.toString()); + return true; + } else if (itemId == R.id.article_img_view_caption) { + m_activity.displayImageCaption(article.flavorImageUri, article.content); + return true; + } + return false; + }); + + popup.show(); + } + }); + + holder.flavorImageView.setOnLongClickListener(view -> { + m_list.showContextMenuForChild(holder.view); + return true; + }); + } + + if (holder.menuButtonView != null) { + holder.menuButtonView.setOnClickListener(view -> { + + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + PopupMenu popup = new PopupMenu(getContext(), view); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.context_headlines, popup.getMenu()); + + popup.getMenu().findItem(R.id.article_set_labels).setEnabled(m_activity.getApiLevel() >= 1); + popup.getMenu().findItem(R.id.article_edit_note).setEnabled(m_activity.getApiLevel() >= 1); + + popup.setOnMenuItemClickListener(item -> onArticleMenuItemSelected(item, + getItem(position), + m_list.getChildAdapterPosition(holder.view))); + + popup.show(); + } + }); + } + + if (holder.markedView != null) { + holder.markedView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = new Article(getItem(position)); + article.marked = !article.marked; + + m_activity.saveArticleMarked(article); + } + }); + } + + if (holder.selectionBoxView != null) { + holder.selectionBoxView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = new Article(getItem(position)); + + CheckBox cb = (CheckBox) view; + + article.selected = cb.isChecked(); + + Application.getArticlesModel().update(article); + } + }); + } + + if (holder.publishedView != null) { + holder.publishedView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = new Article(getItem(position)); + article.published = !article.published; + + m_activity.saveArticlePublished(article); + } + }); + } + + if (holder.textImage != null) { + holder.textImage.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = new Article(getItem(position)); + article.selected = !article.selected; + + Application.getArticlesModel().update(article); + } + }); + + holder.textImage.setOnLongClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + Article article = getItem(position); + + openGalleryForType(article, holder, holder.textImage); + } + + return true; + }); + } + + if (holder.scoreView != null) { + if (m_activity.getApiLevel() >= 16) { + holder.scoreView.setOnClickListener(view -> { + int position = m_list.getChildAdapterPosition(holder.view); + + if (position != -1) { + + final Article articleClone = new Article(getItem(position)); + final EditText edit = new EditText(getActivity()); + + edit.setText(String.valueOf(articleClone.score)); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) + .setTitle(R.string.score_for_this_article) + .setPositiveButton(R.string.set_score, + (dialog, which) -> { + try { + articleClone.score = Integer.parseInt(edit.getText().toString()); + m_activity.saveArticleScore(articleClone); + + } catch (NumberFormatException e) { + m_activity.toast(R.string.score_invalid); + e.printStackTrace(); + } + }) + .setNegativeButton(getString(R.string.cancel), + (dialog, which) -> { + }).setView(edit); + + Dialog dialog = builder.create(); + dialog.show(); + } + }); + } else { + holder.scoreView.setVisibility(View.GONE); + } + } + + return holder; + } + + @Override + public void onViewRecycled(@NonNull ArticleViewHolder holder) { + super.onViewRecycled(holder); + + if (holder.flavorImageView != null) + Glide.with(HeadlinesFragment.this).clear(holder.flavorImageView); + } + + @Override + // https://stackoverflow.com/questions/33176336/need-an-example-about-recyclerview-adapter-notifyitemchangedint-position-objec/50085835#50085835 + public void onBindViewHolder(@NonNull final ArticleViewHolder holder, final int position, final List<Object> payloads) { + if (!payloads.isEmpty()) { + Log.d(TAG, "onBindViewHolder, payloads=" + payloads + " position=" + position); + + final Article article = getItem(position); + + for (final Object pobject : payloads) { + ArticleDiffItemCallback.ChangePayload payload = (ArticleDiffItemCallback.ChangePayload) pobject; + + switch (payload) { + case UNREAD: + case ACTIVE: + updateUnreadView(article, holder); + break; + case MARKED: + updateMarkedView(article, holder); + break; + case SELECTED: + updateSelectedView(article, holder); + updateTextImage(article, holder); + break; + case PUBLISHED: + updatePublishedView(article, holder); + break; + case SCORE: + updateScoreView(article, holder); + break; + } + } + } else { + super.onBindViewHolder(holder, position, payloads); + } + } + + private void updateUnreadView(final Article article, final ArticleViewHolder holder) { + if (m_compactLayoutMode) { + holder.view.setBackgroundColor(article.unread ? m_colorSurfaceContainerLowest : 0); + } else { + MaterialCardView card = (MaterialCardView) holder.view; + + card.setCardBackgroundColor(article.unread ? m_colorSurfaceContainerLowest : m_colorSurface); + } + + if (holder.titleView != null) { + holder.titleView.setTypeface(null, article.unread ? Typeface.BOLD : Typeface.NORMAL); + holder.titleView.setTextColor(article.unread ? m_colorOnSurface : m_colorPrimary); + } + + updateActiveView(article, holder); + } + + private void updateActiveView(final Article article, final ArticleViewHolder holder) { + if (m_activity instanceof DetailActivity) { + if (article.active) { + holder.view.setBackgroundColor(m_colorTertiaryContainer); + + if (holder.titleView != null) { + holder.titleView.setTextColor(m_colorOnTertiaryContainer); + } + } + + if (holder.excerptView != null) { + holder.excerptView.setTextColor(article.active ? m_colorOnTertiaryContainer : m_colorOnSurface); + } + + if (holder.feedTitleView != null) { + holder.feedTitleView.setTextColor(article.active ? m_colorOnTertiaryContainer : m_colorSecondary); + } + } + } + + @Override + public void onBindViewHolder(@NonNull final ArticleViewHolder holder, int position) { + Article article = getItem(position); + + if (article.id == Article.TYPE_AMR_FOOTER && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { + WindowManager wm = (WindowManager) m_activity.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + int screenHeight = (int) (display.getHeight() * 1.5); + + holder.view.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, screenHeight)); + } + + // nothing else of interest for those below anyway + if (article.id < 0) return; + + updateUnreadView(article, holder); + updateTextImage(article, holder); + updateTitleView(article, holder); + updateMarkedView(article, holder); + updateScoreView(article, holder); + updatePublishedView(article, holder); + updateAttachmentsView(article, holder); + updateLinkHost(article, holder); + updateExcerptView(article, holder); + updateAuthorView(article, holder); + updateDateView(article, holder); + updateSelectedView(article, holder); + + if (!m_compactLayoutMode && holder.flavorImageHolder != null) { + + // reset our view to default in case of recycling + holder.flavorImageLoadingBar.setVisibility(View.GONE); + holder.flavorImageLoadingBar.setIndeterminate(false); + + holder.flavorImageView.setVisibility(View.GONE); + holder.flavorVideoKindView.setVisibility(View.GONE); + holder.flavorImageOverflow.setVisibility(View.GONE); + holder.flavorVideoView.setVisibility(View.GONE); + holder.flavorImageHolder.setVisibility(View.GONE); + + if (canShowFlavorImage() && article.flavorImageUri != null && holder.flavorImageView != null) { + int maxImageHeight = (int) (m_screenHeight * 0.5f); + + // we also downsample below using glide to save RAM + holder.flavorImageView.setMaxHeight(maxImageHeight); + + if (m_headlinesFragmentModel.getFlavorImageSizes().containsKey(article.flavorImageUri)) { + Size size = m_headlinesFragmentModel.getFlavorImageSizes().get(article.flavorImageUri); + + if (BuildConfig.DEBUG) + Log.d(TAG, "using cached resource size for " + article.flavorImageUri + " " + size.getWidth() + "x" + size.getHeight()); + + if (size.getWidth() > FLAVOR_IMG_MIN_SIZE && size.getHeight() > FLAVOR_IMG_MIN_SIZE) { + loadFlavorImage(article, holder, maxImageHeight); + } + + } else { + if (BuildConfig.DEBUG) + Log.d(TAG, "checking resource size for " + article.flavorImageUri); + checkImageAndLoad(article, holder, maxImageHeight); + } + } /* if (m_prefs.getBoolean("inline_video_player", false) && article.flavorImage != null && "video".equalsIgnoreCase(article.flavorImage.tagName()) && article.flavorStreamUri != null) { @@ -1192,313 +1202,313 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { } else { holder.flavorImageView.setOnClickListener(view -> openGalleryForType(article, holder, holder.flavorImageView)); } */ - } - } - - private void updateTitleView(final Article article, final ArticleViewHolder holder) { - if (holder.titleView != null) { - holder.titleView.setText(Html.fromHtml(article.title)); - holder.titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, Math.min(21, m_headlineFontSize + 3)); - } - - if (holder.feedTitleView != null) { - if (article.feed_title != null && m_feed != null && (m_feed.is_cat || m_feed.id < 0)) { - holder.feedTitleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); - holder.feedTitleView.setText(article.feed_title); - } else { - holder.feedTitleView.setVisibility(View.GONE); - } - } - } + } + } - private void updateDateView(final Article article, final ArticleViewHolder holder) { - if (holder.dateView != null) { - holder.dateView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); + private void updateTitleView(final Article article, final ArticleViewHolder holder) { + if (holder.titleView != null) { + holder.titleView.setText(Html.fromHtml(article.title)); + holder.titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, Math.min(21, m_headlineFontSize + 3)); + } - Date d = new Date((long)article.updated * 1000); - Date now = new Date(); - long half_a_year_ago = now.getTime()/1000L - 182*24*60*60; + if (holder.feedTitleView != null) { + if (article.feed_title != null && m_feed != null && (m_feed.is_cat || m_feed.id < 0)) { + holder.feedTitleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); + holder.feedTitleView.setText(article.feed_title); + } else { + holder.feedTitleView.setVisibility(View.GONE); + } + } + } - DateFormat df; + private void updateDateView(final Article article, final ArticleViewHolder holder) { + if (holder.dateView != null) { + holder.dateView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); - if (now.getYear() == d.getYear() && now.getMonth() == d.getMonth() && now.getDay() == d.getDay()) { - df = new SimpleDateFormat("HH:mm"); - } else if (article.updated > half_a_year_ago) { - df = new SimpleDateFormat("MMM dd"); - } else { - df = new SimpleDateFormat("MMM yyyy"); - } + Date d = new Date((long) article.updated * 1000); + Date now = new Date(); + long half_a_year_ago = now.getTime() / 1000L - 182 * 24 * 60 * 60; - df.setTimeZone(TimeZone.getDefault()); - holder.dateView.setText(df.format(d)); - } - } + DateFormat df; - private void updateAuthorView(final Article article, final ArticleViewHolder holder) { - String articleAuthor = article.author != null ? article.author : ""; + if (now.getYear() == d.getYear() && now.getMonth() == d.getMonth() && now.getDay() == d.getDay()) { + df = new SimpleDateFormat("HH:mm"); + } else if (article.updated > half_a_year_ago) { + df = new SimpleDateFormat("MMM dd"); + } else { + df = new SimpleDateFormat("MMM yyyy"); + } - if (holder.authorView != null) { - holder.authorView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); + df.setTimeZone(TimeZone.getDefault()); + holder.dateView.setText(df.format(d)); + } + } - if (!articleAuthor.isEmpty()) { - holder.authorView.setText(getString(R.string.author_formatted, articleAuthor)); - } else { - holder.authorView.setText(""); - } - } - } - - private void updateExcerptView(final Article article, final ArticleViewHolder holder) { - if (holder.excerptView != null) { - if (!m_prefs.getBoolean("headlines_show_content", true)) { - holder.excerptView.setVisibility(View.GONE); - } else { - String excerpt = ""; - - try { - if (article.excerpt != null) { - excerpt = article.excerpt; - } else if (article.articleDoc != null) { - excerpt = article.articleDoc.text(); - - if (excerpt.length() > CommonActivity.EXCERPT_MAX_LENGTH) - excerpt = excerpt.substring(0, CommonActivity.EXCERPT_MAX_LENGTH) + "…"; - } - } catch (Exception e) { - e.printStackTrace(); - excerpt = ""; - } - - holder.excerptView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineFontSize); - holder.excerptView.setText(excerpt); - - if (!excerpt.isEmpty()) { - holder.excerptView.setVisibility(View.VISIBLE); - } else { - holder.excerptView.setVisibility(View.GONE); - } - } - } - } - - private void updateLinkHost(final Article article, final ArticleViewHolder holder) { - if (holder.linkHost != null) { - if (article.isHostDistinct()) { - holder.linkHost.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); - holder.linkHost.setText(article.getLinkHost()); - holder.linkHost.setVisibility(View.VISIBLE); - } else { - holder.linkHost.setVisibility(View.GONE); - } - } - } - - private void updateAttachmentsView(final Article article, final ArticleViewHolder holder) { - if (holder.attachmentsView != null) { - if (article.attachments != null && !article.attachments.isEmpty()) { - holder.attachmentsView.setVisibility(View.VISIBLE); - } else { - holder.attachmentsView.setVisibility(View.GONE); - } - } - } - - private void updateMarkedView(final Article article, final ArticleViewHolder holder) { - if (holder.markedView != null) { - holder.markedView.setIconResource(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); - - if (m_enableIconTinting) - holder.markedView.setIconTint(article.marked ? m_cslTertiary : m_cslPrimary); - } - } - - private void updateTextImage(final Article article, final ArticleViewHolder holder) { - if (holder.textImage != null) { - updateTextCheckedState(article, holder); - - ViewCompat.setTransitionName(holder.textImage, - "gallery:" + article.flavorImageUri); - } - } - - private void updateSelectedView(final Article article, final ArticleViewHolder holder) { - if (holder.selectionBoxView != null) { - holder.selectionBoxView.setChecked(article.selected); - } - } - - private void updateScoreView(final Article article, final ArticleViewHolder holder) { - if (holder.scoreView != null) { - int scoreDrawable = R.drawable.baseline_trending_flat_24; - - if (article.score > 0) - scoreDrawable = R.drawable.baseline_trending_up_24; - else if (article.score < 0) - scoreDrawable = R.drawable.baseline_trending_down_24; - - holder.scoreView.setIconResource(scoreDrawable); - - if (m_enableIconTinting) { - if (article.score > Article.SCORE_HIGH) - holder.scoreView.setIconTint(m_cslTertiary); - else - holder.scoreView.setIconTint(m_cslPrimary); - } - } - } - - private void updatePublishedView(final Article article, final ArticleViewHolder holder) { - if (holder.publishedView != null) { - // otherwise we just use tinting in actionbar - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !m_prefs.getBoolean("enable_icon_tinting", true)) { - holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); - } - - if (m_enableIconTinting) - holder.publishedView.setIconTint(article.published ? m_cslTertiary : m_cslPrimary); - } - } - - private void loadFlavorImage(final Article article, final ArticleViewHolder holder, final int maxImageHeight) { - Glide.with(HeadlinesFragment.this) - .load(article.flavorImageUri) - .transition(DrawableTransitionOptions.withCrossFade()) - .override(m_screenWidth, maxImageHeight) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .skipMemoryCache(false) - .listener(new RequestListener<Drawable>() { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { - holder.flavorImageHolder.setVisibility(View.GONE); - - holder.flavorImageView.setVisibility(View.GONE); - holder.flavorImageOverflow.setVisibility(View.VISIBLE); - - return false; - } - - @Override - public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { - holder.flavorImageHolder.setVisibility(View.VISIBLE); - - holder.flavorImageView.setVisibility(View.VISIBLE); - holder.flavorImageOverflow.setVisibility(View.VISIBLE); - - adjustVideoKindView(holder, article); - - return false; - } - }) - .into(new DrawableImageViewTarget(holder.flavorImageView)); - } - - private void checkImageAndLoad(final Article article, final ArticleViewHolder holder, final int maxImageHeight) { - FlavorProgressTarget<Size> flavorProgressTarget = new FlavorProgressTarget<>(new SimpleTarget<Size>() { - @Override - public void onResourceReady(@NonNull Size resource, @Nullable com.bumptech.glide.request.transition.Transition<? super Size> transition) { - - if (BuildConfig.DEBUG) - Log.d(TAG, "got resource of " + resource.getWidth() + "x" + resource.getHeight()); - - m_headlinesFragmentModel.getFlavorImageSizes().put(article.flavorImageUri, resource); - - if (resource.getWidth() > FLAVOR_IMG_MIN_SIZE && resource.getHeight() > FLAVOR_IMG_MIN_SIZE) { - - // now we can actually load the image into our drawable - loadFlavorImage(article, holder, maxImageHeight); - - } else { - holder.flavorImageHolder.setVisibility(View.GONE); - - holder.flavorImageView.setVisibility(View.VISIBLE); - holder.flavorImageOverflow.setVisibility(View.VISIBLE); - } - } - }, article.flavorImageUri, holder); - - Glide.with(HeadlinesFragment.this) - .as(Size.class) - .load(article.flavorImageUri) - .diskCacheStrategy(DiskCacheStrategy.DATA) - .skipMemoryCache(true) - .into(flavorProgressTarget); - } - - @Override - public int getItemViewType(int position) { - Article a = getItem(position); - - if (a.id == Article.TYPE_AMR_FOOTER) { - return VIEW_AMR_FOOTER; - } else { - return VIEW_NORMAL; - } - } - - private void updateTextCheckedState(final Article article, final ArticleViewHolder holder) { + private void updateAuthorView(final Article article, final ArticleViewHolder holder) { + String articleAuthor = article.author != null ? article.author : ""; + + if (holder.authorView != null) { + holder.authorView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); + + if (!articleAuthor.isEmpty()) { + holder.authorView.setText(getString(R.string.author_formatted, articleAuthor)); + } else { + holder.authorView.setText(""); + } + } + } + + private void updateExcerptView(final Article article, final ArticleViewHolder holder) { + if (holder.excerptView != null) { + if (!m_prefs.getBoolean("headlines_show_content", true)) { + holder.excerptView.setVisibility(View.GONE); + } else { + String excerpt = ""; + + try { + if (article.excerpt != null) { + excerpt = article.excerpt; + } else if (article.articleDoc != null) { + excerpt = article.articleDoc.text(); + + if (excerpt.length() > CommonActivity.EXCERPT_MAX_LENGTH) + excerpt = excerpt.substring(0, CommonActivity.EXCERPT_MAX_LENGTH) + "…"; + } + } catch (Exception e) { + e.printStackTrace(); + excerpt = ""; + } + + holder.excerptView.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineFontSize); + holder.excerptView.setText(excerpt); + + if (!excerpt.isEmpty()) { + holder.excerptView.setVisibility(View.VISIBLE); + } else { + holder.excerptView.setVisibility(View.GONE); + } + } + } + } + + private void updateLinkHost(final Article article, final ArticleViewHolder holder) { + if (holder.linkHost != null) { + if (article.isHostDistinct()) { + holder.linkHost.setTextSize(TypedValue.COMPLEX_UNIT_SP, m_headlineSmallFontSize); + holder.linkHost.setText(article.getLinkHost()); + holder.linkHost.setVisibility(View.VISIBLE); + } else { + holder.linkHost.setVisibility(View.GONE); + } + } + } + + private void updateAttachmentsView(final Article article, final ArticleViewHolder holder) { + if (holder.attachmentsView != null) { + if (article.attachments != null && !article.attachments.isEmpty()) { + holder.attachmentsView.setVisibility(View.VISIBLE); + } else { + holder.attachmentsView.setVisibility(View.GONE); + } + } + } + + private void updateMarkedView(final Article article, final ArticleViewHolder holder) { + if (holder.markedView != null) { + holder.markedView.setIconResource(article.marked ? R.drawable.baseline_star_24 : R.drawable.baseline_star_outline_24); + + if (m_enableIconTinting) + holder.markedView.setIconTint(article.marked ? m_cslTertiary : m_cslPrimary); + } + } + + private void updateTextImage(final Article article, final ArticleViewHolder holder) { + if (holder.textImage != null) { + updateTextCheckedState(article, holder); + + ViewCompat.setTransitionName(holder.textImage, + "gallery:" + article.flavorImageUri); + } + } + + private void updateSelectedView(final Article article, final ArticleViewHolder holder) { + if (holder.selectionBoxView != null) { + holder.selectionBoxView.setChecked(article.selected); + } + } + + private void updateScoreView(final Article article, final ArticleViewHolder holder) { + if (holder.scoreView != null) { + int scoreDrawable = R.drawable.baseline_trending_flat_24; + + if (article.score > 0) + scoreDrawable = R.drawable.baseline_trending_up_24; + else if (article.score < 0) + scoreDrawable = R.drawable.baseline_trending_down_24; + + holder.scoreView.setIconResource(scoreDrawable); + + if (m_enableIconTinting) { + if (article.score > Article.SCORE_HIGH) + holder.scoreView.setIconTint(m_cslTertiary); + else + holder.scoreView.setIconTint(m_cslPrimary); + } + } + } + + private void updatePublishedView(final Article article, final ArticleViewHolder holder) { + if (holder.publishedView != null) { + // otherwise we just use tinting in actionbar + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !m_prefs.getBoolean("enable_icon_tinting", true)) { + holder.publishedView.setIconResource(article.published ? R.drawable.rss_box : R.drawable.rss); + } + + if (m_enableIconTinting) + holder.publishedView.setIconTint(article.published ? m_cslTertiary : m_cslPrimary); + } + } + + private void loadFlavorImage(final Article article, final ArticleViewHolder holder, final int maxImageHeight) { + Glide.with(HeadlinesFragment.this) + .load(article.flavorImageUri) + .transition(DrawableTransitionOptions.withCrossFade()) + .override(m_screenWidth, maxImageHeight) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .skipMemoryCache(false) + .listener(new RequestListener<Drawable>() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { + holder.flavorImageHolder.setVisibility(View.GONE); + + holder.flavorImageView.setVisibility(View.GONE); + holder.flavorImageOverflow.setVisibility(View.VISIBLE); + + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { + holder.flavorImageHolder.setVisibility(View.VISIBLE); + + holder.flavorImageView.setVisibility(View.VISIBLE); + holder.flavorImageOverflow.setVisibility(View.VISIBLE); + + adjustVideoKindView(holder, article); + + return false; + } + }) + .into(new DrawableImageViewTarget(holder.flavorImageView)); + } + + private void checkImageAndLoad(final Article article, final ArticleViewHolder holder, final int maxImageHeight) { + FlavorProgressTarget<Size> flavorProgressTarget = new FlavorProgressTarget<>(new SimpleTarget<Size>() { + @Override + public void onResourceReady(@NonNull Size resource, @Nullable com.bumptech.glide.request.transition.Transition<? super Size> transition) { + + if (BuildConfig.DEBUG) + Log.d(TAG, "got resource of " + resource.getWidth() + "x" + resource.getHeight()); + + m_headlinesFragmentModel.getFlavorImageSizes().put(article.flavorImageUri, resource); + + if (resource.getWidth() > FLAVOR_IMG_MIN_SIZE && resource.getHeight() > FLAVOR_IMG_MIN_SIZE) { + + // now we can actually load the image into our drawable + loadFlavorImage(article, holder, maxImageHeight); + + } else { + holder.flavorImageHolder.setVisibility(View.GONE); + + holder.flavorImageView.setVisibility(View.VISIBLE); + holder.flavorImageOverflow.setVisibility(View.VISIBLE); + } + } + }, article.flavorImageUri, holder); + + Glide.with(HeadlinesFragment.this) + .as(Size.class) + .load(article.flavorImageUri) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .skipMemoryCache(true) + .into(flavorProgressTarget); + } + + @Override + public int getItemViewType(int position) { + Article a = getItem(position); + + if (a.id == Article.TYPE_AMR_FOOTER) { + return VIEW_AMR_FOOTER; + } else { + return VIEW_NORMAL; + } + } + + private void updateTextCheckedState(final Article article, final ArticleViewHolder holder) { String tmp = !article.title.isEmpty() ? article.title.substring(0, 1).toUpperCase() : "?"; if (article.selected) { - holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161)); + holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161)); holder.textChecked.setVisibility(View.VISIBLE); } else { - final Drawable textDrawable = m_drawableBuilder.build(tmp, m_colorGenerator.getColor(article.title)); - - holder.textImage.setImageDrawable(textDrawable); - - if (!canShowFlavorImage() || article.flavorImage == null) { - holder.textImage.setImageDrawable(textDrawable); - } else { - Glide.with(HeadlinesFragment.this) - .load(article.flavorImageUri) - .transition(DrawableTransitionOptions.withCrossFade()) - .placeholder(textDrawable) - .thumbnail(0.5f) - .apply(RequestOptions.circleCropTransform()) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .skipMemoryCache(false) - .into(holder.textImage); - } + final Drawable textDrawable = m_drawableBuilder.build(tmp, m_colorGenerator.getColor(article.title)); + + holder.textImage.setImageDrawable(textDrawable); + + if (!canShowFlavorImage() || article.flavorImage == null) { + holder.textImage.setImageDrawable(textDrawable); + } else { + Glide.with(HeadlinesFragment.this) + .load(article.flavorImageUri) + .transition(DrawableTransitionOptions.withCrossFade()) + .placeholder(textDrawable) + .thumbnail(0.5f) + .apply(RequestOptions.circleCropTransform()) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .skipMemoryCache(false) + .into(holder.textImage); + } holder.textChecked.setVisibility(View.GONE); } } - private void openGalleryForType(final Article article, final ArticleViewHolder holder, final View transitionView) { - //Log.d(TAG, "openGalleryForType: " + article + " " + holder + " " + transitionView); + private void openGalleryForType(final Article article, final ArticleViewHolder holder, final View transitionView) { + //Log.d(TAG, "openGalleryForType: " + article + " " + holder + " " + transitionView); - if (article.flavorImage != null) { - if ("iframe".equalsIgnoreCase(article.flavorImage.tagName())) { - m_activity.openUri(Uri.parse(article.flavorStreamUri)); - } else { + if (article.flavorImage != null) { + if ("iframe".equalsIgnoreCase(article.flavorImage.tagName())) { + m_activity.openUri(Uri.parse(article.flavorStreamUri)); + } else { - Intent intent = new Intent(m_activity, GalleryActivity.class); + Intent intent = new Intent(m_activity, GalleryActivity.class); - intent.putExtra("firstSrc", article.flavorStreamUri != null ? article.flavorStreamUri : article.flavorImageUri); - intent.putExtra("title", article.title); + intent.putExtra("firstSrc", article.flavorStreamUri != null ? article.flavorStreamUri : article.flavorImageUri); + intent.putExtra("title", article.title); - // FIXME maybe: gallery view works with document as html, it's easier to add this hack rather than - // rework it to additionally operate on separate attachment array (?) - // also, maybe consider video attachments? kinda hard to do without a poster tho (for flavor view) + // FIXME maybe: gallery view works with document as html, it's easier to add this hack rather than + // rework it to additionally operate on separate attachment array (?) + // also, maybe consider video attachments? kinda hard to do without a poster tho (for flavor view) - String tempContent = article.content; + String tempContent = article.content; - if (article.attachments != null) { - Document doc = new Document(""); + if (article.attachments != null) { + Document doc = new Document(""); - for (Attachment a : article.attachments) { - if (a.content_type != null) { - if (a.content_type.contains("image/")) { - Element img = new Element("img").attr("src", a.content_url); - doc.appendChild(img); - } - } - } + for (Attachment a : article.attachments) { + if (a.content_type != null) { + if (a.content_type.contains("image/")) { + Element img = new Element("img").attr("src", a.content_url); + doc.appendChild(img); + } + } + } - tempContent = doc.outerHtml() + tempContent; - } + tempContent = doc.outerHtml() + tempContent; + } - intent.putExtra("content", tempContent); + intent.putExtra("content", tempContent); /* ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(m_activity, @@ -1507,72 +1517,72 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { ActivityCompat.startActivity(m_activity, intent, options.toBundle()); */ - startActivity(intent); - } - } - } - - private void adjustVideoKindView(final ArticleViewHolder holder, final Article article) { - if (article.flavorImage != null) { - if (article.flavor_kind == Article.FLAVOR_KIND_YOUTUBE || "iframe".equalsIgnoreCase(article.flavorImage.tagName())) { - holder.flavorVideoKindView.setImageResource(R.drawable.baseline_play_circle_outline_24); - holder.flavorVideoKindView.setVisibility(View.VISIBLE); - } else if (article.flavor_kind == Article.FLAVOR_KIND_VIDEO || "video".equalsIgnoreCase(article.flavorImage.tagName())) { - holder.flavorVideoKindView.setImageResource(R.drawable.baseline_play_circle_24); - holder.flavorVideoKindView.setVisibility(View.VISIBLE); - } else { - holder.flavorVideoKindView.setVisibility(View.INVISIBLE); - } - } else { - holder.flavorVideoKindView.setVisibility(View.INVISIBLE); - } - } - } - - private void releaseSurface() { - try { - if (m_mediaPlayer != null) { - m_mediaPlayer.release(); - } - } catch (IllegalStateException e) { - e.printStackTrace(); - } - - try { - if (m_activeTexture != null) { - m_activeTexture.setVisibility(View.GONE); - } - } catch (IllegalStateException e) { - e.printStackTrace(); - } - } - - public void scrollToArticle(Article article) { - int position = Application.getArticles().indexOf(article); - - if (position != -1) - m_list.scrollToPosition(position); - } - - - public Feed getFeed() { - return m_feed; - } - - @Override - public void onPause() { - super.onPause(); - - releaseSurface(); - } - - private void syncToSharedArticles() { - List<Article> tmp = new ArrayList<>(Application.getArticles()); - - if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) - tmp.add(new Article(Article.TYPE_AMR_FOOTER)); - - m_adapter.submitList(tmp); - } + startActivity(intent); + } + } + } + + private void adjustVideoKindView(final ArticleViewHolder holder, final Article article) { + if (article.flavorImage != null) { + if (article.flavor_kind == Article.FLAVOR_KIND_YOUTUBE || "iframe".equalsIgnoreCase(article.flavorImage.tagName())) { + holder.flavorVideoKindView.setImageResource(R.drawable.baseline_play_circle_outline_24); + holder.flavorVideoKindView.setVisibility(View.VISIBLE); + } else if (article.flavor_kind == Article.FLAVOR_KIND_VIDEO || "video".equalsIgnoreCase(article.flavorImage.tagName())) { + holder.flavorVideoKindView.setImageResource(R.drawable.baseline_play_circle_24); + holder.flavorVideoKindView.setVisibility(View.VISIBLE); + } else { + holder.flavorVideoKindView.setVisibility(View.INVISIBLE); + } + } else { + holder.flavorVideoKindView.setVisibility(View.INVISIBLE); + } + } + } + + private void releaseSurface() { + try { + if (m_mediaPlayer != null) { + m_mediaPlayer.release(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } + + try { + if (m_activeTexture != null) { + m_activeTexture.setVisibility(View.GONE); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + public void scrollToArticle(Article article) { + int position = Application.getArticles().indexOf(article); + + if (position != -1) + m_list.scrollToPosition(position); + } + + + public Feed getFeed() { + return m_feed; + } + + @Override + public void onPause() { + super.onPause(); + + releaseSurface(); + } + + private void syncToSharedArticles() { + List<Article> tmp = new ArrayList<>(Application.getArticles()); + + if (m_prefs.getBoolean("headlines_mark_read_scroll", false)) + tmp.add(new Article(Article.TYPE_AMR_FOOTER)); + + m_adapter.submitList(tmp); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java index baa17267..8bb3d706 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/LogcatActivity.java @@ -66,7 +66,7 @@ public class LogcatActivity extends CommonActivity { m_items.clear(); try { - Process process = Runtime.getRuntime().exec("logcat -d -t " + MAX_LOG_ENTRIES); + Process process = Runtime.getRuntime().exec("logcat -d -t " + MAX_LOG_ENTRIES); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(process.getInputStream())); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java index 28e90d53..ff54895b 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/MasterActivity.java @@ -34,64 +34,64 @@ import java.util.HashMap; import java.util.LinkedHashMap; public class MasterActivity extends OnlineActivity implements HeadlinesEventListener { - private final String TAG = this.getClass().getSimpleName(); - - private static final int HEADLINES_REQUEST = 1; - - protected SharedPreferences m_prefs; - protected long m_lastRefresh = 0; - protected long m_lastWidgetRefresh = 0; - - protected Feed m_activeFeed; + private final String TAG = this.getClass().getSimpleName(); + + private static final int HEADLINES_REQUEST = 1; + + protected SharedPreferences m_prefs; + protected long m_lastRefresh = 0; + protected long m_lastWidgetRefresh = 0; + + protected Feed m_activeFeed; private ActionBarDrawerToggle m_drawerToggle; private DrawerLayout m_drawerLayout; - @SuppressLint("NewApi") - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); + @SuppressLint("NewApi") + @Override + public void onCreate(Bundle savedInstanceState) { + m_prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); - setAppTheme(m_prefs); + setAppTheme(m_prefs); - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); - if (m_prefs.getBoolean("force_phone_layout", false)) { - setContentView(R.layout.activity_master_phone); - } else { - setContentView(R.layout.activity_master); - } + if (m_prefs.getBoolean("force_phone_layout", false)) { + setContentView(R.layout.activity_master_phone); + } else { + setContentView(R.layout.activity_master); + } - setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); + setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); - applyEdgeToEdgeInsets(); + applyEdgeToEdgeInsets(); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); - Application.getInstance().load(savedInstanceState); + Application.getInstance().load(savedInstanceState); - enableActionModeObserver(); + enableActionModeObserver(); - m_lastWidgetRefresh = new Date().getTime(); + m_lastWidgetRefresh = new Date().getTime(); - m_loadingProgress = findViewById(R.id.loading_progress); + m_loadingProgress = findViewById(R.id.loading_progress); - m_drawerLayout = findViewById(R.id.headlines_drawer); + m_drawerLayout = findViewById(R.id.headlines_drawer); - if (m_drawerLayout != null) { + if (m_drawerLayout != null) { - m_drawerToggle = new ActionBarDrawerToggle(this, m_drawerLayout, R.string.blank, R.string.blank) { + m_drawerToggle = new ActionBarDrawerToggle(this, m_drawerLayout, R.string.blank, R.string.blank) { @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); - Date date = new Date(); - if (date.getTime() - m_lastRefresh > 60*1000) { - m_lastRefresh = date.getTime(); - refresh(false); - } + Date date = new Date(); + if (date.getTime() - m_lastRefresh > 60 * 1000) { + m_lastRefresh = date.getTime(); + refresh(false); + } } @Override @@ -120,193 +120,193 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList m_drawerLayout.openDrawer(GravityCompat.START); } - final Intent i = getIntent(); - boolean shortcutMode = i.getBooleanExtra("shortcut_mode", false); - - Log.d(TAG, "is_shortcut_mode: " + shortcutMode); - - if (shortcutMode) { - LoginRequest lr = new LoginRequest(this, false, new OnLoginFinishedListener() { - - @Override - public void OnLoginSuccess() { - int feedId = i.getIntExtra("feed_id", 0); - boolean isCat = i.getBooleanExtra("feed_is_cat", false); - String feedTitle = i.getStringExtra("feed_title"); - - // app shortcuts are not allowed to pass string extras - if (feedTitle == null) - feedTitle = getString(Feed.getSpecialFeedTitleId(feedId, isCat)); - - Feed tmpFeed = new Feed(feedId, feedTitle, isCat); - - onFeedSelected(tmpFeed); - } - - @Override - public void OnLoginFailed() { - login(); - } - }); - - HashMap<String, String> map = new HashMap<>(); - map.put("op", "login"); - map.put("user", m_prefs.getString("login", "").trim()); - map.put("password", m_prefs.getString("password", "").trim()); - - lr.execute(map); - } - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - FeedsFragment fc; - - if (m_prefs.getBoolean("enable_cats", true)) { - fc = new RootCategoriesFragment(); - // it doesn't matter which feed is used here - fc.initialize(new Feed(Feed.CAT_SPECIAL, getString(R.string.cat_special), true), false); - } else { - fc = new FeedsFragment(); - fc.initialize(new Feed(Feed.ALL_ARTICLES, getString(R.string.feed_all_articles), true), false); - } - - ft.replace(R.id.feeds_fragment, fc, FRAG_FEEDS); + final Intent i = getIntent(); + boolean shortcutMode = i.getBooleanExtra("shortcut_mode", false); + + Log.d(TAG, "is_shortcut_mode: " + shortcutMode); + + if (shortcutMode) { + LoginRequest lr = new LoginRequest(this, false, new OnLoginFinishedListener() { + + @Override + public void OnLoginSuccess() { + int feedId = i.getIntExtra("feed_id", 0); + boolean isCat = i.getBooleanExtra("feed_is_cat", false); + String feedTitle = i.getStringExtra("feed_title"); + + // app shortcuts are not allowed to pass string extras + if (feedTitle == null) + feedTitle = getString(Feed.getSpecialFeedTitleId(feedId, isCat)); + + Feed tmpFeed = new Feed(feedId, feedTitle, isCat); + + onFeedSelected(tmpFeed); + } + + @Override + public void OnLoginFailed() { + login(); + } + }); + + HashMap<String, String> map = new HashMap<>(); + map.put("op", "login"); + map.put("user", m_prefs.getString("login", "").trim()); + map.put("password", m_prefs.getString("password", "").trim()); + + lr.execute(map); + } + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + FeedsFragment fc; + + if (m_prefs.getBoolean("enable_cats", true)) { + fc = new RootCategoriesFragment(); + // it doesn't matter which feed is used here + fc.initialize(new Feed(Feed.CAT_SPECIAL, getString(R.string.cat_special), true), false); + } else { + fc = new FeedsFragment(); + fc.initialize(new Feed(Feed.ALL_ARTICLES, getString(R.string.feed_all_articles), true), false); + } + + ft.replace(R.id.feeds_fragment, fc, FRAG_FEEDS); /* FeedsFragment ff = new FeedsFragment(); ff.initialize(new Feed(12, "Technology", true), true); ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); */ - // allow overriding feed to open on startup in non-shortcut mode, default to - // open_on_startup prefs setting and not-category + // allow overriding feed to open on startup in non-shortcut mode, default to + // open_on_startup prefs setting and not-category - int openFeedId = i.getIntExtra("feed_id", - Integer.parseInt(m_prefs.getString("open_on_startup", "0"))); - boolean openFeedIsCat = i.getBooleanExtra("feed_is_cat", false); + int openFeedId = i.getIntExtra("feed_id", + Integer.parseInt(m_prefs.getString("open_on_startup", "0"))); + boolean openFeedIsCat = i.getBooleanExtra("feed_is_cat", false); - String openFeedTitle = i.getStringExtra("feed_title"); + String openFeedTitle = i.getStringExtra("feed_title"); - if (openFeedTitle == null) - openFeedTitle = getString(Feed.getSpecialFeedTitleId(openFeedId, openFeedIsCat)); + if (openFeedTitle == null) + openFeedTitle = getString(Feed.getSpecialFeedTitleId(openFeedId, openFeedIsCat)); - if (!shortcutMode && openFeedId != 0) { - Log.d(TAG, "opening feed id: " + openFeedId); + if (!shortcutMode && openFeedId != 0) { + Log.d(TAG, "opening feed id: " + openFeedId); - onFeedSelected(new Feed(openFeedId, openFeedTitle, openFeedIsCat)); + onFeedSelected(new Feed(openFeedId, openFeedTitle, openFeedIsCat)); } else if (m_drawerLayout != null) { m_drawerLayout.openDrawer(GravityCompat.START); } - ft.commit(); + ft.commit(); - } else { // savedInstanceState != null + } else { // savedInstanceState != null - m_activeFeed = savedInstanceState.getParcelable("m_activeFeed"); + m_activeFeed = savedInstanceState.getParcelable("m_activeFeed"); - if (m_drawerLayout != null && m_activeFeed == null) { - m_drawerLayout.openDrawer(GravityCompat.START); - } - } + if (m_drawerLayout != null && m_activeFeed == null) { + m_drawerLayout.openDrawer(GravityCompat.START); + } + } - FloatingActionButton fab = findViewById(R.id.master_fab); + FloatingActionButton fab = findViewById(R.id.master_fab); if (fab != null) { - fab.show(); + fab.show(); - fab.setOnClickListener(view -> { + fab.setOnClickListener(view -> { HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null && hf.isAdded()) { hf.refresh(false); } }); - } - } - - private void applyEdgeToEdgeInsets() { - // https://stackoverflow.com/questions/79018063/trying-to-understand-edge-to-edge-in-android - // https://developer.android.com/develop/ui/views/layout/edge-to-edge - - View coordinatorView = findViewById(R.id.headlines_coordinator); - - if (coordinatorView != null) { - ViewCompat.setOnApplyWindowInsetsListener(coordinatorView, (v, windowInsets) -> { - Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(0, insets.top, 0, insets.bottom); - return windowInsets; - }); - } - - View navigationView = findViewById(R.id.modal_navigation_view); - - if (navigationView != null) { - ViewCompat.setOnApplyWindowInsetsListener(navigationView, (v, windowInsets) -> { - Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(0, insets.top, 0, insets.bottom); - return windowInsets; - }); - } - } - - protected void onPostCreate(Bundle savedInstanceState) { + } + } + + private void applyEdgeToEdgeInsets() { + // https://stackoverflow.com/questions/79018063/trying-to-understand-edge-to-edge-in-android + // https://developer.android.com/develop/ui/views/layout/edge-to-edge + + View coordinatorView = findViewById(R.id.headlines_coordinator); + + if (coordinatorView != null) { + ViewCompat.setOnApplyWindowInsetsListener(coordinatorView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, insets.top, 0, insets.bottom); + return windowInsets; + }); + } + + View navigationView = findViewById(R.id.modal_navigation_view); + + if (navigationView != null) { + ViewCompat.setOnApplyWindowInsetsListener(navigationView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(0, insets.top, 0, insets.bottom); + return windowInsets; + }); + } + } + + protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. if (m_drawerToggle != null) m_drawerToggle.syncState(); } - @Override - protected void initMenu() { - super.initMenu(); - - if (m_menu != null && getSessionId() != null) { - Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + @Override + protected void initMenu() { + super.initMenu(); - m_menu.setGroupVisible(R.id.menu_group_feeds, ff != null && ff.isAdded()); - m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded()); - } - } + if (m_menu != null && getSessionId() != null) { + Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - public void onFeedSelected(Feed feed) { + m_menu.setGroupVisible(R.id.menu_group_feeds, ff != null && ff.isAdded()); + m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded()); + } + } - // show subfolder of feeds below current level - if (feed.is_cat && !feed.always_open_headlines) { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); + public void onFeedSelected(Feed feed) { - FeedsFragment ff = new FeedsFragment(); - ff.initialize(feed, true); - ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); + // show subfolder of feeds below current level + if (feed.is_cat && !feed.always_open_headlines) { + FragmentTransaction ft = getSupportFragmentManager() + .beginTransaction(); - ft.addToBackStack(null); - ft.commit(); + FeedsFragment ff = new FeedsFragment(); + ff.initialize(feed, true); + ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); - } else { - // actualy open the feed (i.e. show headlines) + ft.addToBackStack(null); + ft.commit(); + + } else { + // actualy open the feed (i.e. show headlines) - setActiveFeed(feed); + setActiveFeed(feed); - if (m_drawerLayout != null) { - m_drawerLayout.closeDrawers(); - } + if (m_drawerLayout != null) { + m_drawerLayout.closeDrawers(); + } - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - if (hf != null) { - hf.initialize(feed); - hf.refresh(false); - } else { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); + if (hf != null) { + hf.initialize(feed); + hf.refresh(false); + } else { + FragmentTransaction ft = getSupportFragmentManager() + .beginTransaction(); - hf = new HeadlinesFragment(); - hf.initialize(feed); + hf = new HeadlinesFragment(); + hf.initialize(feed); - ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); + ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - ft.commit(); - } - } - } + ft.commit(); + } + } + } @Override @@ -316,8 +316,8 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList finish(); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { + @Override + public boolean onOptionsItemSelected(MenuItem item) { if (m_drawerToggle != null && m_drawerToggle.onOptionsItemSelected(item)) { return true; } @@ -342,7 +342,7 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList ++i; } - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) .setTitle(getString(R.string.headlines_sort_articles_title)) .setSingleChoiceItems( sortTitles, @@ -378,135 +378,135 @@ public class MasterActivity extends OnlineActivity implements HeadlinesEventList m_drawerLayout.openDrawer(GravityCompat.START); } else { - try { - super.onBackPressed(); - } catch (IllegalStateException e) { - // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - e.printStackTrace(); - } + try { + super.onBackPressed(); + } catch (IllegalStateException e) { + // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState + e.printStackTrace(); + } } } - @Override - protected void loginSuccess(boolean refresh) { - invalidateOptionsMenu(); + @Override + protected void loginSuccess(boolean refresh) { + invalidateOptionsMenu(); - if (refresh) refresh(); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); + if (refresh) refresh(); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); - out.putParcelable("m_activeFeed", m_activeFeed); + out.putParcelable("m_activeFeed", m_activeFeed); - Application.getInstance().save(out); - } - - @Override - public void onResume() { - super.onResume(); - invalidateOptionsMenu(); + Application.getInstance().save(out); + } - } + @Override + public void onResume() { + super.onResume(); + invalidateOptionsMenu(); - public void onArticleSelected(Article article) { - Article articleClone = new Article(article); + } - if (articleClone.unread) { - articleClone.unread = false; - saveArticleUnread(articleClone); - } + public void onArticleSelected(Article article) { + Article articleClone = new Article(article); - Application.getArticlesModel().setActive(articleClone); + if (articleClone.unread) { + articleClone.unread = false; + saveArticleUnread(articleClone); + } - if (m_prefs.getBoolean("always_open_uri", false)) { - openUri(Uri.parse(article.link)); - } else { - Intent intent = new Intent(MasterActivity.this, DetailActivity.class); - intent.putExtra("feed", m_activeFeed); + Application.getArticlesModel().setActive(articleClone); - startActivityForResult(intent, HEADLINES_REQUEST); - overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); - } - } + if (m_prefs.getBoolean("always_open_uri", false)) { + openUri(Uri.parse(article.link)); + } else { + Intent intent = new Intent(MasterActivity.this, DetailActivity.class); + intent.putExtra("feed", m_activeFeed); + + startActivityForResult(intent, HEADLINES_REQUEST); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + } + } @Override public void onPause() { super.onPause(); - Date date = new Date(); + Date date = new Date(); - if (isFinishing() || date.getTime() - m_lastWidgetRefresh > 60*1000) { - m_lastWidgetRefresh = date.getTime(); + if (isFinishing() || date.getTime() - m_lastWidgetRefresh > 60 * 1000) { + m_lastWidgetRefresh = date.getTime(); - CommonActivity.requestWidgetUpdate(MasterActivity.this); - } + CommonActivity.requestWidgetUpdate(MasterActivity.this); + } } - @Override - public void onHeadlinesLoaded(boolean appended) { - setLoadingVisible(false); - } + @Override + public void onHeadlinesLoaded(boolean appended) { + setLoadingVisible(false); + } - @Override - public void onHeadlinesLoadingProgress(int progress) { - setLoadingVisible(progress < 100); - setLoadingProgress(progress); - } + @Override + public void onHeadlinesLoadingProgress(int progress) { + setLoadingVisible(progress < 100); + setLoadingProgress(progress); + } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); - Log.d(TAG, "onActivityResult:" + requestCode + " "+ resultCode + " " + data); + Log.d(TAG, "onActivityResult:" + requestCode + " " + resultCode + " " + data); - if (requestCode == HEADLINES_REQUEST) { - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + if (requestCode == HEADLINES_REQUEST) { + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - if (hf != null) { - Article activeArticle = Application.getArticlesModel().getActiveArticle(); + if (hf != null) { + Article activeArticle = Application.getArticlesModel().getActiveArticle(); - if (activeArticle != null) { - Log.d(TAG, "got back from detail activity, scrolling to active article=" + activeArticle); - hf.scrollToArticle(activeArticle); - } - } - } - } + if (activeArticle != null) { + Log.d(TAG, "got back from detail activity, scrolling to active article=" + activeArticle); + hf.scrollToArticle(activeArticle); + } + } + } + } - public void unsubscribeFeed(final Feed feed) { - ApiRequest req = new ApiRequest(getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - refresh(); - } - }; + public void unsubscribeFeed(final Feed feed) { + ApiRequest req = new ApiRequest(getApplicationContext()) { + protected void onPostExecute(JsonElement result) { + refresh(); + } + }; - HashMap<String, String> map = new HashMap<>(); - map.put("sid", getSessionId()); - map.put("op", "unsubscribeFeed"); - map.put("feed_id", String.valueOf(feed.id)); - - req.execute(map); + HashMap<String, String> map = new HashMap<>(); + map.put("sid", getSessionId()); + map.put("op", "unsubscribeFeed"); + map.put("feed_id", String.valueOf(feed.id)); - } + req.execute(map); - public Feed getActiveFeed() { - return m_activeFeed; - } + } - public void setActiveFeed(Feed feed) { - m_activeFeed = feed; + public Feed getActiveFeed() { + return m_activeFeed; + } + + public void setActiveFeed(Feed feed) { + m_activeFeed = feed; - setTitle(feed.title); + setTitle(feed.title); - FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - if (ff != null) { - ff.setSelectedFeed(feed); - } + if (ff != null) { + ff.setSelectedFeed(feed); + } - } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java index 50f9d533..fb423de1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/OnlineActivity.java @@ -53,74 +53,74 @@ import java.util.stream.Collectors; @SuppressLint("StaticFieldLeak") public class OnlineActivity extends CommonActivity { - private final String TAG = this.getClass().getSimpleName(); + private final String TAG = this.getClass().getSimpleName(); - protected SharedPreferences m_prefs; - protected Menu m_menu; + protected SharedPreferences m_prefs; + protected Menu m_menu; - private ActionMode m_headlinesActionMode; - private HeadlinesActionModeCallback m_headlinesActionModeCallback; + private ActionMode m_headlinesActionMode; + private HeadlinesActionModeCallback m_headlinesActionModeCallback; - private String m_lastImageHitTestUrl; + private String m_lastImageHitTestUrl; protected LinearProgressIndicator m_loadingProgress; - public void catchupDialog(final Feed feed) { + public void catchupDialog(final Feed feed) { - if (getApiLevel() >= 15) { + if (getApiLevel() >= 15) { - int selectedIndex = 0; + int selectedIndex = 0; - final String searchQuery = Application.getArticlesModel().getSearchQuery(); + final String searchQuery = Application.getArticlesModel().getSearchQuery(); - int titleStringId = !searchQuery.isEmpty() ? R.string.catchup_dialog_title_search : R.string.catchup_dialog_title; + int titleStringId = !searchQuery.isEmpty() ? R.string.catchup_dialog_title_search : R.string.catchup_dialog_title; - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(getString(titleStringId, feed.title)) - .setSingleChoiceItems( - new String[] { - getString(R.string.catchup_dialog_all_articles), - getString(R.string.catchup_dialog_1day), - getString(R.string.catchup_dialog_1week), - getString(R.string.catchup_dialog_2week) - }, - selectedIndex, (dialog, which) -> { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setTitle(getString(titleStringId, feed.title)) + .setSingleChoiceItems( + new String[]{ + getString(R.string.catchup_dialog_all_articles), + getString(R.string.catchup_dialog_1day), + getString(R.string.catchup_dialog_1week), + getString(R.string.catchup_dialog_2week) + }, + selectedIndex, (dialog, which) -> { }) - .setPositiveButton(R.string.catchup, + .setPositiveButton(R.string.catchup, (dialog, which) -> { - ListView list = ((AlertDialog)dialog).getListView(); + ListView list = ((AlertDialog) dialog).getListView(); if (list.getCheckedItemCount() > 0) { int position = list.getCheckedItemPosition(); - String[] catchupModes = { "all", "1day", "1week", "2week" }; + String[] catchupModes = {"all", "1day", "1week", "2week"}; String mode = catchupModes[position]; catchupFeed(feed, mode, true, searchQuery); } }) - .setNegativeButton(R.string.dialog_cancel, + .setNegativeButton(R.string.dialog_cancel, (dialog, which) -> { }); - Dialog dialog = builder.create(); - dialog.show(); + Dialog dialog = builder.create(); + dialog.show(); - } else { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(getString(R.string.catchup_dialog_title, feed.title)) - .setPositiveButton(R.string.catchup, + } else { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setMessage(getString(R.string.catchup_dialog_title, feed.title)) + .setPositiveButton(R.string.catchup, (dialog, which) -> catchupFeed(feed, "all", true, "")) - .setNegativeButton(R.string.dialog_cancel, + .setNegativeButton(R.string.dialog_cancel, (dialog, which) -> { }); - Dialog dialog = builder.create(); - dialog.show(); - } - } + Dialog dialog = builder.create(); + dialog.show(); + } + } public void confirmCatchupAbove(final Article article) { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) @@ -128,7 +128,8 @@ public class OnlineActivity extends CommonActivity { .setPositiveButton(R.string.dialog_ok, (dialog, which) -> catchupAbove(article)) .setNegativeButton(R.string.dialog_cancel, - (dialog, which) -> { }); + (dialog, which) -> { + }); Dialog dialog = builder.create(); dialog.show(); @@ -136,137 +137,138 @@ public class OnlineActivity extends CommonActivity { //protected PullToRefreshAttacher m_pullToRefreshAttacher; - protected static abstract class OnLoginFinishedListener { - public abstract void OnLoginSuccess(); - public abstract void OnLoginFailed(); - } - - private class HeadlinesActionModeCallback implements ActionMode.Callback { - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - m_headlinesActionMode = null; + protected static abstract class OnLoginFinishedListener { + public abstract void OnLoginSuccess(); + + public abstract void OnLoginFailed(); + } + + private class HeadlinesActionModeCallback implements ActionMode.Callback { + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + m_headlinesActionMode = null; Application.getArticlesModel().setSelection(ArticleModel.ArticlesSelection.NONE); invalidateOptionsMenu(); - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.action_mode_headlines, menu); - - return true; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - onOptionsItemSelected(item); - return false; - } - } + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.action_mode_headlines, menu); + + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + onOptionsItemSelected(item); + return false; + } + } protected String getSessionId() { - return Application.getInstance().getSessionId(); - } + return Application.getInstance().getSessionId(); + } - protected void setSessionId(String sessionId) { - Application.getInstance().setSessionId(sessionId); - } - - @Override - public void onCreate(Bundle savedInstanceState) { + protected void setSessionId(String sessionId) { + Application.getInstance().setSessionId(sessionId); + } + + @Override + public void onCreate(Bundle savedInstanceState) { - // we use that before parent onCreate so let's init locally - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); + // we use that before parent onCreate so let's init locally + m_prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); - setAppTheme(m_prefs); + setAppTheme(m_prefs); - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); - setContentView(R.layout.activity_login); + setContentView(R.layout.activity_login); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); - m_headlinesActionModeCallback = new HeadlinesActionModeCallback(); + m_headlinesActionModeCallback = new HeadlinesActionModeCallback(); ArticleModel model = Application.getArticlesModel(); model.getActive().observe(this, (articles) -> { invalidateOptionsMenu(); - }); + }); } - public void login() { - login(false, null); - } - - public void login(boolean refresh) { - login(refresh, null); - } - - public void login(boolean refresh, OnLoginFinishedListener listener) { - - if (m_prefs.getString("ttrss_url", "").trim().isEmpty()) { - - setLoadingStatus(R.string.login_need_configure); - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(R.string.dialog_need_configure_prompt) - .setCancelable(false) - .setPositiveButton(R.string.dialog_open_preferences, (dialog, id) -> { - // launch preferences - - Intent intent = new Intent(OnlineActivity.this, - PreferencesActivity.class); - startActivityForResult(intent, 0); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); - - Dialog alert = builder.create(); - alert.show(); - - } else { - setLoadingStatus(R.string.login_in_progress); - - LoginRequest ar = new LoginRequest(getApplicationContext(), refresh, listener); - - HashMap<String, String> map = new HashMap<>(); - map.put("op", "login"); - map.put("user", m_prefs.getString("login", "").trim()); - map.put("password", m_prefs.getString("password", "").trim()); - - ar.execute(map); - - setLoadingStatus(R.string.login_in_progress); - } - } - - protected void loginSuccess(boolean refresh) { - setLoadingStatus(R.string.blank); - - initMenu(); - - Intent intent = new Intent(OnlineActivity.this, MasterActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - - startActivityForResult(intent, 0); - overridePendingTransition(0, 0); - - finish(); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { + public void login() { + login(false, null); + } + + public void login(boolean refresh) { + login(refresh, null); + } + + public void login(boolean refresh, OnLoginFinishedListener listener) { + + if (m_prefs.getString("ttrss_url", "").trim().isEmpty()) { + + setLoadingStatus(R.string.login_need_configure); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setMessage(R.string.dialog_need_configure_prompt) + .setCancelable(false) + .setPositiveButton(R.string.dialog_open_preferences, (dialog, id) -> { + // launch preferences + + Intent intent = new Intent(OnlineActivity.this, + PreferencesActivity.class); + startActivityForResult(intent, 0); + }) + .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); + + Dialog alert = builder.create(); + alert.show(); + + } else { + setLoadingStatus(R.string.login_in_progress); + + LoginRequest ar = new LoginRequest(getApplicationContext(), refresh, listener); + + HashMap<String, String> map = new HashMap<>(); + map.put("op", "login"); + map.put("user", m_prefs.getString("login", "").trim()); + map.put("password", m_prefs.getString("password", "").trim()); + + ar.execute(map); + + setLoadingStatus(R.string.login_in_progress); + } + } + + protected void loginSuccess(boolean refresh) { + setLoadingStatus(R.string.blank); + + initMenu(); + + Intent intent = new Intent(OnlineActivity.this, MasterActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + + startActivityForResult(intent, 0); + overridePendingTransition(0, 0); + + finish(); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.article_img_open) { if (getLastContentImageHitTestUrl() != null) { @@ -318,43 +320,43 @@ public class OnlineActivity extends CommonActivity { return super.onContextItemSelected(item); } - public void displayAttachments(Article article) { - if (article != null && article.attachments != null && !article.attachments.isEmpty()) { - CharSequence[] items = new CharSequence[article.attachments.size()]; - final CharSequence[] itemUrls = new CharSequence[article.attachments.size()]; + public void displayAttachments(Article article) { + if (article != null && article.attachments != null && !article.attachments.isEmpty()) { + CharSequence[] items = new CharSequence[article.attachments.size()]; + final CharSequence[] itemUrls = new CharSequence[article.attachments.size()]; - for (int i = 0; i < article.attachments.size(); i++) { - items[i] = article.attachments.get(i).title != null && !article.attachments.get(i).title.isEmpty() ? - article.attachments.get(i).title : article.attachments.get(i).content_url; + for (int i = 0; i < article.attachments.size(); i++) { + items[i] = article.attachments.get(i).title != null && !article.attachments.get(i).title.isEmpty() ? + article.attachments.get(i).title : article.attachments.get(i).content_url; - itemUrls[i] = article.attachments.get(i).content_url; - } + itemUrls[i] = article.attachments.get(i).content_url; + } - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(R.string.attachments_prompt) - .setCancelable(true) - .setSingleChoiceItems(items, 0, (dialog, which) -> { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setTitle(R.string.attachments_prompt) + .setCancelable(true) + .setSingleChoiceItems(items, 0, (dialog, which) -> { // }).setNeutralButton(R.string.attachment_copy, (dialog, which) -> { - int selectedPosition = ((AlertDialog)dialog).getListView().getCheckedItemPosition(); + int selectedPosition = ((AlertDialog) dialog).getListView().getCheckedItemPosition(); - copyToClipboard((String)itemUrls[selectedPosition]); + copyToClipboard((String) itemUrls[selectedPosition]); }).setPositiveButton(R.string.attachment_view, (dialog, id) -> { - int selectedPosition = ((AlertDialog)dialog).getListView().getCheckedItemPosition(); + int selectedPosition = ((AlertDialog) dialog).getListView().getCheckedItemPosition(); - openUri(Uri.parse((String)itemUrls[selectedPosition])); + openUri(Uri.parse((String) itemUrls[selectedPosition])); dialog.cancel(); }).setNegativeButton(R.string.dialog_cancel, (dialog, id) -> dialog.cancel()); - Dialog dialog = builder.create(); - dialog.show(); - } - } + Dialog dialog = builder.create(); + dialog.show(); + } + } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - final HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); Article activeArticle = Application.getArticlesModel().getActiveArticle(); @@ -362,13 +364,13 @@ public class OnlineActivity extends CommonActivity { int itemId = item.getItemId(); if (itemId == R.id.subscribe_to_feed) { - Intent subscribe = new Intent(OnlineActivity.this, SubscribeActivity.class); - startActivityForResult(subscribe, 0); - return true; - } else if (itemId == R.id.toggle_attachments) { + Intent subscribe = new Intent(OnlineActivity.this, SubscribeActivity.class); + startActivityForResult(subscribe, 0); + return true; + } else if (itemId == R.id.toggle_attachments) { if (activeArticle != null) displayAttachments(activeArticle); - return true; + return true; } else if (itemId == R.id.login) { login(); return true; @@ -422,7 +424,7 @@ public class OnlineActivity extends CommonActivity { int selectedIndex = Arrays.asList(headlineModeValues).indexOf(headlineMode); - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) .setTitle(R.string.headlines_set_display_mode) .setSingleChoiceItems(headlineModeNames, selectedIndex, (dialog2, which) -> { @@ -469,7 +471,7 @@ public class OnlineActivity extends CommonActivity { break; } - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) .setTitle(R.string.headlines_set_view_mode) .setSingleChoiceItems( new String[]{ @@ -536,16 +538,16 @@ public class OnlineActivity extends CommonActivity { Dialog dialog = builder.create(); dialog.show(); return true; - } else if (itemId == R.id.share_article) { + } else if (itemId == R.id.share_article) { if (activeArticle != null) shareArticle(activeArticle); - return true; - } else if (itemId == R.id.article_set_score) { + return true; + } else if (itemId == R.id.article_set_score) { if (activeArticle != null) { setArticleScore(activeArticle); } - return true; + return true; } else if (itemId == R.id.toggle_marked) { if (activeArticle != null) { Article articleClone = new Article(activeArticle); @@ -625,7 +627,7 @@ public class OnlineActivity extends CommonActivity { return super.onOptionsItemSelected(item); } - protected void catchupAbove(final Article startingArticle) { + protected void catchupAbove(final Article startingArticle) { if (startingArticle != null) { List<Article> tmp = new ArrayList<>(); @@ -646,53 +648,54 @@ public class OnlineActivity extends CommonActivity { setArticlesUnread(tmp, Article.UPDATE_SET_FALSE); } } - } + } + + public void editArticleNote(final Article article) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setTitle(article.title); - public void editArticleNote(final Article article) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(article.title); + final EditText topicEdit = new EditText(this); + topicEdit.setText(article.note); + builder.setView(topicEdit); - final EditText topicEdit = new EditText(this); - topicEdit.setText(article.note); - builder.setView(topicEdit); - - builder.setPositiveButton(R.string.article_edit_note, (dialog, which) -> { + builder.setPositiveButton(R.string.article_edit_note, (dialog, which) -> { String note = topicEdit.getText().toString().trim(); saveArticleNote(article, note); }); - - builder.setNegativeButton(R.string.dialog_cancel, (dialog, which) -> { + + builder.setNegativeButton(R.string.dialog_cancel, (dialog, which) -> { // }); - - Dialog dialog = builder.create(); - dialog.show(); - } - - public void editArticleLabels(Article article) { - final int articleId = article.id; - - ApiRequest req = new ApiRequest(getApplicationContext()) { - @Override - protected void onPostExecute(JsonElement result) { - if (result != null) { - Type listType = new TypeToken<List<Label>>() {}.getType(); - final List<Label> labels = new Gson().fromJson(result, listType); - - CharSequence[] items = new CharSequence[labels.size()]; - final int[] itemIds = new int[labels.size()]; - boolean[] checkedItems = new boolean[labels.size()]; - - for (int i = 0; i < labels.size(); i++) { - items[i] = labels.get(i).caption; - itemIds[i] = labels.get(i).id; - checkedItems[i] = labels.get(i).checked; - } - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(OnlineActivity.this) - .setTitle(R.string.article_set_labels) - .setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> { + + Dialog dialog = builder.create(); + dialog.show(); + } + + public void editArticleLabels(Article article) { + final int articleId = article.id; + + ApiRequest req = new ApiRequest(getApplicationContext()) { + @Override + protected void onPostExecute(JsonElement result) { + if (result != null) { + Type listType = new TypeToken<List<Label>>() { + }.getType(); + final List<Label> labels = new Gson().fromJson(result, listType); + + CharSequence[] items = new CharSequence[labels.size()]; + final int[] itemIds = new int[labels.size()]; + boolean[] checkedItems = new boolean[labels.size()]; + + for (int i = 0; i < labels.size(); i++) { + items[i] = labels.get(i).caption; + itemIds[i] = labels.get(i).id; + checkedItems[i] = labels.get(i).checked; + } + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(OnlineActivity.this) + .setTitle(R.string.article_set_labels) + .setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> { final int labelId = itemIds[which]; HashMap<String, String> map = new HashMap<>(); @@ -707,94 +710,94 @@ public class OnlineActivity extends CommonActivity { }).setPositiveButton(R.string.dialog_close, (dialog, which) -> dialog.cancel()); - Dialog dialog = builder.create(); - dialog.show(); - - } - } - }; - - HashMap<String, String> map = new HashMap<>(); - map.put("sid", getSessionId()); - map.put("op", "getLabels"); - map.put("article_id", String.valueOf(articleId)); - - req.execute(map); - } + Dialog dialog = builder.create(); + dialog.show(); + + } + } + }; + + HashMap<String, String> map = new HashMap<>(); + map.put("sid", getSessionId()); + map.put("op", "getLabels"); + map.put("article_id", String.valueOf(articleId)); + + req.execute(map); + } private void setLoadingStatus(int status) { - setLoadingStatus(getString(status)); + setLoadingStatus(getString(status)); } - private void setLoadingStatus(String status) { - TextView tv = findViewById(R.id.loading_message); + private void setLoadingStatus(String status) { + TextView tv = findViewById(R.id.loading_message); - if (tv != null) { - tv.setText(status); - } - } + if (tv != null) { + tv.setText(status); + } + } protected void logout() { - setSessionId(null); - - setLoadingStatus(R.string.login_ready); - - initMenu(); - } - - protected void loginFailure() { - setSessionId(null); - initMenu(); - } - - @Override - public void onResume() { - super.onResume(); - - if (getSessionId() == null) { - login(); - } else { - loginSuccess(false); - } - } - - public Menu getMenu() { - return m_menu; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.activity_main, menu); - - m_menu = menu; - - initMenu(); - - return true; - } - - public int getApiLevel() { - return Application.getInstance().getApiLevel(); - } - - protected void setApiLevel(int apiLevel) { - Application.getInstance().setApiLevel(apiLevel); - } - - public void shareArticle(Article article) { - if (article != null) { - shareText(article.link, article.title); - } - } - - public void setArticleScore(Article article) { - final EditText edit = new EditText(this); - edit.setText(String.valueOf(article.score)); - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(R.string.score_for_this_article) - .setPositiveButton(R.string.set_score, + setSessionId(null); + + setLoadingStatus(R.string.login_ready); + + initMenu(); + } + + protected void loginFailure() { + setSessionId(null); + initMenu(); + } + + @Override + public void onResume() { + super.onResume(); + + if (getSessionId() == null) { + login(); + } else { + loginSuccess(false); + } + } + + public Menu getMenu() { + return m_menu; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.activity_main, menu); + + m_menu = menu; + + initMenu(); + + return true; + } + + public int getApiLevel() { + return Application.getInstance().getApiLevel(); + } + + protected void setApiLevel(int apiLevel) { + Application.getInstance().setApiLevel(apiLevel); + } + + public void shareArticle(Article article) { + if (article != null) { + shareText(article.link, article.title); + } + } + + public void setArticleScore(Article article) { + final EditText edit = new EditText(this); + edit.setText(String.valueOf(article.score)); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setTitle(R.string.score_for_this_article) + .setPositiveButton(R.string.set_score, (dialog, which) -> { try { @@ -808,115 +811,115 @@ public class OnlineActivity extends CommonActivity { e.printStackTrace(); } }) - .setNegativeButton(getString(R.string.cancel), + .setNegativeButton(getString(R.string.cancel), (dialog, which) -> { // }).setView(edit); - Dialog dialog = builder.create(); - dialog.show(); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (ap != null && ap.isAdded()) { - ap.switchToArticle(false); - return true; - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (ap != null && ap.isAdded()) { - ap.switchToArticle(true); - return true; - } - break; - case KeyEvent.KEYCODE_ESCAPE: - moveTaskToBack(true); - return true; - case KeyEvent.KEYCODE_O: - if (ap != null) { - Article selectedArticle = Application.getArticlesModel().getActiveArticle(); + Dialog dialog = builder.create(); + dialog.show(); + } - if (selectedArticle != null) - openUri(Uri.parse(selectedArticle.link)); - } - return true; - case KeyEvent.KEYCODE_R: - refresh(); - return true; - case KeyEvent.KEYCODE_U: - if (ap != null) { - Article selectedArticle = Application.getArticlesModel().getActiveArticle(); - - if (selectedArticle != null) { - selectedArticle.unread = !selectedArticle.unread; - saveArticleUnread(selectedArticle); + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (ap != null && ap.isAdded()) { + ap.switchToArticle(false); + return true; + } + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (ap != null && ap.isAdded()) { + ap.switchToArticle(true); + return true; } - } - return true; - } - - if (m_prefs.getBoolean("use_volume_keys", false)) { - - if (ap != null && ap.isAdded()) { - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_UP: - ap.switchToArticle(false); - return true; - case KeyEvent.KEYCODE_VOLUME_DOWN: - ap.switchToArticle(true); - return true; - } - } - } - - return super.onKeyDown(keyCode, event); - } - - // Handle onKeyUp too to suppress beep - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (m_prefs.getBoolean("use_volume_keys", false)) { - - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: - return true; - } - } - - return super.onKeyUp(keyCode, event); - } - - public void catchupFeed(final Feed feed, final String mode, final boolean refreshAfter, final String searchQuery) { - Log.d(TAG, "catchupFeed=" + feed + "; mode=" + mode + "; search=" + searchQuery); - - ApiRequest req = new ApiRequest(getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - if (refreshAfter) - refresh(); - } - }; - - HashMap<String, String> map = new HashMap<>(); - map.put("sid", getSessionId()); - map.put("op", "catchupFeed"); - map.put("feed_id", String.valueOf(feed.id)); - map.put("search_query", searchQuery); - map.put("search_lang", ""); // for the time being always user per-user default - map.put("mode", mode); - if (feed.is_cat) - map.put("is_cat", "1"); - - req.execute(map); - } + break; + case KeyEvent.KEYCODE_ESCAPE: + moveTaskToBack(true); + return true; + case KeyEvent.KEYCODE_O: + if (ap != null) { + Article selectedArticle = Application.getArticlesModel().getActiveArticle(); + + if (selectedArticle != null) + openUri(Uri.parse(selectedArticle.link)); + } + return true; + case KeyEvent.KEYCODE_R: + refresh(); + return true; + case KeyEvent.KEYCODE_U: + if (ap != null) { + Article selectedArticle = Application.getArticlesModel().getActiveArticle(); + + if (selectedArticle != null) { + selectedArticle.unread = !selectedArticle.unread; + saveArticleUnread(selectedArticle); + } + } + return true; + } + + if (m_prefs.getBoolean("use_volume_keys", false)) { + + if (ap != null && ap.isAdded()) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + ap.switchToArticle(false); + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + ap.switchToArticle(true); + return true; + } + } + } + + return super.onKeyDown(keyCode, event); + } + + // Handle onKeyUp too to suppress beep + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (m_prefs.getBoolean("use_volume_keys", false)) { + + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + return true; + } + } + + return super.onKeyUp(keyCode, event); + } + + public void catchupFeed(final Feed feed, final String mode, final boolean refreshAfter, final String searchQuery) { + Log.d(TAG, "catchupFeed=" + feed + "; mode=" + mode + "; search=" + searchQuery); + + ApiRequest req = new ApiRequest(getApplicationContext()) { + protected void onPostExecute(JsonElement result) { + if (refreshAfter) + refresh(); + } + }; + + HashMap<String, String> map = new HashMap<>(); + map.put("sid", getSessionId()); + map.put("op", "catchupFeed"); + map.put("feed_id", String.valueOf(feed.id)); + map.put("search_query", searchQuery); + map.put("search_lang", ""); // for the time being always user per-user default + map.put("mode", mode); + if (feed.is_cat) + map.put("is_cat", "1"); + + req.execute(map); + } public void saveArticleUnread(final Article article) { setArticlesField(Collections.singletonList(article), Article.UPDATE_FIELD_UNREAD, @@ -945,25 +948,25 @@ public class OnlineActivity extends CommonActivity { setArticlesMarked(articles, Article.UPDATE_TOGGLE); } - public void setArticlesMarked(final List<Article> articles, int mode) { + public void setArticlesMarked(final List<Article> articles, int mode) { setArticlesField(articles, Article.UPDATE_FIELD_MARKED, mode); - } + } public void toggleArticlesUnread(final List<Article> articles) { setArticlesUnread(articles, Article.UPDATE_FIELD_UNREAD); } - public void setArticlesUnread(final List<Article> articles, int mode) { + public void setArticlesUnread(final List<Article> articles, int mode) { setArticlesField(articles, Article.UPDATE_FIELD_UNREAD, mode); - } + } public void toggleArticlesPublished(final List<Article> articles) { setArticlesPublished(articles, Article.UPDATE_TOGGLE); } - public void setArticlesPublished(final List<Article> articles, int mode) { + public void setArticlesPublished(final List<Article> articles, int mode) { setArticlesField(articles, Article.UPDATE_FIELD_PUBLISHED, mode); - } + } public void setArticlesField(final List<Article> articles, int field, int mode) { ApiRequest req = new ApiRequest(getApplicationContext()) { @@ -1063,30 +1066,30 @@ public class OnlineActivity extends CommonActivity { req.execute(map); } - - // this may be called after activity has been destroyed (i.e. long asynctask) - protected void initMenu() { - if (m_menu != null) { - if (getSessionId() != null) { - m_menu.setGroupVisible(R.id.menu_group_feeds, true); + + // this may be called after activity has been destroyed (i.e. long asynctask) + protected void initMenu() { + if (m_menu != null) { + if (getSessionId() != null) { + m_menu.setGroupVisible(R.id.menu_group_feeds, true); m_menu.setGroupVisible(R.id.menu_group_headlines, true); m_menu.setGroupVisible(R.id.menu_group_article, true); - m_menu.setGroupVisible(R.id.menu_group_logged_out, false); - } else { + m_menu.setGroupVisible(R.id.menu_group_logged_out, false); + } else { m_menu.setGroupVisible(R.id.menu_group_feeds, false); m_menu.setGroupVisible(R.id.menu_group_headlines, false); m_menu.setGroupVisible(R.id.menu_group_article, false); - m_menu.setGroupVisible(R.id.menu_group_logged_out, true); - } + m_menu.setGroupVisible(R.id.menu_group_logged_out, true); + } - m_menu.setGroupVisible(R.id.menu_group_headlines, false); - m_menu.setGroupVisible(R.id.menu_group_article, false); - m_menu.setGroupVisible(R.id.menu_group_feeds, false); + m_menu.setGroupVisible(R.id.menu_group_headlines, false); + m_menu.setGroupVisible(R.id.menu_group_article, false); + m_menu.setGroupVisible(R.id.menu_group_feeds, false); - m_menu.findItem(R.id.subscribe_to_feed).setEnabled(getApiLevel() >= 5); + m_menu.findItem(R.id.subscribe_to_feed).setEnabled(getApiLevel() >= 5); - MenuItem search = m_menu.findItem(R.id.search); - search.setEnabled(getApiLevel() >= 2); + MenuItem search = m_menu.findItem(R.id.search); + search.setEnabled(getApiLevel() >= 2); Article activeArticle = Application.getArticlesModel().getActiveArticle(); @@ -1117,195 +1120,196 @@ public class OnlineActivity extends CommonActivity { R.drawable.baseline_rss_feed_24); } } - } - } - - protected void refresh(boolean includeHeadlines) { - FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - - if (ff != null) { - ff.refresh(); - } - - if (includeHeadlines) { - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - if (hf != null) { - hf.refresh(false); - } - } - } - - protected void refresh() { - refresh(true); - } - - protected class LoginRequest extends ApiRequest { - boolean m_refreshAfterLogin; - OnLoginFinishedListener m_listener; - - public LoginRequest(Context context, boolean refresh, OnLoginFinishedListener listener) { - super(context); - m_refreshAfterLogin = refresh; - m_listener = listener; - } - - @SuppressLint("StaticFieldLeak") - protected void onPostExecute(JsonElement result) { - if (result != null) { - try { - JsonObject content = result.getAsJsonObject(); - - if (content != null) { - setSessionId(content.get("session_id").getAsString()); - - JsonElement apiLevel = content.get("api_level"); - - Log.d(TAG, "Authenticated!"); - - if (apiLevel != null) { - setApiLevel(apiLevel.getAsInt()); - Log.d(TAG, "Received API level: " + getApiLevel()); - - // get custom sort from configuration object - if (getApiLevel() >= 17) { - - // daemon_is_running, icons_dir, etc... - JsonObject config = content.get("config").getAsJsonObject(); - - Type hashType = new TypeToken<Map<String, String>>(){}.getType(); - Map<String, String> customSortTypes = new Gson().fromJson(config.get("custom_sort_types"), hashType); - - setCustomSortModes(customSortTypes); - } - - if (m_listener != null) { - m_listener.OnLoginSuccess(); - } else { - loginSuccess(m_refreshAfterLogin); - } - - } else { - - ApiRequest req = new ApiRequest(OnlineActivity.this) { - protected void onPostExecute(JsonElement result) { - setApiLevel(0); - - if (result != null) { - try { - setApiLevel(result.getAsJsonObject().get("level").getAsInt()); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (m_lastError != ApiCommon.ApiError.API_UNKNOWN_METHOD) { - // Unknown method means old tt-rss, in that case we assume API 0 and continue - - setLoadingStatus(getErrorMessage()); - - if (m_lastErrorMessage != null) { - setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_lastErrorMessage); - } else { - setLoadingStatus(getErrorMessage()); - } - - if (m_listener != null) { - m_listener.OnLoginFailed(); - } else { - loginFailure(); - } - - return; - } - - Log.d(TAG, "Received API level: " + getApiLevel()); - - loginSuccess(m_refreshAfterLogin); - } - }; - - HashMap<String, String> map = new HashMap<>(); - map.put("sid", getSessionId()); - map.put("op", "getApiLevel"); - - req.execute(map); - - setLoadingStatus(R.string.loading_message); - } - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - setSessionId(null); - - if (m_lastErrorMessage != null) { - setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_lastErrorMessage); - } else { - setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_apiStatusCode); - } - - loginFailure(); - } - - } - - public LinkedHashMap<String, String> getSortModes() { - LinkedHashMap<String, String> tmp = new LinkedHashMap<>(); - - tmp.put("default", getString(R.string.headlines_sort_default)); - tmp.put("feed_dates", getString(R.string.headlines_sort_newest_first)); - tmp.put("date_reverse", getString(R.string.headlines_sort_oldest_first)); - tmp.put("title", getString(R.string.headlines_sort_title)); - - tmp.putAll(Application.getInstance().m_customSortModes); - - return tmp; - } - - public String getSortMode() { + } + } + + protected void refresh(boolean includeHeadlines) { + FeedsFragment ff = (FeedsFragment) getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); + + if (ff != null) { + ff.refresh(); + } + + if (includeHeadlines) { + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + + if (hf != null) { + hf.refresh(false); + } + } + } + + protected void refresh() { + refresh(true); + } + + protected class LoginRequest extends ApiRequest { + boolean m_refreshAfterLogin; + OnLoginFinishedListener m_listener; + + public LoginRequest(Context context, boolean refresh, OnLoginFinishedListener listener) { + super(context); + m_refreshAfterLogin = refresh; + m_listener = listener; + } + + @SuppressLint("StaticFieldLeak") + protected void onPostExecute(JsonElement result) { + if (result != null) { + try { + JsonObject content = result.getAsJsonObject(); + + if (content != null) { + setSessionId(content.get("session_id").getAsString()); + + JsonElement apiLevel = content.get("api_level"); + + Log.d(TAG, "Authenticated!"); + + if (apiLevel != null) { + setApiLevel(apiLevel.getAsInt()); + Log.d(TAG, "Received API level: " + getApiLevel()); + + // get custom sort from configuration object + if (getApiLevel() >= 17) { + + // daemon_is_running, icons_dir, etc... + JsonObject config = content.get("config").getAsJsonObject(); + + Type hashType = new TypeToken<Map<String, String>>() { + }.getType(); + Map<String, String> customSortTypes = new Gson().fromJson(config.get("custom_sort_types"), hashType); + + setCustomSortModes(customSortTypes); + } + + if (m_listener != null) { + m_listener.OnLoginSuccess(); + } else { + loginSuccess(m_refreshAfterLogin); + } + + } else { + + ApiRequest req = new ApiRequest(OnlineActivity.this) { + protected void onPostExecute(JsonElement result) { + setApiLevel(0); + + if (result != null) { + try { + setApiLevel(result.getAsJsonObject().get("level").getAsInt()); + } catch (Exception e) { + e.printStackTrace(); + } + } else if (m_lastError != ApiCommon.ApiError.API_UNKNOWN_METHOD) { + // Unknown method means old tt-rss, in that case we assume API 0 and continue + + setLoadingStatus(getErrorMessage()); + + if (m_lastErrorMessage != null) { + setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_lastErrorMessage); + } else { + setLoadingStatus(getErrorMessage()); + } + + if (m_listener != null) { + m_listener.OnLoginFailed(); + } else { + loginFailure(); + } + + return; + } + + Log.d(TAG, "Received API level: " + getApiLevel()); + + loginSuccess(m_refreshAfterLogin); + } + }; + + HashMap<String, String> map = new HashMap<>(); + map.put("sid", getSessionId()); + map.put("op", "getApiLevel"); + + req.execute(map); + + setLoadingStatus(R.string.loading_message); + } + + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + setSessionId(null); + + if (m_lastErrorMessage != null) { + setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_lastErrorMessage); + } else { + setLoadingStatus(getString(getErrorMessage()) + "\n\n" + m_apiStatusCode); + } + + loginFailure(); + } + + } + + public LinkedHashMap<String, String> getSortModes() { + LinkedHashMap<String, String> tmp = new LinkedHashMap<>(); + + tmp.put("default", getString(R.string.headlines_sort_default)); + tmp.put("feed_dates", getString(R.string.headlines_sort_newest_first)); + tmp.put("date_reverse", getString(R.string.headlines_sort_oldest_first)); + tmp.put("title", getString(R.string.headlines_sort_title)); + + tmp.putAll(Application.getInstance().m_customSortModes); + + return tmp; + } + + public String getSortMode() { return m_prefs.getString("headlines_sort_mode", "default"); } public void setSortMode(String sortMode) { SharedPreferences.Editor editor = m_prefs.edit(); editor.putString("headlines_sort_mode", sortMode); - editor.apply(); + editor.apply(); } - private synchronized void setCustomSortModes(Map<String, String> modes) { - Application.getInstance().m_customSortModes.clear(); - Application.getInstance().m_customSortModes.putAll(modes); - } + private synchronized void setCustomSortModes(Map<String, String> modes) { + Application.getInstance().m_customSortModes.clear(); + Application.getInstance().m_customSortModes.putAll(modes); + } public void setViewMode(String viewMode) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putString("view_mode", viewMode); - editor.apply(); - } - - public String getViewMode() { - return m_prefs.getString("view_mode", "adaptive"); - } - - public void setLastContentImageHitTestUrl(String url) { - m_lastImageHitTestUrl = url; - } - - public String getLastContentImageHitTestUrl() { - return m_lastImageHitTestUrl; - } - - public int getResizeWidth() { - Display display = getWindowManager().getDefaultDisplay(); - Point size = new Point(); - display.getSize(size); - - return size.x > size.y ? (int)(size.y * 0.75) : (int)(size.x * 0.75); - } + SharedPreferences.Editor editor = m_prefs.edit(); + editor.putString("view_mode", viewMode); + editor.apply(); + } + + public String getViewMode() { + return m_prefs.getString("view_mode", "adaptive"); + } + + public void setLastContentImageHitTestUrl(String url) { + m_lastImageHitTestUrl = url; + } + + public String getLastContentImageHitTestUrl() { + return m_lastImageHitTestUrl; + } + + public int getResizeWidth() { + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + + return size.x > size.y ? (int) (size.y * 0.75) : (int) (size.x * 0.75); + } public void setLoadingProgress(int progress) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesActivity.java index c880161a..eba3e196 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesActivity.java @@ -8,7 +8,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.preference.PreferenceManager; public class PreferencesActivity extends CommonActivity { - @Override + @Override public void onCreate(Bundle savedInstanceState) { // we use that before parent onCreate so let's init locally m_prefs = PreferenceManager diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesFragment.java index 2eaeae0d..6826c453 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/PreferencesFragment.java @@ -37,8 +37,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat { findPreference("network_settings").setOnPreferenceClickListener(preference -> { getActivity().getSupportFragmentManager() .beginTransaction() - .replace(R.id.preferences_container, new NetworkPreferencesFragment() ) - .addToBackStack( NetworkPreferencesFragment.class.getSimpleName() ) + .replace(R.id.preferences_container, new NetworkPreferencesFragment()) + .addToBackStack(NetworkPreferencesFragment.class.getSimpleName()) .commit(); return false; @@ -73,6 +73,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { - setPreferencesFromResource(R.xml.preferences,rootKey); + setPreferencesFromResource(R.xml.preferences, rootKey); } }
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java index f8e2d254..8e936bad 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesFragment.java @@ -3,10 +3,10 @@ package org.fox.ttrss; import androidx.lifecycle.ViewModelProvider; public class RootCategoriesFragment extends FeedsFragment { - private final String TAG = this.getClass().getSimpleName(); + private final String TAG = this.getClass().getSimpleName(); - @Override - protected FeedsModel getModel() { - return new ViewModelProvider(this).get(RootCategoriesModel.class); - } + @Override + protected FeedsModel getModel() { + return new ViewModelProvider(this).get(RootCategoriesModel.class); + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesModel.java index d0a4ab16..59790346 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesModel.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/RootCategoriesModel.java @@ -46,7 +46,7 @@ public class RootCategoriesModel extends FeedsModel { params.put("op", "getFeeds"); params.put("cat_id", String.valueOf(Feed.CAT_SPECIAL)); params.put("include_nested", "true"); - params.put("sid", ((org.fox.ttrss.Application)getApplication()).getSessionId()); + params.put("sid", ((org.fox.ttrss.Application) getApplication()).getSessionId()); final JsonElement result = ApiCommon.performRequest(getApplication(), params, this); @@ -57,7 +57,8 @@ public class RootCategoriesModel extends FeedsModel { JsonArray content = result.getAsJsonArray(); if (content != null) { - Type listType = new TypeToken<List<Feed>>() { }.getType(); + Type listType = new TypeToken<List<Feed>>() { + }.getType(); List<Feed> feedsJson = new Gson().fromJson(content, listType); @@ -77,10 +78,10 @@ public class RootCategoriesModel extends FeedsModel { } // get all root categories - final HashMap<String,String> params = new HashMap<>(); + final HashMap<String, String> params = new HashMap<>(); params.put("op", "getCategories"); - params.put("sid", ((org.fox.ttrss.Application)getApplication()).getSessionId()); + params.put("sid", ((org.fox.ttrss.Application) getApplication()).getSessionId()); // this confusingly named option means "return top level categories only" params.put("enable_nested", "true"); @@ -96,14 +97,15 @@ public class RootCategoriesModel extends FeedsModel { JsonArray content = result.getAsJsonArray(); if (content != null) { - Type listType = new TypeToken<List<Feed>>() {}.getType(); + Type listType = new TypeToken<List<Feed>>() { + }.getType(); List<Feed> feedsJson = new Gson().fromJson(content, listType); // seems to be necessary evil because of deserialization feedsJson = feedsJson.stream().peek(Feed::fixNullFields).collect(Collectors.toList()); - sortFeeds(feedsJson, m_feed, new CatOrderComparator()); + sortFeeds(feedsJson, m_feed, new CatOrderComparator()); // virtual cats implemented in getCategories since api level 1 if (org.fox.ttrss.Application.getInstance().getApiLevel() == 0) { @@ -125,14 +127,14 @@ public class RootCategoriesModel extends FeedsModel { .peek(f -> f.is_cat = true) .collect(Collectors.toList()); - if (expandSpecial) { + if (expandSpecial) { feedsJson = feedsJson.stream() .filter(f -> f.id != Feed.CAT_SPECIAL) .collect(Collectors.toList()); if (!feedsJson.isEmpty()) feedsCombined.add(new Feed(Feed.TYPE_DIVIDER)); - } + } feedsCombined.addAll(feedsJson); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java index d196ab89..842f1f07 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java @@ -34,7 +34,8 @@ import okio.Source; @GlideModule public class OkHttpProgressGlideModule extends AppGlideModule { - @Override public void registerComponents(Context context, Glide glide, Registry registry) { + @Override + public void registerComponents(Context context, Glide glide, Registry registry) { OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(createInterceptor(new DispatchingProgressListener())) .build(); @@ -59,8 +60,10 @@ public class OkHttpProgressGlideModule extends AppGlideModule { public interface UIProgressListener { void onProgress(long bytesRead, long expectedLength); + /** * Control how often the listener needs an update. 0% and 100% will always be dispatched. + * * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress) */ float getGranualityPercentage(); @@ -69,6 +72,7 @@ public class OkHttpProgressGlideModule extends AppGlideModule { public static void forget(String url) { DispatchingProgressListener.forget(url); } + public static void expect(String url, UIProgressListener listener) { DispatchingProgressListener.expect(url, listener); } @@ -82,6 +86,7 @@ public class OkHttpProgressGlideModule extends AppGlideModule { private static final Map<String, Long> PROGRESSES = new HashMap<>(); private final Handler handler; + DispatchingProgressListener() { this.handler = new Handler(Looper.getMainLooper()); } @@ -90,11 +95,13 @@ public class OkHttpProgressGlideModule extends AppGlideModule { LISTENERS.remove(url); PROGRESSES.remove(url); } + static void expect(String url, UIProgressListener listener) { LISTENERS.put(url, listener); } - @Override public void update(HttpUrl url, final long bytesRead, final long contentLength) { + @Override + public void update(HttpUrl url, final long bytesRead, final long contentLength) { //System.out.printf("%s: %d/%d = %.2f%%%n", url, bytesRead, contentLength, (100f * bytesRead) / contentLength); //Log.d("resource progress", "url=" + url + " bytesRead="+ bytesRead + " of " + contentLength); @@ -117,7 +124,7 @@ public class OkHttpProgressGlideModule extends AppGlideModule { return true; } float percent = 100f * current / total; - long currentProgress = (long)(percent / granularity); + long currentProgress = (long) (percent / granularity); Long lastProgress = PROGRESSES.get(key); if (lastProgress == null || currentProgress != lastProgress) { PROGRESSES.put(key, currentProgress); @@ -142,15 +149,18 @@ public class OkHttpProgressGlideModule extends AppGlideModule { this.progressListener = progressListener; } - @Override public MediaType contentType() { + @Override + public MediaType contentType() { return responseBody.contentType(); } - @Override public long contentLength() { + @Override + public long contentLength() { return responseBody.contentLength(); } - @Override public BufferedSource source() { + @Override + public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } @@ -160,7 +170,9 @@ public class OkHttpProgressGlideModule extends AppGlideModule { private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; - @Override public long read(Buffer sink, long byteCount) throws IOException { + + @Override + public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); long fullLength = responseBody.contentLength(); if (bytesRead == -1) { // this source is exhausted diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java index 1e7c868a..2701e2e5 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java @@ -12,9 +12,11 @@ import com.bumptech.glide.request.transition.Transition; public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements OkHttpProgressGlideModule.UIProgressListener { private T model; private boolean ignoreProgress = true; + public ProgressTarget(Target<Z> target) { this(null, target); } + public ProgressTarget(T model, Target<Z> target) { super(target); this.model = model; @@ -23,14 +25,17 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements public final T getModel() { return model; } + public final void setModel(T model) { this.model = model; } + /** * Convert a model into an Url string that is used to match up the OkHttp requests. For explicit * {@link com.bumptech.glide.load.model.GlideUrl GlideUrl} loads this needs to return * {@link com.bumptech.glide.load.model.GlideUrl#toStringUrl toStringUrl}. For custom models do the same as your * {@link com.bumptech.glide.load.model.stream.BaseGlideUrlLoader BaseGlideUrlLoader} does. + * * @param model return the representation of the given model, DO NOT use {@link #getModel()} inside this method. * @return a stable Url representation of the model, otherwise the progress reporting won't work */ @@ -38,11 +43,13 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements return String.valueOf(model); } - @Override public float getGranualityPercentage() { + @Override + public float getGranualityPercentage() { return 1.0f; } - @Override public void onProgress(long bytesRead, long expectedLength) { + @Override + public void onProgress(long bytesRead, long expectedLength) { if (ignoreProgress) { return; } @@ -60,11 +67,13 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements * At this time it is not known if the Glide will even go and use the network to fetch the image. */ protected abstract void onConnecting(); + /** * Called when there's any progress on the download; not called when loading from cache. * At this time we know how many bytes have been transferred through the wire. */ protected abstract void onDownloading(long bytesRead, long expectedLength); + /** * Called when the bytes downloaded reach the length reported by the server; not called when loading from cache. * At this time it is fairly certain, that Glide either finished reading the stream. @@ -73,6 +82,7 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements * These cannot be listened to for progress so it's unsure how fast they'll be, best to show indeterminate progress. */ protected abstract void onDownloaded(); + /** * Called when the Glide load has finished either by successfully loading the image or failing to load or cancelled. * In any case the best is to hide/reset any progress displays. @@ -84,6 +94,7 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements ignoreProgress = false; onProgress(0, Long.MAX_VALUE); } + private void cleanup() { ignoreProgress = true; T model = this.model; // save in case it gets modified @@ -92,20 +103,28 @@ public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements this.model = null; } - @Override public void onLoadStarted(Drawable placeholder) { + @Override + public void onLoadStarted(Drawable placeholder) { super.onLoadStarted(placeholder); start(); } - /** @noinspection unchecked*/ + + /** + * @noinspection unchecked + */ public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) { cleanup(); - super.onResourceReady(resource, (Transition)transition); + super.onResourceReady(resource, (Transition) transition); } - @Override public void onLoadFailed(Drawable errorDrawable) { + + @Override + public void onLoadFailed(Drawable errorDrawable) { cleanup(); super.onLoadFailed(errorDrawable); } - @Override public void onLoadCleared(Drawable placeholder) { + + @Override + public void onLoadCleared(Drawable placeholder) { cleanup(); super.onLoadCleared(placeholder); } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java index 314800d9..71ece0b6 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java @@ -12,13 +12,17 @@ import com.bumptech.glide.request.transition.Transition; public class WrappingTarget<Z> implements Target<Z> { protected final @NonNull Target<? super Z> target; + public WrappingTarget(@NonNull Target<? super Z> target) { this.target = target; } + public @NonNull Target<? super Z> getWrappedTarget() { return target; } - @Override public void getSize(SizeReadyCallback cb) { + + @Override + public void getSize(SizeReadyCallback cb) { target.getSize(cb); } @@ -27,37 +31,51 @@ public class WrappingTarget<Z> implements Target<Z> { } - @Override public void onLoadStarted(Drawable placeholder) { + @Override + public void onLoadStarted(Drawable placeholder) { target.onLoadStarted(placeholder); } - @Override public void onLoadFailed(Drawable errorDrawable) { + + @Override + public void onLoadFailed(Drawable errorDrawable) { target.onLoadFailed(errorDrawable); } - /** @noinspection unchecked*/ + /** + * @noinspection unchecked + */ @Override public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) { - target.onResourceReady(resource, (Transition)transition); + target.onResourceReady(resource, (Transition) transition); } - @Override public void onLoadCleared(Drawable placeholder) { + @Override + public void onLoadCleared(Drawable placeholder) { target.onLoadCleared(placeholder); } - @Override public Request getRequest() { + @Override + public Request getRequest() { return target.getRequest(); } - @Override public void setRequest(Request request) { + + @Override + public void setRequest(Request request) { target.setRequest(request); } - @Override public void onStart() { + @Override + public void onStart() { target.onStart(); } - @Override public void onStop() { + + @Override + public void onStop() { target.onStop(); } - @Override public void onDestroy() { + + @Override + public void onDestroy() { target.onDestroy(); } }
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java index 7ece9e4f..ae7d93a4 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonActivity.java @@ -6,37 +6,37 @@ import android.view.Display; import android.widget.Toast; public class CommonActivity extends Activity { - private final String TAG = this.getClass().getSimpleName(); - - private boolean m_smallScreenMode = true; - - protected void setSmallScreen(boolean smallScreen) { - Log.d(TAG, "m_smallScreenMode=" + smallScreen); - m_smallScreenMode = smallScreen; - } - - public void toast(int msgId) { - Toast toast = Toast.makeText(CommonActivity.this, msgId, Toast.LENGTH_SHORT); - toast.show(); - } - - public void toast(String msg) { - Toast toast = Toast.makeText(CommonActivity.this, msg, Toast.LENGTH_SHORT); - toast.show(); - } - - public boolean isSmallScreen() { - return m_smallScreenMode; - } - - public boolean isPortrait() { - Display display = getWindowManager().getDefaultDisplay(); - - int width = display.getWidth(); - int height = display.getHeight(); - - return width < height; - } - + private final String TAG = this.getClass().getSimpleName(); + + private boolean m_smallScreenMode = true; + + protected void setSmallScreen(boolean smallScreen) { + Log.d(TAG, "m_smallScreenMode=" + smallScreen); + m_smallScreenMode = smallScreen; + } + + public void toast(int msgId) { + Toast toast = Toast.makeText(CommonActivity.this, msgId, Toast.LENGTH_SHORT); + toast.show(); + } + + public void toast(String msg) { + Toast toast = Toast.makeText(CommonActivity.this, msg, Toast.LENGTH_SHORT); + toast.show(); + } + + public boolean isSmallScreen() { + return m_smallScreenMode; + } + + public boolean isPortrait() { + Display display = getWindowManager().getDefaultDisplay(); + + int width = display.getWidth(); + int height = display.getHeight(); + + return width < height; + } + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonShareActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonShareActivity.java index 5796f289..6df7c226 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonShareActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/CommonShareActivity.java @@ -20,87 +20,87 @@ import org.fox.ttrss.util.SimpleLoginManager; public abstract class CommonShareActivity extends CommonActivity { - protected SharedPreferences m_prefs; - protected String m_sessionId; - protected int m_apiLevel = 0; - - private final String TAG = this.getClass().getSimpleName(); - - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - if (savedInstanceState != null) { - m_sessionId = savedInstanceState.getString("m_sessionId"); - m_apiLevel = savedInstanceState.getInt("m_apiLevel"); - } - - super.onCreate(savedInstanceState); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putString("m_sessionId", m_sessionId); - out.putInt("m_apiLevel", m_apiLevel); - } - - protected abstract void onLoggedIn(int requestId); - - protected abstract void onLoggingIn(int requestId); - - public void login(int requestId) { - - if (m_prefs.getString("ttrss_url", "").trim().isEmpty()) { - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(R.string.dialog_need_configure_prompt) - .setCancelable(false) - .setPositiveButton(R.string.dialog_open_preferences, (dialog, id) -> { - // launch preferences - - Intent intent = new Intent(CommonShareActivity.this, - PreferencesActivity.class); - startActivityForResult(intent, 0); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); - Dialog alert = builder.create(); - alert.show(); - - } else { - - SimpleLoginManager loginManager = new SimpleLoginManager() { - - @Override - protected void onLoginSuccess(int requestId, String sessionId, int apiLevel) { - m_sessionId = sessionId; - m_apiLevel = apiLevel; - - CommonShareActivity.this.onLoggedIn(requestId); - } - - @Override - protected void onLoginFailed(int requestId, ApiRequest ar) { - toast(ar.getErrorMessage()); - setProgressBarIndeterminateVisibility(false); - } - - @Override - protected void onLoggingIn(int requestId) { - CommonShareActivity.this.onLoggingIn(requestId); - } - }; - - String login = m_prefs.getString("login", "").trim(); - String password = m_prefs.getString("password", "").trim(); - - loginManager.logIn(this, requestId, login, password); - } - } - - public boolean onOptionsItemSelected(MenuItem item) { + protected SharedPreferences m_prefs; + protected String m_sessionId; + protected int m_apiLevel = 0; + + private final String TAG = this.getClass().getSimpleName(); + + @Override + public void onCreate(Bundle savedInstanceState) { + m_prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + + if (savedInstanceState != null) { + m_sessionId = savedInstanceState.getString("m_sessionId"); + m_apiLevel = savedInstanceState.getInt("m_apiLevel"); + } + + super.onCreate(savedInstanceState); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + out.putString("m_sessionId", m_sessionId); + out.putInt("m_apiLevel", m_apiLevel); + } + + protected abstract void onLoggedIn(int requestId); + + protected abstract void onLoggingIn(int requestId); + + public void login(int requestId) { + + if (m_prefs.getString("ttrss_url", "").trim().isEmpty()) { + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) + .setMessage(R.string.dialog_need_configure_prompt) + .setCancelable(false) + .setPositiveButton(R.string.dialog_open_preferences, (dialog, id) -> { + // launch preferences + + Intent intent = new Intent(CommonShareActivity.this, + PreferencesActivity.class); + startActivityForResult(intent, 0); + }) + .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); + Dialog alert = builder.create(); + alert.show(); + + } else { + + SimpleLoginManager loginManager = new SimpleLoginManager() { + + @Override + protected void onLoginSuccess(int requestId, String sessionId, int apiLevel) { + m_sessionId = sessionId; + m_apiLevel = apiLevel; + + CommonShareActivity.this.onLoggedIn(requestId); + } + + @Override + protected void onLoginFailed(int requestId, ApiRequest ar) { + toast(ar.getErrorMessage()); + setProgressBarIndeterminateVisibility(false); + } + + @Override + protected void onLoggingIn(int requestId) { + CommonShareActivity.this.onLoggingIn(requestId); + } + }; + + String login = m_prefs.getString("login", "").trim(); + String password = m_prefs.getString("password", "").trim(); + + loginManager.logIn(this, requestId, login, password); + } + } + + public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.preferences) { Intent intent = new Intent(CommonShareActivity.this, PreferencesActivity.class); @@ -112,13 +112,12 @@ public abstract class CommonShareActivity extends CommonActivity { return super.onOptionsItemSelected(item); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.activity_share, menu); - return true; - } - + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.activity_share, menu); + return true; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java index 66a77aef..f5b01947 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/ShareActivity.java @@ -15,120 +15,120 @@ import org.fox.ttrss.R; import java.util.HashMap; public class ShareActivity extends CommonShareActivity { - private final String TAG = this.getClass().getSimpleName(); - - private Button m_button; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - requestWindowFeature(Window.FEATURE_LEFT_ICON); - - Intent intent = getIntent(); - - String urlValue = intent.getStringExtra(Intent.EXTRA_TEXT); - String titleValue = intent.getStringExtra(Intent.EXTRA_SUBJECT); - String contentValue = ""; - - if (savedInstanceState != null) { - urlValue = savedInstanceState.getString("url"); - titleValue = savedInstanceState.getString("title"); - contentValue = savedInstanceState.getString("content"); - } - - setContentView(R.layout.activity_share); - - setSmallScreen(false); - - EditText url = (EditText) findViewById(R.id.url); - url.setText(urlValue); - - EditText title = (EditText) findViewById(R.id.title); - title.setText(titleValue); - - EditText content = (EditText) findViewById(R.id.content); - content.setText(contentValue); - - m_button = (Button) findViewById(R.id.share_button); - - m_button.setOnClickListener(v -> login(0)); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - EditText url = (EditText) findViewById(R.id.url); - - if (url != null) { - out.putString("url", url.getText().toString()); - } - - EditText title = (EditText) findViewById(R.id.title); - - if (title != null) { - out.putString("title", title.getText().toString()); - } - - EditText content = (EditText) findViewById(R.id.content); - - if (content != null) { - out.putString("content", content.getText().toString()); - } - - } - - private void postData() { - m_button.setEnabled(false); - - ApiRequest req = new ApiRequest(getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - setProgressBarIndeterminateVisibility(false); - - if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) { - toast(getErrorMessage()); - } else { - toast(R.string.share_article_posted); - finish(); - } - - m_button.setEnabled(true); - } - }; - - final EditText url = (EditText) findViewById(R.id.url); - final EditText title = (EditText) findViewById(R.id.title); - final EditText content = (EditText) findViewById(R.id.content); - - if (url != null && title != null && content != null) { - HashMap<String, String> map = new HashMap<>(); - map.put("sid", m_sessionId); - map.put("op", "shareToPublished"); - map.put("title", title.getText().toString()); - map.put("url", url.getText().toString()); - map.put("content", content.getText().toString()); - - setProgressBarIndeterminateVisibility(true); - - req.execute(map); - } - } - - - @Override - public void onLoggingIn(int requestId) { - m_button.setEnabled(false); - } - - @Override - protected void onLoggedIn(int requestId) { - m_button.setEnabled(true); - - if (m_apiLevel < 4) { - toast(R.string.api_too_low); - } else { - postData(); - } - } + private final String TAG = this.getClass().getSimpleName(); + + private Button m_button; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_LEFT_ICON); + + Intent intent = getIntent(); + + String urlValue = intent.getStringExtra(Intent.EXTRA_TEXT); + String titleValue = intent.getStringExtra(Intent.EXTRA_SUBJECT); + String contentValue = ""; + + if (savedInstanceState != null) { + urlValue = savedInstanceState.getString("url"); + titleValue = savedInstanceState.getString("title"); + contentValue = savedInstanceState.getString("content"); + } + + setContentView(R.layout.activity_share); + + setSmallScreen(false); + + EditText url = (EditText) findViewById(R.id.url); + url.setText(urlValue); + + EditText title = (EditText) findViewById(R.id.title); + title.setText(titleValue); + + EditText content = (EditText) findViewById(R.id.content); + content.setText(contentValue); + + m_button = (Button) findViewById(R.id.share_button); + + m_button.setOnClickListener(v -> login(0)); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + EditText url = (EditText) findViewById(R.id.url); + + if (url != null) { + out.putString("url", url.getText().toString()); + } + + EditText title = (EditText) findViewById(R.id.title); + + if (title != null) { + out.putString("title", title.getText().toString()); + } + + EditText content = (EditText) findViewById(R.id.content); + + if (content != null) { + out.putString("content", content.getText().toString()); + } + + } + + private void postData() { + m_button.setEnabled(false); + + ApiRequest req = new ApiRequest(getApplicationContext()) { + protected void onPostExecute(JsonElement result) { + setProgressBarIndeterminateVisibility(false); + + if (m_lastError != ApiCommon.ApiError.UNKNOWN_ERROR) { + toast(getErrorMessage()); + } else { + toast(R.string.share_article_posted); + finish(); + } + + m_button.setEnabled(true); + } + }; + + final EditText url = (EditText) findViewById(R.id.url); + final EditText title = (EditText) findViewById(R.id.title); + final EditText content = (EditText) findViewById(R.id.content); + + if (url != null && title != null && content != null) { + HashMap<String, String> map = new HashMap<>(); + map.put("sid", m_sessionId); + map.put("op", "shareToPublished"); + map.put("title", title.getText().toString()); + map.put("url", url.getText().toString()); + map.put("content", content.getText().toString()); + + setProgressBarIndeterminateVisibility(true); + + req.execute(map); + } + } + + + @Override + public void onLoggingIn(int requestId) { + m_button.setEnabled(false); + } + + @Override + protected void onLoggedIn(int requestId) { + m_button.setEnabled(true); + + if (m_apiLevel < 4) { + toast(R.string.api_too_low); + } else { + postData(); + } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java index b894f213..fdc5915d 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/share/SubscribeActivity.java @@ -33,329 +33,330 @@ import java.util.Map; import java.util.stream.Collectors; public class SubscribeActivity extends CommonShareActivity { - private final String TAG = this.getClass().getSimpleName(); - - private Button m_postButton; - private Button m_catButton; - private CatListAdapter m_catAdapter; - private FeedListAdapter m_feedAdapter; - private final List<Feed> m_cats = new ArrayList<>(); - private final ArrayList<Map.Entry<String, JsonElement>> m_feeds = new ArrayList<>(); - private ProgressBar m_progressBar; - - private static final int REQ_CATS = 1; - private static final int REQ_POST = 2; - - static class TitleComparator implements Comparator<Feed> { - - @Override - public int compare(Feed a, Feed b) { - if (a.id >= 0 && b.id >= 0) - return a.title.compareTo(b.title); - else - return a.id - b.id; - } - - } - - public void sortCats() { - - if (m_catAdapter != null) { - m_cats.sort(new TitleComparator()); - m_catAdapter.notifyDataSetChanged(); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - requestWindowFeature(Window.FEATURE_LEFT_ICON); - - String urlValue = getIntent().getDataString(); - - if (urlValue == null) - urlValue = getIntent().getStringExtra(Intent.EXTRA_TEXT); - - if (savedInstanceState != null) { - urlValue = savedInstanceState.getString("url"); - } - - setContentView(R.layout.activity_subscribe); - - setSmallScreen(false); - - m_progressBar = findViewById(R.id.subscribe_progress); - Spinner catList = findViewById(R.id.category_spinner); - - if (m_cats.isEmpty()) m_cats.add(new Feed(0, "Uncategorized", true)); - - m_catAdapter = new CatListAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_cats); - catList.setAdapter(m_catAdapter); - - final Spinner feedList = findViewById(R.id.feed_spinner); - feedList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - String feed = m_feedAdapter.getItemURL(position); - EditText feedUrl = findViewById(R.id.feed_url); - - if (feed != null && feedUrl != null) { - feedUrl.setText(feed); - } - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - - } - }); - - m_feedAdapter = new FeedListAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_feeds); - - feedList.setAdapter(m_feedAdapter); - - EditText feedUrl = (EditText) findViewById(R.id.feed_url); - feedUrl.setText(urlValue); - - m_postButton = (Button) findViewById(R.id.subscribe_button); - - m_postButton.setOnClickListener(v -> login(REQ_POST)); - - m_catButton = (Button) findViewById(R.id.cats_button); - - m_catButton.setOnClickListener(v -> login(REQ_CATS)); - - login(REQ_CATS); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - EditText url = findViewById(R.id.url); - - if (url != null) { - out.putString("url", url.getText().toString()); - } - - } - - private void subscribeToFeed() { - m_postButton.setEnabled(false); - - @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - m_progressBar.setVisibility(View.INVISIBLE); - - if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) { - toast(getErrorMessage()); - } else { - try { - int rc = -1; - - try { - rc = result.getAsJsonObject().get("status").getAsJsonObject().get("code").getAsInt(); - } catch (Exception e) { - toast(e.getMessage()); - } - - switch (rc) { - case -1: - toast(R.string.error_api_unknown); - //finish(); - break; - case 0: - toast(R.string.error_feed_already_exists_); - //finish(); - break; - case 1: - toast(R.string.subscribed_to_feed); - finish(); - break; - case 2: - toast(R.string.error_invalid_url); - break; - case 3: - toast(R.string.error_url_is_an_html_page_no_feeds_found); - break; - case 4: - //toast(R.string.error_url_contains_multiple_feeds); - - JsonObject feeds = result.getAsJsonObject().get("status").getAsJsonObject().get("feeds").getAsJsonObject(); - - if (feeds != null) { - m_feeds.clear(); - m_feeds.addAll(feeds.entrySet()); - - m_feedAdapter.notifyDataSetChanged(); - - findViewById(R.id.feed_spinner).setVisibility(View.VISIBLE); - - } else { - toast(R.string.error_while_subscribing); - } - - break; - case 5: - toast(R.string.error_could_not_download_url); - break; - } - - } catch (Exception e) { - toast(e.getMessage()); - } - } - - m_postButton.setEnabled(true); - } - }; - - Spinner catSpinner = findViewById(R.id.category_spinner); - - final Feed cat = m_catAdapter.getCategory(catSpinner.getSelectedItemPosition()); - final EditText feedUrl = findViewById(R.id.feed_url); - - if (feedUrl != null ) { - HashMap<String, String> map = new HashMap<>(); - map.put("sid", m_sessionId); - map.put("op", "subscribeToFeed"); - map.put("feed_url", feedUrl.getText().toString()); - - if (cat != null) { - map.put("category_id", String.valueOf(cat.id)); - } - - m_progressBar.setVisibility(View.VISIBLE); - - req.execute(map); - } - } - - @Override - public void onLoggingIn(int requestId) { - switch (requestId) { - case REQ_CATS: - m_catButton.setEnabled(false); - break; - case REQ_POST: - m_postButton.setEnabled(false); - break; - } - } - - private void updateCats() { - @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) { - protected void onPostExecute(JsonElement result) { - m_progressBar.setVisibility(View.INVISIBLE); - - if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) { - toast(getErrorMessage()); - } else { - JsonArray content = result.getAsJsonArray(); - - if (content != null) { - Type listType = new TypeToken<List<Feed>>() {}.getType(); - final List<Feed> catsJson = new Gson().fromJson(content, listType); - - m_cats.clear(); - m_cats.addAll(catsJson.stream().filter(f -> f.id > 0).collect(Collectors.toList())); - - sortCats(); - - m_cats.add(0, new Feed(0, "Uncategorized", true)); - - m_catAdapter.notifyDataSetChanged(); - - toast(R.string.category_list_updated); - } - } - - m_catButton.setEnabled(true); - } - }; - - HashMap<String, String> map = new HashMap<>(); - map.put("sid", m_sessionId); - map.put("op", "getCategories"); - - m_progressBar.setVisibility(View.VISIBLE); - - req.execute(map); - } - - @Override - protected void onLoggedIn(int requestId) { - switch (requestId) { - case REQ_CATS: - updateCats(); - break; - case REQ_POST: - m_postButton.setEnabled(true); - if (m_apiLevel < 5) { - toast(R.string.api_too_low); - } else { - subscribeToFeed(); - } - break; - } - } - - private static class CatListAdapter extends ArrayAdapter<String> { - private final List<Feed> m_items; - - public CatListAdapter(Context context, int resource, - List<Feed> items) { - super(context, resource); - - m_items = items; - } - - @Override - public String getItem(int item) { - return m_items.get(item).title; - } - - public Feed getCategory(int item) { - try { - return m_items.get(item); - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - - @Override - public int getCount() { - return m_items.size(); - } - } - - private static class FeedListAdapter extends ArrayAdapter<String> { - private final List<Map.Entry<String, JsonElement>> m_items; - - public FeedListAdapter(Context context, int resource, List<Map.Entry<String, JsonElement>> items) { - super(context, resource); - - m_items = items; - } - - @Override - public String getItem(int item) { - return m_items.get(item).getValue().getAsString(); - } - - public String getItemURL(int item) { - try { - return m_items.get(item).getKey(); - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - - @Override - public int getCount() { - return m_items.size(); - } - } + private final String TAG = this.getClass().getSimpleName(); + + private Button m_postButton; + private Button m_catButton; + private CatListAdapter m_catAdapter; + private FeedListAdapter m_feedAdapter; + private final List<Feed> m_cats = new ArrayList<>(); + private final ArrayList<Map.Entry<String, JsonElement>> m_feeds = new ArrayList<>(); + private ProgressBar m_progressBar; + + private static final int REQ_CATS = 1; + private static final int REQ_POST = 2; + + static class TitleComparator implements Comparator<Feed> { + + @Override + public int compare(Feed a, Feed b) { + if (a.id >= 0 && b.id >= 0) + return a.title.compareTo(b.title); + else + return a.id - b.id; + } + + } + + public void sortCats() { + + if (m_catAdapter != null) { + m_cats.sort(new TitleComparator()); + m_catAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_LEFT_ICON); + + String urlValue = getIntent().getDataString(); + + if (urlValue == null) + urlValue = getIntent().getStringExtra(Intent.EXTRA_TEXT); + + if (savedInstanceState != null) { + urlValue = savedInstanceState.getString("url"); + } + + setContentView(R.layout.activity_subscribe); + + setSmallScreen(false); + + m_progressBar = findViewById(R.id.subscribe_progress); + Spinner catList = findViewById(R.id.category_spinner); + + if (m_cats.isEmpty()) m_cats.add(new Feed(0, "Uncategorized", true)); + + m_catAdapter = new CatListAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_cats); + catList.setAdapter(m_catAdapter); + + final Spinner feedList = findViewById(R.id.feed_spinner); + feedList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + String feed = m_feedAdapter.getItemURL(position); + EditText feedUrl = findViewById(R.id.feed_url); + + if (feed != null && feedUrl != null) { + feedUrl.setText(feed); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + + } + }); + + m_feedAdapter = new FeedListAdapter(this, android.R.layout.simple_spinner_dropdown_item, m_feeds); + + feedList.setAdapter(m_feedAdapter); + + EditText feedUrl = (EditText) findViewById(R.id.feed_url); + feedUrl.setText(urlValue); + + m_postButton = (Button) findViewById(R.id.subscribe_button); + + m_postButton.setOnClickListener(v -> login(REQ_POST)); + + m_catButton = (Button) findViewById(R.id.cats_button); + + m_catButton.setOnClickListener(v -> login(REQ_CATS)); + + login(REQ_CATS); + } + + @Override + public void onSaveInstanceState(Bundle out) { + super.onSaveInstanceState(out); + + EditText url = findViewById(R.id.url); + + if (url != null) { + out.putString("url", url.getText().toString()); + } + + } + + private void subscribeToFeed() { + m_postButton.setEnabled(false); + + @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) { + protected void onPostExecute(JsonElement result) { + m_progressBar.setVisibility(View.INVISIBLE); + + if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) { + toast(getErrorMessage()); + } else { + try { + int rc = -1; + + try { + rc = result.getAsJsonObject().get("status").getAsJsonObject().get("code").getAsInt(); + } catch (Exception e) { + toast(e.getMessage()); + } + + switch (rc) { + case -1: + toast(R.string.error_api_unknown); + //finish(); + break; + case 0: + toast(R.string.error_feed_already_exists_); + //finish(); + break; + case 1: + toast(R.string.subscribed_to_feed); + finish(); + break; + case 2: + toast(R.string.error_invalid_url); + break; + case 3: + toast(R.string.error_url_is_an_html_page_no_feeds_found); + break; + case 4: + //toast(R.string.error_url_contains_multiple_feeds); + + JsonObject feeds = result.getAsJsonObject().get("status").getAsJsonObject().get("feeds").getAsJsonObject(); + + if (feeds != null) { + m_feeds.clear(); + m_feeds.addAll(feeds.entrySet()); + + m_feedAdapter.notifyDataSetChanged(); + + findViewById(R.id.feed_spinner).setVisibility(View.VISIBLE); + + } else { + toast(R.string.error_while_subscribing); + } + + break; + case 5: + toast(R.string.error_could_not_download_url); + break; + } + + } catch (Exception e) { + toast(e.getMessage()); + } + } + + m_postButton.setEnabled(true); + } + }; + + Spinner catSpinner = findViewById(R.id.category_spinner); + + final Feed cat = m_catAdapter.getCategory(catSpinner.getSelectedItemPosition()); + final EditText feedUrl = findViewById(R.id.feed_url); + + if (feedUrl != null) { + HashMap<String, String> map = new HashMap<>(); + map.put("sid", m_sessionId); + map.put("op", "subscribeToFeed"); + map.put("feed_url", feedUrl.getText().toString()); + + if (cat != null) { + map.put("category_id", String.valueOf(cat.id)); + } + + m_progressBar.setVisibility(View.VISIBLE); + + req.execute(map); + } + } + + @Override + public void onLoggingIn(int requestId) { + switch (requestId) { + case REQ_CATS: + m_catButton.setEnabled(false); + break; + case REQ_POST: + m_postButton.setEnabled(false); + break; + } + } + + private void updateCats() { + @SuppressLint("StaticFieldLeak") ApiRequest req = new ApiRequest(getApplicationContext()) { + protected void onPostExecute(JsonElement result) { + m_progressBar.setVisibility(View.INVISIBLE); + + if (m_lastError != null && m_lastError != ApiCommon.ApiError.SUCCESS) { + toast(getErrorMessage()); + } else { + JsonArray content = result.getAsJsonArray(); + + if (content != null) { + Type listType = new TypeToken<List<Feed>>() { + }.getType(); + final List<Feed> catsJson = new Gson().fromJson(content, listType); + + m_cats.clear(); + m_cats.addAll(catsJson.stream().filter(f -> f.id > 0).collect(Collectors.toList())); + + sortCats(); + + m_cats.add(0, new Feed(0, "Uncategorized", true)); + + m_catAdapter.notifyDataSetChanged(); + + toast(R.string.category_list_updated); + } + } + + m_catButton.setEnabled(true); + } + }; + + HashMap<String, String> map = new HashMap<>(); + map.put("sid", m_sessionId); + map.put("op", "getCategories"); + + m_progressBar.setVisibility(View.VISIBLE); + + req.execute(map); + } + + @Override + protected void onLoggedIn(int requestId) { + switch (requestId) { + case REQ_CATS: + updateCats(); + break; + case REQ_POST: + m_postButton.setEnabled(true); + if (m_apiLevel < 5) { + toast(R.string.api_too_low); + } else { + subscribeToFeed(); + } + break; + } + } + + private static class CatListAdapter extends ArrayAdapter<String> { + private final List<Feed> m_items; + + public CatListAdapter(Context context, int resource, + List<Feed> items) { + super(context, resource); + + m_items = items; + } + + @Override + public String getItem(int item) { + return m_items.get(item).title; + } + + public Feed getCategory(int item) { + try { + return m_items.get(item); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + @Override + public int getCount() { + return m_items.size(); + } + } + + private static class FeedListAdapter extends ArrayAdapter<String> { + private final List<Map.Entry<String, JsonElement>> m_items; + + public FeedListAdapter(Context context, int resource, List<Map.Entry<String, JsonElement>> items) { + super(context, resource); + + m_items = items; + } + + @Override + public String getItem(int item) { + return m_items.get(item).getValue().getAsString(); + } + + public String getItemURL(int item) { + try { + return m_items.get(item).getKey(); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + + @Override + public int getCount() { + return m_items.size(); + } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java index 8029d77d..2c1966ad 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Article.java @@ -17,358 +17,362 @@ import java.util.regex.Pattern; // TODO: serialize Labels public class Article implements Parcelable { - public static final int TYPE_AMR_FOOTER = -2; - - public static final int FLAVOR_KIND_ALBUM = 1; - public static final int FLAVOR_KIND_VIDEO = 2; - public static final int FLAVOR_KIND_YOUTUBE = 3; - - public static final int SCORE_LOW = -500; - public static final int SCORE_HIGH = 500; - - public static final int UPDATE_FIELD_MARKED = 0; - public static final int UPDATE_FIELD_PUBLISHED = 1; - public static final int UPDATE_FIELD_UNREAD = 2; - public static final int UPDATE_FIELD_NOTE = 3; - public static final int UPDATE_FIELD_SCORE = 4; - - public static final int UPDATE_SET_FALSE = 0; - public static final int UPDATE_SET_TRUE = 1; - public static final int UPDATE_TOGGLE = 2; - - public int id; - public boolean unread; - public boolean marked; - public boolean published; - public int score; - public int updated; - public boolean is_updated; - public String title; - public String link; - public int feed_id; - public List<String> tags; - public List<Attachment> attachments; - public String content; + public static final int TYPE_AMR_FOOTER = -2; + + public static final int FLAVOR_KIND_ALBUM = 1; + public static final int FLAVOR_KIND_VIDEO = 2; + public static final int FLAVOR_KIND_YOUTUBE = 3; + + public static final int SCORE_LOW = -500; + public static final int SCORE_HIGH = 500; + + public static final int UPDATE_FIELD_MARKED = 0; + public static final int UPDATE_FIELD_PUBLISHED = 1; + public static final int UPDATE_FIELD_UNREAD = 2; + public static final int UPDATE_FIELD_NOTE = 3; + public static final int UPDATE_FIELD_SCORE = 4; + + public static final int UPDATE_SET_FALSE = 0; + public static final int UPDATE_SET_TRUE = 1; + public static final int UPDATE_TOGGLE = 2; + + public int id; + public boolean unread; + public boolean marked; + public boolean published; + public int score; + public int updated; + public boolean is_updated; + public String title; + public String link; + public int feed_id; + public List<String> tags; + public List<Attachment> attachments; + public String content; public String excerpt; - public List<List<String>> labels; - public String feed_title; - public int comments_count; - public String comments_link; - public boolean always_display_attachments; - public String author; - public String note; + public List<List<String>> labels; + public String feed_title; + public int comments_count; + public String comments_link; + public boolean always_display_attachments; + public String author; + public String note; public boolean selected; - public boolean active; + public boolean active; public String flavor_image; public String flavor_stream; public int flavor_kind; - public String site_url; + public String site_url; - /* not serialized */ - transient public Document articleDoc; - transient public Element flavorImage; + /* not serialized */ + transient public Document articleDoc; + transient public Element flavorImage; - transient public String flavorImageUri; - transient public String flavorStreamUri; - transient public String youtubeVid; - transient public List<Element> mediaList = new ArrayList<>(); + transient public String flavorImageUri; + transient public String flavorStreamUri; + transient public String youtubeVid; + transient public List<Element> mediaList = new ArrayList<>(); public Article(Parcel in) { - readFromParcel(in); - } - - public Article() { - - } - - public void cleanupExcerpt() { - if (excerpt != null) { - excerpt = excerpt.replace("…", "…"); - excerpt = excerpt.replace("]]>", ""); - excerpt = Jsoup.parse(excerpt).text(); - } - } - - public void collectMediaInfo() { - if (flavor_image != null && !flavor_image.isEmpty()) { - flavorImageUri = flavor_image; - - flavorImage = new Element("img") - .attr("src", flavorImageUri); - - if (flavor_stream != null && !flavor_stream.isEmpty()) { - flavorStreamUri = flavor_stream; - } - - return; - } - - articleDoc = Jsoup.parse(content); - - if (articleDoc != null) { - mediaList = articleDoc.select("img,video,iframe[src*=youtube.com/embed/]"); - - for (Element e : mediaList) { - if ("iframe".equals(e.tagName().toLowerCase())) { - flavorImage = e; - break; - } /*else if ("video".equals(e.tagName().toLowerCase())) { + readFromParcel(in); + } + + public Article() { + + } + + public void cleanupExcerpt() { + if (excerpt != null) { + excerpt = excerpt.replace("…", "…"); + excerpt = excerpt.replace("]]>", ""); + excerpt = Jsoup.parse(excerpt).text(); + } + } + + public void collectMediaInfo() { + if (flavor_image != null && !flavor_image.isEmpty()) { + flavorImageUri = flavor_image; + + flavorImage = new Element("img") + .attr("src", flavorImageUri); + + if (flavor_stream != null && !flavor_stream.isEmpty()) { + flavorStreamUri = flavor_stream; + } + + return; + } + + articleDoc = Jsoup.parse(content); + + if (articleDoc != null) { + mediaList = articleDoc.select("img,video,iframe[src*=youtube.com/embed/]"); + + for (Element e : mediaList) { + if ("iframe".equals(e.tagName().toLowerCase())) { + flavorImage = e; + break; + } /*else if ("video".equals(e.tagName().toLowerCase())) { flavorImage = e; break; }*/ - } + } - if (flavorImage == null) { - for (Element e : mediaList) { - flavorImage = e; - break; - } - } - - if (flavorImage != null) { - try { - - if ("video".equals(flavorImage.tagName().toLowerCase())) { - Element source = flavorImage.select("source").first(); - - if (source != null) { - flavorStreamUri = source.attr("src"); - flavorImageUri = flavorImage.attr("poster"); - } - } else if ("iframe".equals(flavorImage.tagName().toLowerCase())) { - - String srcEmbed = flavorImage.attr("src"); - - if (!srcEmbed.isEmpty()) { - Pattern pattern = Pattern.compile("/embed/([\\w-]+)"); - Matcher matcher = pattern.matcher(srcEmbed); - - if (matcher.find()) { - youtubeVid = matcher.group(1); - - flavorImageUri = "https://img.youtube.com/vi/" + youtubeVid + "/hqdefault.jpg"; - flavorStreamUri = "https://youtu.be/" + youtubeVid; - } - } - } else { - flavorImageUri = flavorImage.attr("src"); - - if (!flavorImageUri.isEmpty() && flavorImageUri.startsWith("//")) { - flavorImageUri = "https:" + flavorImageUri; - } - - flavorStreamUri = null; - } - } catch (Exception e) { - e.printStackTrace(); - - flavorImage = null; - flavorImageUri = null; - flavorStreamUri = null; - } - } - } - - if (flavorImageUri == null || flavorImageUri.isEmpty()) { - // consider attachments - if (attachments != null) { - for (Attachment a : attachments) { - if (a.content_type != null && a.content_type.contains("image/")) { - flavorImageUri = a.content_url; - - if (flavorImageUri != null && flavorImageUri.startsWith("//")) { - flavorImageUri = "https:" + flavorImageUri; - } - - // this is needed for the gallery view - flavorImage = new Element("img") - .attr("src", flavorImageUri); - - break; - } - } - } - } - - //Log.d("Article", "collectMediaInfo: " + flavorImage); - } - - public Article(int id) { - this.id = id; - this.title = "ID:" + id; - fixNullFields(); - } - - public Article(Article clone) { - id = clone.id; - unread = clone.unread; - marked = clone.marked; - published = clone.published; - score = clone.score; - updated = clone.updated; - is_updated = clone.is_updated; - title = clone.title; - link = clone.link; - feed_id = clone.feed_id; - tags = clone.tags; - attachments = clone.attachments; - content = clone.content; - excerpt = clone.excerpt; - labels = clone.labels; - feed_title = clone.feed_title; - comments_count = clone.comments_count; - comments_link = clone.comments_link; - always_display_attachments = clone.always_display_attachments; - author = clone.author; - note = clone.note; - selected = clone.selected; - flavor_image = clone.flavor_image; - flavor_stream = clone.flavor_stream; - flavor_kind = clone.flavor_kind; - site_url = clone.site_url; - active = clone.active; - - articleDoc = clone.articleDoc; - flavorImage = clone.flavorImage; - - flavorImageUri = clone.flavorImageUri; - flavorStreamUri = clone.flavorStreamUri; - youtubeVid = clone.youtubeVid; - mediaList = new ArrayList<>(clone.mediaList); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(id); - out.writeInt(unread ? 1 : 0); - out.writeInt(marked ? 1 : 0); - out.writeInt(published ? 1 : 0); - out.writeInt(score); - out.writeInt(updated); - out.writeInt(is_updated ? 1 : 0); - out.writeString(title); - out.writeString(link); - out.writeInt(feed_id); - out.writeStringList(tags); - out.writeString(content); + if (flavorImage == null) { + for (Element e : mediaList) { + flavorImage = e; + break; + } + } + + if (flavorImage != null) { + try { + + if ("video".equals(flavorImage.tagName().toLowerCase())) { + Element source = flavorImage.select("source").first(); + + if (source != null) { + flavorStreamUri = source.attr("src"); + flavorImageUri = flavorImage.attr("poster"); + } + } else if ("iframe".equals(flavorImage.tagName().toLowerCase())) { + + String srcEmbed = flavorImage.attr("src"); + + if (!srcEmbed.isEmpty()) { + Pattern pattern = Pattern.compile("/embed/([\\w-]+)"); + Matcher matcher = pattern.matcher(srcEmbed); + + if (matcher.find()) { + youtubeVid = matcher.group(1); + + flavorImageUri = "https://img.youtube.com/vi/" + youtubeVid + "/hqdefault.jpg"; + flavorStreamUri = "https://youtu.be/" + youtubeVid; + } + } + } else { + flavorImageUri = flavorImage.attr("src"); + + if (!flavorImageUri.isEmpty() && flavorImageUri.startsWith("//")) { + flavorImageUri = "https:" + flavorImageUri; + } + + flavorStreamUri = null; + } + } catch (Exception e) { + e.printStackTrace(); + + flavorImage = null; + flavorImageUri = null; + flavorStreamUri = null; + } + } + } + + if (flavorImageUri == null || flavorImageUri.isEmpty()) { + // consider attachments + if (attachments != null) { + for (Attachment a : attachments) { + if (a.content_type != null && a.content_type.contains("image/")) { + flavorImageUri = a.content_url; + + if (flavorImageUri != null && flavorImageUri.startsWith("//")) { + flavorImageUri = "https:" + flavorImageUri; + } + + // this is needed for the gallery view + flavorImage = new Element("img") + .attr("src", flavorImageUri); + + break; + } + } + } + } + + //Log.d("Article", "collectMediaInfo: " + flavorImage); + } + + public Article(int id) { + this.id = id; + this.title = "ID:" + id; + fixNullFields(); + } + + public Article(Article clone) { + id = clone.id; + unread = clone.unread; + marked = clone.marked; + published = clone.published; + score = clone.score; + updated = clone.updated; + is_updated = clone.is_updated; + title = clone.title; + link = clone.link; + feed_id = clone.feed_id; + tags = clone.tags; + attachments = clone.attachments; + content = clone.content; + excerpt = clone.excerpt; + labels = clone.labels; + feed_title = clone.feed_title; + comments_count = clone.comments_count; + comments_link = clone.comments_link; + always_display_attachments = clone.always_display_attachments; + author = clone.author; + note = clone.note; + selected = clone.selected; + flavor_image = clone.flavor_image; + flavor_stream = clone.flavor_stream; + flavor_kind = clone.flavor_kind; + site_url = clone.site_url; + active = clone.active; + + articleDoc = clone.articleDoc; + flavorImage = clone.flavorImage; + + flavorImageUri = clone.flavorImageUri; + flavorStreamUri = clone.flavorStreamUri; + youtubeVid = clone.youtubeVid; + mediaList = new ArrayList<>(clone.mediaList); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(id); + out.writeInt(unread ? 1 : 0); + out.writeInt(marked ? 1 : 0); + out.writeInt(published ? 1 : 0); + out.writeInt(score); + out.writeInt(updated); + out.writeInt(is_updated ? 1 : 0); + out.writeString(title); + out.writeString(link); + out.writeInt(feed_id); + out.writeStringList(tags); + out.writeString(content); out.writeString(excerpt); - out.writeList(attachments); - out.writeString(feed_title); - out.writeInt(comments_count); - out.writeString(comments_link); - out.writeInt(always_display_attachments ? 1 : 0); - out.writeString(author); - out.writeString(note); + out.writeList(attachments); + out.writeString(feed_title); + out.writeInt(comments_count); + out.writeString(comments_link); + out.writeInt(always_display_attachments ? 1 : 0); + out.writeString(author); + out.writeString(note); out.writeInt(selected ? 1 : 0); - out.writeString(site_url); - } - - public void readFromParcel(Parcel in) { - id = in.readInt(); - unread = in.readInt() == 1; - marked = in.readInt() == 1; - published = in.readInt() == 1; - score = in.readInt(); - updated = in.readInt(); - is_updated = in.readInt() == 1; - title = in.readString(); - link = in.readString(); - feed_id = in.readInt(); - - if (tags == null) tags = new ArrayList<>(); - in.readStringList(tags); - - content = in.readString(); + out.writeString(site_url); + } + + public void readFromParcel(Parcel in) { + id = in.readInt(); + unread = in.readInt() == 1; + marked = in.readInt() == 1; + published = in.readInt() == 1; + score = in.readInt(); + updated = in.readInt(); + is_updated = in.readInt() == 1; + title = in.readString(); + link = in.readString(); + feed_id = in.readInt(); + + if (tags == null) tags = new ArrayList<>(); + in.readStringList(tags); + + content = in.readString(); excerpt = in.readString(); - - attachments = new ArrayList<>(); - in.readList(attachments, Attachment.class.getClassLoader()); - - feed_title = in.readString(); - - comments_count = in.readInt(); - comments_link = in.readString(); - always_display_attachments = in.readInt() == 1; - author = in.readString(); - note = in.readString(); + + attachments = new ArrayList<>(); + in.readList(attachments, Attachment.class.getClassLoader()); + + feed_title = in.readString(); + + comments_count = in.readInt(); + comments_link = in.readString(); + always_display_attachments = in.readInt() == 1; + author = in.readString(); + note = in.readString(); selected = in.readInt() == 1; - site_url = in.readString(); - } - - @SuppressWarnings("rawtypes") - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public Article createFromParcel(Parcel in) { - return new Article(in); - } - - public Article[] newArray(int size) { - return new Article[size]; - } - }; - - /** set fields which might be missing during JSON deserialization to sane values */ - public void fixNullFields() { - if (note == null) note = ""; - if (link == null) link = ""; - if (tags == null) tags = new ArrayList<>(); - if (excerpt == null) excerpt = ""; - if (content == null) content = ""; - if (comments_link == null) comments_link = ""; - } - - /** compares by id only, we need this to skip manual lookup by id */ - @Override - public boolean equals(Object other) { - if (other == null) - return false; - - if (other == this) - return true; - - if (this.getClass() != other.getClass()) - return false; - - Article article = (Article) other; - - return article.id == this.id; - } - - @NonNull - @Override - public String toString() { - return "{id:" + this.id + ",unread:" + this.unread + - ",marked:" + this.marked + ",published:" + this.published + ",score:" + this.score + - ",selected:" + this.selected + "}"; - } - - public boolean isHostDistinct() { - try { - String siteDomain = new URL(site_url).getHost().replace("www.", ""); - String linkDomain = new URL(link).getHost().replace("www.", ""); - - return !linkDomain.contains(siteDomain); - - } catch (MalformedURLException e) { - // - } - - return false; - } - - @NonNull - public String getLinkHost() { - try { - return new URL(link).getHost(); - } catch (MalformedURLException e) { - // - } - - return ""; - } + site_url = in.readString(); + } + + @SuppressWarnings("rawtypes") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public Article createFromParcel(Parcel in) { + return new Article(in); + } + + public Article[] newArray(int size) { + return new Article[size]; + } + }; + + /** + * set fields which might be missing during JSON deserialization to sane values + */ + public void fixNullFields() { + if (note == null) note = ""; + if (link == null) link = ""; + if (tags == null) tags = new ArrayList<>(); + if (excerpt == null) excerpt = ""; + if (content == null) content = ""; + if (comments_link == null) comments_link = ""; + } + + /** + * compares by id only, we need this to skip manual lookup by id + */ + @Override + public boolean equals(Object other) { + if (other == null) + return false; + + if (other == this) + return true; + + if (this.getClass() != other.getClass()) + return false; + + Article article = (Article) other; + + return article.id == this.id; + } + + @NonNull + @Override + public String toString() { + return "{id:" + this.id + ",unread:" + this.unread + + ",marked:" + this.marked + ",published:" + this.published + ",score:" + this.score + + ",selected:" + this.selected + "}"; + } + + public boolean isHostDistinct() { + try { + String siteDomain = new URL(site_url).getHost().replace("www.", ""); + String linkDomain = new URL(link).getHost().replace("www.", ""); + + return !linkDomain.contains(siteDomain); + + } catch (MalformedURLException e) { + // + } + + return false; + } + + @NonNull + public String getLinkHost() { + try { + return new URL(link).getHost(); + } catch (MalformedURLException e) { + // + } + + return ""; + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Attachment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Attachment.java index 70212800..6d934689 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Attachment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Attachment.java @@ -8,68 +8,68 @@ import java.net.MalformedURLException; import java.net.URL; public class Attachment implements Parcelable { - public int id; - public String content_url; - public String content_type; - public String title; - public String duration; - public int post_id; - - public Attachment(Parcel in) { - readFromParcel(in); - } - - public Attachment() { - - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(id); - out.writeString(content_url); - out.writeString(content_type); - out.writeString(title); - out.writeString(duration); - out.writeInt(post_id); - } - - public String toString() { - if (title != null && !title.isEmpty()) { - return title; - } else { - try { - URL url = new URL(content_url.trim()); - return new File(url.getFile()).getName(); - } catch (MalformedURLException e) { - return content_url; - } - } - } - - public void readFromParcel(Parcel in) { - id = in.readInt(); - content_url = in.readString(); - content_type = in.readString(); - title = in.readString(); - duration = in.readString(); - post_id = in.readInt(); - } - - @SuppressWarnings("rawtypes") - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public Attachment createFromParcel(Parcel in) { - return new Attachment(in); - } - - public Attachment[] newArray(int size) { - return new Attachment[size]; + public int id; + public String content_url; + public String content_type; + public String title; + public String duration; + public int post_id; + + public Attachment(Parcel in) { + readFromParcel(in); + } + + public Attachment() { + + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(id); + out.writeString(content_url); + out.writeString(content_type); + out.writeString(title); + out.writeString(duration); + out.writeInt(post_id); + } + + public String toString() { + if (title != null && !title.isEmpty()) { + return title; + } else { + try { + URL url = new URL(content_url.trim()); + return new File(url.getFile()).getName(); + } catch (MalformedURLException e) { + return content_url; } - }; + } + } + + public void readFromParcel(Parcel in) { + id = in.readInt(); + content_url = in.readString(); + content_type = in.readString(); + title = in.readString(); + duration = in.readString(); + post_id = in.readInt(); + } + + @SuppressWarnings("rawtypes") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public Attachment createFromParcel(Parcel in) { + return new Attachment(in); + } + + public Attachment[] newArray(int size) { + return new Attachment[size]; + } + }; } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java index ab1e8c8e..9fc40e96 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Feed.java @@ -8,185 +8,185 @@ import androidx.annotation.NonNull; import org.fox.ttrss.R; public class Feed implements Comparable<Feed>, Parcelable { - public static final int TYPE_GOBACK = -10001; - public static final int TYPE_DIVIDER = -10002; - public static final int TYPE_TOGGLE_UNREAD = -10003; - - public String feed_url; - public String title; - public int id; - public int unread; - public boolean has_icon; - public int cat_id; - public int last_updated; - public int order_id; - public String last_error; - public boolean is_cat; - public int update_interval; + public static final int TYPE_GOBACK = -10001; + public static final int TYPE_DIVIDER = -10002; + public static final int TYPE_TOGGLE_UNREAD = -10003; + + public String feed_url; + public String title; + public int id; + public int unread; + public boolean has_icon; + public int cat_id; + public int last_updated; + public int order_id; + public String last_error; + public boolean is_cat; + public int update_interval; transient public boolean always_open_headlines; - public Feed(int id) { - this.id = id; - this.title = "ID:" + id; - this.is_cat = false; - this.last_error = ""; - } - - public Feed(int id, String title, boolean is_cat) { - this.id = id; - this.title = title; - this.is_cat = is_cat; - this.last_error = ""; - } - - public Feed(Feed feed) { - id = feed.id; - feed_url = feed.feed_url; - title = feed.title; - unread = feed.unread; - has_icon = feed.has_icon; - cat_id = feed.cat_id; - last_updated = feed.last_updated; - order_id = feed.order_id; - is_cat = feed.is_cat; - always_open_headlines = feed.always_open_headlines; - last_error = feed.last_error; - update_interval = feed.update_interval; - } - - public static final int MARKED = -1; - public static final int PUBLISHED = -2; - public static final int FRESH = -3; - public static final int ALL_ARTICLES = -4; - public static final int RECENTLY_READ = -6; - public static final int ARCHIVED = 0; - - public static final int CAT_SPECIAL = -1; - public static final int CAT_LABELS = -2; - public static final int CAT_UNCATEGORIZED = 0; - - public static int getSpecialFeedTitleId(int feedId, boolean isCat) { - if (!isCat) - switch (feedId) { - case MARKED: - return R.string.feed_starred_articles; - case PUBLISHED: - return R.string.feed_published_articles; - case FRESH: - return R.string.fresh_articles; - case ALL_ARTICLES: - return R.string.feed_all_articles; - case RECENTLY_READ: - return R.string.feed_recently_read; - case ARCHIVED: - return R.string.feed_archived_articles; - case TYPE_TOGGLE_UNREAD: - return R.string.unread_only; - default: - throw new IllegalArgumentException("Invalid special feed id: " + feedId); - } - else - switch (feedId) { - case CAT_LABELS: - return R.string.cat_labels; - case CAT_SPECIAL: - return R.string.cat_special; - case CAT_UNCATEGORIZED: - return R.string.cat_uncategorized; - default: - throw new IllegalArgumentException("Invalid special category id: " + feedId); - } - } - - public Feed(Parcel in) { - readFromParcel(in); - } - - public Feed() { - - } - - @Override - public boolean equals(Object other) { - if (other == null) - return false; - - if (other == this) - return true; - - if (this.getClass() != other.getClass()) - return false; - - Feed feed = (Feed) other; - - return feed.id == this.id && this.is_cat == feed.is_cat; - } - - @NonNull - @Override - public String toString() { - return "{id:" + this.id + ",is_cat:" + this.is_cat + "}"; - } - - @Override - public int compareTo(Feed feed) { - if (feed.unread != this.unread) - return feed.unread - this.unread; - else - return this.title.compareTo(feed.title); - } - - public void fixNullFields() { - if (feed_url == null) feed_url = ""; - if (title == null) title = ""; - if (last_error == null) last_error = ""; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeString(feed_url); - out.writeString(title); - out.writeInt(id); - out.writeInt(unread); - out.writeInt(has_icon ? 1 : 0); - out.writeInt(cat_id); - out.writeInt(last_updated); - out.writeInt(is_cat ? 1 : 0); - out.writeInt(order_id); - out.writeInt(always_open_headlines ? 1 : 0); - out.writeString(last_error); - out.writeInt(update_interval); - } - - public void readFromParcel(Parcel in) { - feed_url = in.readString(); - title = in.readString(); - id = in.readInt(); - unread = in.readInt(); - has_icon = in.readInt() == 1; - cat_id = in.readInt(); - last_updated = in.readInt(); - is_cat = in.readInt() == 1; - order_id = in.readInt(); - always_open_headlines = in.readInt() == 1; - last_error = in.readString(); - update_interval = in.readInt(); - } - - @SuppressWarnings("rawtypes") - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public Feed createFromParcel(Parcel in) { - return new Feed(in); + public Feed(int id) { + this.id = id; + this.title = "ID:" + id; + this.is_cat = false; + this.last_error = ""; + } + + public Feed(int id, String title, boolean is_cat) { + this.id = id; + this.title = title; + this.is_cat = is_cat; + this.last_error = ""; + } + + public Feed(Feed feed) { + id = feed.id; + feed_url = feed.feed_url; + title = feed.title; + unread = feed.unread; + has_icon = feed.has_icon; + cat_id = feed.cat_id; + last_updated = feed.last_updated; + order_id = feed.order_id; + is_cat = feed.is_cat; + always_open_headlines = feed.always_open_headlines; + last_error = feed.last_error; + update_interval = feed.update_interval; + } + + public static final int MARKED = -1; + public static final int PUBLISHED = -2; + public static final int FRESH = -3; + public static final int ALL_ARTICLES = -4; + public static final int RECENTLY_READ = -6; + public static final int ARCHIVED = 0; + + public static final int CAT_SPECIAL = -1; + public static final int CAT_LABELS = -2; + public static final int CAT_UNCATEGORIZED = 0; + + public static int getSpecialFeedTitleId(int feedId, boolean isCat) { + if (!isCat) + switch (feedId) { + case MARKED: + return R.string.feed_starred_articles; + case PUBLISHED: + return R.string.feed_published_articles; + case FRESH: + return R.string.fresh_articles; + case ALL_ARTICLES: + return R.string.feed_all_articles; + case RECENTLY_READ: + return R.string.feed_recently_read; + case ARCHIVED: + return R.string.feed_archived_articles; + case TYPE_TOGGLE_UNREAD: + return R.string.unread_only; + default: + throw new IllegalArgumentException("Invalid special feed id: " + feedId); } - - public Feed[] newArray(int size) { - return new Feed[size]; + else + switch (feedId) { + case CAT_LABELS: + return R.string.cat_labels; + case CAT_SPECIAL: + return R.string.cat_special; + case CAT_UNCATEGORIZED: + return R.string.cat_uncategorized; + default: + throw new IllegalArgumentException("Invalid special category id: " + feedId); } - }; + } + + public Feed(Parcel in) { + readFromParcel(in); + } + + public Feed() { + + } + + @Override + public boolean equals(Object other) { + if (other == null) + return false; + + if (other == this) + return true; + + if (this.getClass() != other.getClass()) + return false; + + Feed feed = (Feed) other; + + return feed.id == this.id && this.is_cat == feed.is_cat; + } + + @NonNull + @Override + public String toString() { + return "{id:" + this.id + ",is_cat:" + this.is_cat + "}"; + } + + @Override + public int compareTo(Feed feed) { + if (feed.unread != this.unread) + return feed.unread - this.unread; + else + return this.title.compareTo(feed.title); + } + + public void fixNullFields() { + if (feed_url == null) feed_url = ""; + if (title == null) title = ""; + if (last_error == null) last_error = ""; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(feed_url); + out.writeString(title); + out.writeInt(id); + out.writeInt(unread); + out.writeInt(has_icon ? 1 : 0); + out.writeInt(cat_id); + out.writeInt(last_updated); + out.writeInt(is_cat ? 1 : 0); + out.writeInt(order_id); + out.writeInt(always_open_headlines ? 1 : 0); + out.writeString(last_error); + out.writeInt(update_interval); + } + + public void readFromParcel(Parcel in) { + feed_url = in.readString(); + title = in.readString(); + id = in.readInt(); + unread = in.readInt(); + has_icon = in.readInt() == 1; + cat_id = in.readInt(); + last_updated = in.readInt(); + is_cat = in.readInt() == 1; + order_id = in.readInt(); + always_open_headlines = in.readInt() == 1; + last_error = in.readString(); + update_interval = in.readInt(); + } + + @SuppressWarnings("rawtypes") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public Feed createFromParcel(Parcel in) { + return new Feed(in); + } + + public Feed[] newArray(int size) { + return new Feed[size]; + } + }; }
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/GalleryEntry.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/GalleryEntry.java index 50e34f78..b55e365a 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/GalleryEntry.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/GalleryEntry.java @@ -23,7 +23,7 @@ public class GalleryEntry implements Serializable, Parcelable { } }; - public enum GalleryEntryType { TYPE_IMAGE, TYPE_VIDEO } + public enum GalleryEntryType {TYPE_IMAGE, TYPE_VIDEO} public String url; public GalleryEntryType type; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Label.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Label.java index 0d4f3699..bca1d992 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Label.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/types/Label.java @@ -1,13 +1,13 @@ package org.fox.ttrss.types; public class Label { - public int id; - public String caption; - public String fg_color; - public String bg_color; - public boolean checked; - - public Label() { - - } + public int id; + public String caption; + public String fg_color; + public String bg_color; + public boolean checked; + + public Label() { + + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java index 4832a2b4..b9d3864c 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ArticleDiffItemCallback.java @@ -8,7 +8,9 @@ import org.fox.ttrss.types.Article; public class ArticleDiffItemCallback extends DiffUtil.ItemCallback<Article> { private final String TAG = this.getClass().getSimpleName(); - public enum ChangePayload { UNREAD, MARKED, SELECTED, PUBLISHED, NOTE, ACTIVE, SCORE }; + public enum ChangePayload {UNREAD, MARKED, SELECTED, PUBLISHED, NOTE, ACTIVE, SCORE} + + ; @Override public boolean areItemsTheSame(@NonNull Article oldItem, @NonNull Article newItem) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/SimpleLoginManager.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/SimpleLoginManager.java index abbe1ffa..94ead554 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/SimpleLoginManager.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/SimpleLoginManager.java @@ -11,88 +11,88 @@ import org.fox.ttrss.ApiRequest; import java.util.HashMap; public abstract class SimpleLoginManager { - private final String TAG = this.getClass().getSimpleName(); - - protected class LoginRequest extends ApiRequest { - private final int m_requestId; - protected String m_sessionId; - protected int m_apiLevel; - protected Context m_context; - - public LoginRequest(Context context, int requestId) { - super(context); - m_context = context; - m_requestId = requestId; - } - - protected void onPostExecute(JsonElement result) { - Log.d(TAG, "onPostExecute"); - - if (result != null) { - try { - JsonObject content = result.getAsJsonObject(); - if (content != null) { - m_sessionId = content.get("session_id").getAsString(); - - Log.d(TAG, "[SLM] Authenticated!"); - - ApiRequest req = new ApiRequest(m_context) { - protected void onPostExecute(JsonElement result) { - m_apiLevel = 0; - - if (result != null) { - try { - m_apiLevel = result.getAsJsonObject() - .get("level").getAsInt(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - Log.d(TAG, "[SLM] Received API level: " + m_apiLevel); - - onLoginSuccess(m_requestId, m_sessionId, m_apiLevel); - } - }; - - HashMap<String, String> map = new HashMap<>(); - map.put("sid", m_sessionId); - map.put("op", "getApiLevel"); - - req.execute(map); - - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - m_sessionId = null; - - onLoginFailed(m_requestId, this); - } - - } - - public void logIn(Context context, int requestId, final String login, final String password) { - LoginRequest ar = new LoginRequest(context, requestId); - - HashMap<String, String> map = new HashMap<>(); - map.put("op", "login"); - map.put("user", login.trim()); - map.put("password", password.trim()); - - onLoggingIn(requestId); - - ar.execute(map); - } - - protected abstract void onLoggingIn(int requestId); - - protected abstract void onLoginSuccess(int requestId, String sessionId, int apiLevel); - - protected abstract void onLoginFailed(int requestId, ApiRequest ar); - + private final String TAG = this.getClass().getSimpleName(); + + protected class LoginRequest extends ApiRequest { + private final int m_requestId; + protected String m_sessionId; + protected int m_apiLevel; + protected Context m_context; + + public LoginRequest(Context context, int requestId) { + super(context); + m_context = context; + m_requestId = requestId; + } + + protected void onPostExecute(JsonElement result) { + Log.d(TAG, "onPostExecute"); + + if (result != null) { + try { + JsonObject content = result.getAsJsonObject(); + if (content != null) { + m_sessionId = content.get("session_id").getAsString(); + + Log.d(TAG, "[SLM] Authenticated!"); + + ApiRequest req = new ApiRequest(m_context) { + protected void onPostExecute(JsonElement result) { + m_apiLevel = 0; + + if (result != null) { + try { + m_apiLevel = result.getAsJsonObject() + .get("level").getAsInt(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + Log.d(TAG, "[SLM] Received API level: " + m_apiLevel); + + onLoginSuccess(m_requestId, m_sessionId, m_apiLevel); + } + }; + + HashMap<String, String> map = new HashMap<>(); + map.put("sid", m_sessionId); + map.put("op", "getApiLevel"); + + req.execute(map); + + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + m_sessionId = null; + + onLoginFailed(m_requestId, this); + } + + } + + public void logIn(Context context, int requestId, final String login, final String password) { + LoginRequest ar = new LoginRequest(context, requestId); + + HashMap<String, String> map = new HashMap<>(); + map.put("op", "login"); + map.put("user", login.trim()); + map.put("password", password.trim()); + + onLoggingIn(requestId); + + ar.execute(map); + } + + protected abstract void onLoggingIn(int requestId); + + protected abstract void onLoginSuccess(int requestId, String sessionId, int apiLevel); + + protected abstract void onLoginFailed(int requestId, ApiRequest ar); + } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/SmallWidgetProvider.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/SmallWidgetProvider.java index d525cb90..677b2670 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/SmallWidgetProvider.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/SmallWidgetProvider.java @@ -18,12 +18,12 @@ import org.fox.ttrss.OnlineActivity; import org.fox.ttrss.R; public class SmallWidgetProvider extends AppWidgetProvider { - private final String TAG = this.getClass().getSimpleName(); + private final String TAG = this.getClass().getSimpleName(); - public static final String ACTION_REQUEST_UPDATE = "org.fox.ttrss.WIDGET_FORCE_UPDATE"; + public static final String ACTION_REQUEST_UPDATE = "org.fox.ttrss.WIDGET_FORCE_UPDATE"; @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d(TAG, "onUpdate"); Intent intent = new Intent(context, OnlineActivity.class); @@ -32,7 +32,7 @@ public class SmallWidgetProvider extends AppWidgetProvider { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_small); views.setOnClickPendingIntent(R.id.widget_main, pendingIntent); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String widgetBackground = prefs.getString("widget_background", "WB_LIGHT"); Log.d(TAG, "widget bg: " + widgetBackground); @@ -51,7 +51,7 @@ public class SmallWidgetProvider extends AppWidgetProvider { appWidgetManager.updateAppWidget(appWidgetIds, views); } - @Override + @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "onReceive: " + intent); @@ -59,15 +59,15 @@ public class SmallWidgetProvider extends AppWidgetProvider { ComponentName thisAppWidget = new ComponentName(context.getPackageName(), SmallWidgetProvider.class.getName()); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget); - onUpdate(context, appWidgetManager, appWidgetIds); + onUpdate(context, appWidgetManager, appWidgetIds); - if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(intent.getAction()) || + if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(intent.getAction()) || ACTION_REQUEST_UPDATE.equals(intent.getAction())) { - Log.d(TAG, "sheduling widget update..."); + Log.d(TAG, "sheduling widget update..."); CommonActivity.requestWidgetUpdate(context); } - } + } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/WidgetUpdateService.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/WidgetUpdateService.java index 43b12d9e..59e7ccf3 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/WidgetUpdateService.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/widget/WidgetUpdateService.java @@ -176,7 +176,7 @@ public class WidgetUpdateService extends JobIntentService { return networkInfo != null && networkInfo.isConnected(); } - public void updateWidgets(int unread, int resultCode) { + public void updateWidgets(int unread, int resultCode) { Log.d(TAG, "updateWidgets:" + unread + " " + resultCode); Context context = getApplicationContext(); @@ -188,7 +188,7 @@ public class WidgetUpdateService extends JobIntentService { updateWidgetsText(context, appWidgetManager, appWidgetIds, unread, resultCode); if (resultCode != UPDATE_IN_PROGRESS) stopSelf(); - } + } private void updateWidgetsText(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, int unread, int resultCode) { |