diff options
| author | Andrew Dolgov <fox@fakecake.org> | 2025-05-11 11:55:06 +0300 |
|---|---|---|
| committer | Andrew Dolgov <fox@fakecake.org> | 2025-05-11 11:55:06 +0300 |
| commit | ea2ecf47388e3211f499dea0e10c26047f753341 (patch) | |
| tree | 96e785c5c5f501fa2692154df2712ed61682c494 | |
| parent | 6c7fec94127e62975e2a5213a3af4010089a9702 (diff) | |
| parent | fd30ab142e6aad2adc7175ebdfab8db25757cf51 (diff) | |
Merge branch 'protected/bottomappbar' into 'master'
wip bottom appbar
See merge request tt-rss/tt-rss-android!30
51 files changed, 623 insertions, 6533 deletions
diff --git a/org.fox.ttrss/build.gradle b/org.fox.ttrss/build.gradle index be621d57..c8d9b3d5 100755 --- a/org.fox.ttrss/build.gradle +++ b/org.fox.ttrss/build.gradle @@ -13,7 +13,7 @@ android { //noinspection ExpiredTargetSdkVersion targetSdkVersion 31 compileSdk 35 - versionCode 563 + versionCode getGitVersionCode() versionName getVersion() vectorDrawables.useSupportLibrary = true } @@ -55,10 +55,18 @@ android { } } +def getGitVersionCode() { + return new Date(getGitTimestamp()).format('yyyyMMdd').toInteger() +} + +def getGitTimestampFormatted() { + return new Date(getGitTimestamp()).format('YY.MM') +} + def getGitTimestamp() { // gitlab CI iso-8601 timestamp if (System.getenv("CI_COMMIT_TIMESTAMP")) { - return Date.parse("yyyy-MM-dd'T'HH:mm:ssXXX", System.getenv("CI_COMMIT_TIMESTAMP")).format('YY.MM') + return Date.parse("yyyy-MM-dd'T'HH:mm:ssXXX", System.getenv("CI_COMMIT_TIMESTAMP")).getTime(); } // try to get version from git repo in current dir @@ -69,7 +77,7 @@ def getGitTimestamp() { standardOutput = stdout } - return new Date(stdout.toString().trim().toLong() * 1000).format('YY.MM') + return stdout.toString().trim().toLong() * 1000; } catch (ignored) { return 0; @@ -97,7 +105,7 @@ def getGitCommitHash() { } def getVersion() { - return getGitTimestamp() + '-' + getGitCommitHash(); + return getGitTimestampFormatted() + '-' + getGitCommitHash(); } dependencies { diff --git a/org.fox.ttrss/src/main/AndroidManifest.xml b/org.fox.ttrss/src/main/AndroidManifest.xml index b79e3b05..0f66928f 100755 --- a/org.fox.ttrss/src/main/AndroidManifest.xml +++ b/org.fox.ttrss/src/main/AndroidManifest.xml @@ -81,19 +81,6 @@ android:label="@string/logcat_title" > </activity> <activity - android:name=".offline.OfflineActivity" - android:label="@string/app_name" > - </activity> - <activity - android:name=".offline.OfflineMasterActivity" - android:label="@string/app_name" > - </activity> - <activity - android:name=".offline.OfflineDetailActivity" - android:configChanges="keyboardHidden|orientation|screenSize" - android:label="@string/app_name" > - </activity> - <activity android:name=".share.ShareActivity" android:excludeFromRecents="true" android:label="@string/app_name" @@ -209,16 +196,6 @@ </intent-filter> </activity> - <service - android:name=".offline.OfflineDownloadService" - android:enabled="true" /> - <service - android:name=".offline.OfflineUploadService" - android:enabled="true" /> - <service - android:name=".util.ImageCacheService" - android:enabled="true" /> - <meta-data android:name="com.google.android.backup.api_key" android:value="AEdPqrEAAAAIwG6zsGB4qo6ZhjfwIJpm9WI7AqmWaoRXm6ZJnA" /> 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 ce5089f8..9fd0623a 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 @@ -9,6 +9,8 @@ import android.net.NetworkInfo; import android.preference.PreferenceManager; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -18,7 +20,6 @@ import java.io.IOException; import java.util.HashMap; import java.util.Locale; -import androidx.annotation.NonNull; import okhttp3.Credentials; import okhttp3.MediaType; import okhttp3.OkHttpClient; diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java index 95f7fa87..18eedee1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/ApiLoader.java @@ -2,14 +2,14 @@ package org.fox.ttrss; import android.content.Context; +import androidx.loader.content.AsyncTaskLoader; + import com.google.gson.JsonElement; import org.fox.ttrss.ApiCommon.ApiError; import java.util.HashMap; -import androidx.loader.content.AsyncTaskLoader; - public class ApiLoader extends AsyncTaskLoader<JsonElement> implements ApiCommon.ApiCaller { private final String TAG = this.getClass().getSimpleName(); 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 e993a866..e6c323e9 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 @@ -1,5 +1,7 @@ package org.fox.ttrss; +import static org.fox.ttrss.ApiCommon.ApiError; + import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; @@ -8,8 +10,6 @@ import com.google.gson.JsonElement; import java.util.HashMap; -import static org.fox.ttrss.ApiCommon.ApiError; - public class ApiRequest extends AsyncTask<HashMap<String,String>, Integer, JsonElement> implements ApiCommon.ApiCaller { private final String TAG = this.getClass().getSimpleName(); 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 dd27631b..2b2bf090 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 @@ -16,19 +16,15 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.webkit.WebBackForwardList; -import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebView.HitTestResult; import android.webkit.WebViewClient; -import android.widget.EditText; -import android.widget.FrameLayout; import android.widget.TextView; import androidx.core.text.HtmlCompat; import com.google.android.material.button.MaterialButton; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.fox.ttrss.types.Article; import org.fox.ttrss.types.Attachment; @@ -39,6 +35,7 @@ import java.text.DateFormat; 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(); @@ -46,10 +43,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { protected Article m_article; private DetailActivity m_activity; private WebView m_web; - protected View m_customView; - protected FrameLayout m_customViewContainer; - protected View m_contentView; - protected FSVideoChromeClient m_chromeClient; //protected View m_fab; protected int m_articleFontSize; protected int m_articleSmallFontSize; @@ -58,68 +51,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { m_article = article; } - private class FSVideoChromeClient extends WebChromeClient { - //protected View m_videoChildView; - - private CustomViewCallback m_callback; - - public FSVideoChromeClient(View container) { - super(); - - } - - @Override - public void onShowCustomView(View view, CustomViewCallback callback) { - m_activity.getSupportActionBar().hide(); - - // if a view already exists then immediately terminate the new one - if (m_customView != null) { - callback.onCustomViewHidden(); - return; - } - m_customView = view; - m_contentView.setVisibility(View.GONE); - - m_customViewContainer.setVisibility(View.VISIBLE); - m_customViewContainer.addView(view); - - //if (m_fab != null) m_fab.setVisibility(View.GONE); - - m_activity.showSidebar(false); - - m_callback = callback; - } - - @Override - public void onHideCustomView() { - super.onHideCustomView(); - - m_activity.getSupportActionBar().show(); - - if (m_customView == null) - return; - - m_contentView.setVisibility(View.VISIBLE); - m_customViewContainer.setVisibility(View.GONE); - - // Hide the custom view. - m_customView.setVisibility(View.GONE); - - // Remove the custom view from its container. - m_customViewContainer.removeView(m_customView); - m_callback.onCustomViewHidden(); - - /*if (m_fab != null && m_prefs.getBoolean("enable_article_fab", true)) - m_fab.setVisibility(View.VISIBLE);*/ - - m_customView = null; - - m_activity.showSidebar(true); - } - } - - //private View.OnTouchListener m_gestureListener; - @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { @@ -169,13 +100,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { m_activity.finish(); } - /* if (m_fsviewShown) { - view.findViewById(R.id.article_fullscreen_video).setVisibility(View.VISIBLE); - view.findViewById(R.id.article_scrollview).setVisibility(View.INVISIBLE); - } */ - - m_customViewContainer = view.findViewById(R.id.article_fullscreen_video); - /* if (m_article.id == HeadlinesFragment.ARTICLE_SPECIAL_TOP_CHANGED) { TextView statusMessage = (TextView) view.findViewById(R.id.article_status_message); statusMessage.setText(R.string.headlines_row_top_changed); @@ -259,6 +183,13 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { } else { noteContainer.setVisibility(View.GONE); } + + note.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + m_activity.editArticleNote(m_article); + } + }); } TextView dv = view.findViewById(R.id.date); @@ -336,8 +267,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { } }); - m_web.setVisibility(View.VISIBLE); - renderContent(savedInstanceState); return view; @@ -385,8 +314,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { ws.setJavaScriptEnabled(false); - m_chromeClient = new FSVideoChromeClient(getView()); - m_web.setWebChromeClient(m_chromeClient); m_web.setBackgroundColor(Color.TRANSPARENT); ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); @@ -486,30 +413,6 @@ public class ArticleFragment extends androidx.fragment.app.Fragment { if (m_web != null) m_web.onResume(); } - public boolean inCustomView() { - return (m_customView != null); - } - - @Override - public void onStop() { - super.onStop(); - - if (inCustomView()) { - hideCustomView(); - } - } - - public void hideCustomView() { - if (m_chromeClient != null) { - m_chromeClient.onHideCustomView(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - @Override public void onAttach(Activity activity) { super.onAttach(activity); 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 c94f0c3a..445f94bb 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 @@ -6,7 +6,6 @@ import android.content.SharedPreferences; import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; -import android.os.Parcelable; import android.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; @@ -14,9 +13,10 @@ import android.view.View; import android.view.ViewGroup; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; +import androidx.fragment.app.FragmentActivity; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.snackbar.Snackbar; import com.google.gson.JsonElement; @@ -31,7 +31,7 @@ import java.util.HashMap; public class ArticlePager extends androidx.fragment.app.Fragment { private final String TAG = "ArticlePager"; - private PagerAdapter m_adapter; + private RecyclerView.Adapter m_adapter; private HeadlinesEventListener m_listener; protected Article m_article; protected ArticleList m_articles = new ArticleList(); //m_articles = Application.getInstance().m_loadedArticles; @@ -42,29 +42,18 @@ public class ArticlePager extends androidx.fragment.app.Fragment { protected int m_firstId = 0; private boolean m_refreshInProgress; private boolean m_lazyLoadDisabled; + private ViewPager2 m_pager; - private class PagerAdapter extends FragmentStatePagerAdapter { + private class PagerAdapter extends FragmentStateAdapter { - public PagerAdapter(FragmentManager fm) { - super(fm); + public PagerAdapter(FragmentActivity fa) { + super(fa); } private ArticleFragment m_currentFragment; - // workaround for possible TransactionTooLarge exception on 8.0+ - // we don't need to save member state anyway, bridge takes care of it @Override - public Parcelable saveState() { - Bundle bundle = (Bundle) super.saveState(); - - if (bundle != null) - bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out - - return bundle; - } - - @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { try { Article article = m_articles.get(position); @@ -82,7 +71,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { } @Override - public int getCount() { + public int getItemCount() { return m_articles.size(); } @@ -90,13 +79,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { return m_currentFragment; } - @Override - public void setPrimaryItem(ViewGroup container, int position, Object object) { - m_currentFragment = ((ArticleFragment) object); - - super.setPrimaryItem(container, position, object); - } - } public void initialize(Article article, Feed feed, ArticleList articles) { @@ -125,7 +107,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { if (savedInstanceState != null) { m_article = savedInstanceState.getParcelable("m_article"); - //m_articles = savedInstanceState.getParcelable("m_articles"); m_feed = savedInstanceState.getParcelable("m_feed"); m_firstId = savedInstanceState.getInt("m_firstId"); } @@ -136,30 +117,26 @@ public class ArticlePager extends androidx.fragment.app.Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - + if (savedInstanceState != null) { if (m_activity instanceof DetailActivity) { m_articles = ((DetailActivity)m_activity).m_articles; } } - m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager()); + m_adapter = new PagerAdapter(getActivity()); - ViewPager pager = view.findViewById(R.id.article_pager); - + m_pager = view.findViewById(R.id.article_pager); + int position = m_articles.indexOf(m_article); m_listener.onArticleSelected(m_article, false); - pager.setAdapter(m_adapter); - - pager.setCurrentItem(position); - pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - - } + m_pager.setAdapter(m_adapter); + m_pager.setOffscreenPageLimit(3); + m_pager.setCurrentItem(position, false); + m_pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { Log.d(TAG, "onPageSelected: " + position); @@ -178,7 +155,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { //Log.d(TAG, "Page #" + position + "/" + m_adapter.getCount()); - if (!m_refreshInProgress && !m_lazyLoadDisabled && (m_activity.isSmallScreen() || m_activity.isPortrait()) && position >= m_adapter.getCount() - 5) { + if (!m_refreshInProgress && !m_lazyLoadDisabled && (m_activity.isSmallScreen() || m_activity.isPortrait()) && position >= m_adapter.getItemCount() - 5) { Log.d(TAG, "loading more articles..."); new Handler().postDelayed(new Runnable() { @@ -190,11 +167,6 @@ public class ArticlePager extends androidx.fragment.app.Fragment { } } } - - @Override - public void onPageScrollStateChanged(int state) { - - } }); return view; @@ -220,9 +192,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { if (isDetached() || !isAdded()) return; if (!append) { - ViewPager pager = getView().findViewById(R.id.article_pager); - pager.setCurrentItem(0); - + m_pager.setCurrentItem(0, false); m_articles.clear(); } @@ -390,9 +360,7 @@ public class ArticlePager extends androidx.fragment.app.Fragment { int position = m_articles.indexOf(m_article); - ViewPager pager = getView().findViewById(R.id.article_pager); - - pager.setCurrentItem(position); + m_pager.setCurrentItem(position); } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java index 0952e7f7..06f18667 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/BaseFeedlistFragment.java @@ -3,7 +3,6 @@ package org.fox.ttrss; import android.content.Intent; import android.content.SharedPreferences; import android.util.TypedValue; -import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.widget.CompoundButton; @@ -13,8 +12,6 @@ import android.widget.TextView; import androidx.appcompat.widget.SwitchCompat; -import org.fox.ttrss.offline.OfflineActivity; - import java.net.MalformedURLException; import java.net.URL; @@ -23,115 +20,76 @@ public abstract class BaseFeedlistFragment extends androidx.fragment.app.Fragmen public void initDrawerHeader(LayoutInflater inflater, View view, ListView list, final CommonActivity activity, final SharedPreferences prefs, boolean isRoot) { - boolean isOffline = activity instanceof OfflineActivity; - - try { - View layout = inflater.inflate(R.layout.drawer_header, list, false); - list.addHeaderView(layout, null, false); + View layout = inflater.inflate(R.layout.drawer_header, list, false); + list.addHeaderView(layout, null, false); - TextView login = view.findViewById(R.id.drawer_header_login); - TextView server = view.findViewById(R.id.drawer_header_server); + TextView login = view.findViewById(R.id.drawer_header_login); + TextView server = view.findViewById(R.id.drawer_header_server); - login.setText(prefs.getString("login", "")); - try { - server.setText(new URL(prefs.getString("ttrss_url", "")).getHost()); - } catch (MalformedURLException e) { - server.setText(""); - } + login.setText(prefs.getString("login", "")); + try { + server.setText(new URL(prefs.getString("ttrss_url", "")).getHost()); + } catch (MalformedURLException e) { + server.setText(""); + } - View settings = view.findViewById(R.id.drawer_settings_btn); + View settings = view.findViewById(R.id.drawer_settings_btn); - settings.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - Intent intent = new Intent(getActivity(), - PreferencesActivity.class); + settings.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + try { + Intent intent = new Intent(getActivity(), + PreferencesActivity.class); - startActivityForResult(intent, 0); + startActivityForResult(intent, 0); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception e) { + e.printStackTrace(); } - }); - - /* deal with ~material~ footers */ + } + }); - // divider - final View footer = inflater.inflate(R.layout.drawer_divider, list, false); - footer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // - } - }); - list.addFooterView(footer); - - // unread only checkbox - final View rowToggle = inflater.inflate(R.layout.feeds_row_toggle, list, false); - list.addFooterView(rowToggle); - TextView text = rowToggle.findViewById(R.id.title); - text.setText(R.string.unread_only); - - ImageView icon = rowToggle.findViewById(R.id.icon); - TypedValue tv = new TypedValue(); - getActivity().getTheme().resolveAttribute(R.attr.ic_filter_variant, tv, true); - icon.setImageResource(tv.resourceId); - - final SwitchCompat rowSwitch = rowToggle.findViewById(R.id.row_switch); - rowSwitch.setChecked(activity.getUnreadOnly()); - - rowSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton button, boolean isChecked) { - activity.setUnreadOnly(isChecked); - refresh(); - } - }); + /* deal with ~material~ footers */ - footer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - rowSwitch.setChecked(!rowSwitch.isChecked()); - } - }); - - // root or subdirectory (i.e. feed category) - if (isRoot) { - // offline - final View offlineFooter = inflater.inflate(R.layout.feeds_row, list, false); - offlineFooter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (activity instanceof OnlineActivity) { - ((OnlineActivity)activity).switchOffline(); - - } else if (activity instanceof OfflineActivity) { - ((OfflineActivity)activity).switchOnline(); - } - } - }); - - list.addFooterView(offlineFooter); - text = offlineFooter.findViewById(R.id.title); - text.setText(isOffline ? R.string.go_online : R.string.go_offline); - - icon = offlineFooter.findViewById(R.id.icon); - tv = new TypedValue(); - getActivity().getTheme().resolveAttribute(isOffline ? R.attr.ic_cloud_upload : R.attr.ic_cloud_download, tv, true); - icon.setImageResource(tv.resourceId); - - TextView counter = offlineFooter.findViewById(R.id.unread_counter); - counter.setText(R.string.blank); + // divider + final View footer = inflater.inflate(R.layout.drawer_divider, list, false); + footer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // } + }); + list.addFooterView(footer); + + // unread only checkbox + final View rowToggle = inflater.inflate(R.layout.feeds_row_toggle, list, false); + list.addFooterView(rowToggle); + TextView text = rowToggle.findViewById(R.id.title); + text.setText(R.string.unread_only); + + ImageView icon = rowToggle.findViewById(R.id.icon); + TypedValue tv = new TypedValue(); + getActivity().getTheme().resolveAttribute(R.attr.ic_filter_variant, tv, true); + icon.setImageResource(tv.resourceId); + + final SwitchCompat rowSwitch = rowToggle.findViewById(R.id.row_switch); + rowSwitch.setChecked(activity.getUnreadOnly()); + + rowSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton button, boolean isChecked) { + activity.setUnreadOnly(isChecked); + refresh(); + } + }); - } catch (InflateException e) { - // welp couldn't inflate header i guess - e.printStackTrace(); - } catch (java.lang.UnsupportedOperationException e) { - e.printStackTrace(); - } + footer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + rowSwitch.setChecked(!rowSwitch.isChecked()); + } + }); } 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 0ce80cb2..d93a52d3 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 @@ -28,7 +28,6 @@ import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; -import android.util.TypedValue; import android.view.Display; import android.view.View; import android.widget.CheckBox; 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 0edd6c10..c9da82da 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 @@ -10,12 +10,14 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.util.Log; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentTransaction; +import com.google.android.material.bottomappbar.BottomAppBar; import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.fox.ttrss.types.Article; @@ -25,6 +27,7 @@ import org.fox.ttrss.types.Feed; public class DetailActivity extends OnlineActivity implements HeadlinesEventListener { private final String TAG = this.getClass().getSimpleName(); protected ArticleList m_articles = new ArticleList(); + protected BottomAppBar m_bottomAppBar; protected SharedPreferences m_prefs; private Article m_activeArticle; @@ -68,6 +71,52 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList headlines.setVisibility(View.GONE); } + m_bottomAppBar = findViewById(R.id.detail_bottom_appbar); + + if (m_bottomAppBar != null) { + m_bottomAppBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + + final ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + final HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); + + Article article = ap.getSelectedArticle(); + + if (article == null) return false; + + int itemId = item.getItemId(); + + if (itemId == R.id.article_set_labels) { + editArticleLabels(article); + + return true; + } else if (itemId == R.id.toggle_attachments) { + displayAttachments(article); + + return true; + } else if (itemId == R.id.article_edit_note) { + editArticleNote(article); + + return true; + } else if (itemId == R.id.article_set_score) { + setArticleScore(article); + + return true; + } else if (itemId == R.id.toggle_unread) { + article.unread = !article.unread; + saveArticleUnread(article); + + if (hf != null) { + hf.notifyUpdated(); + } + } + + return false; + } + }); + } + FloatingActionButton fab = findViewById(R.id.detail_fab); if (fab != null && m_prefs.getBoolean("enable_article_fab", true)) { @@ -136,11 +185,50 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList if (feed != null) setTitle(feed.title); + + initBottomBarMenu(); + } + } + } + + @Override + public void invalidateOptionsMenu() { + super.invalidateOptionsMenu(); + + initBottomBarMenu(); + } + + 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); + + final ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + + if (ap != null) { + Article article = ap.getSelectedArticle(); + + if (article != null) { + if (article.score > 0) { + menu.findItem(R.id.article_set_score).setIcon(R.drawable.baseline_trending_up_24); + } else if (article.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(article.unread ? R.drawable.baseline_mark_email_unread_24 : + R.drawable.baseline_email_24); + + menu.findItem(R.id.toggle_attachments).setVisible(article.attachments != null && article.attachments.size() > 0); + } } } } - @Override + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -197,30 +285,13 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList if (m_menu != null && getSessionId() != null) { m_menu.setGroupVisible(R.id.menu_group_feeds, false); - //HeadlinesFragment hf = (HeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - m_menu.setGroupVisible(R.id.menu_group_headlines, !isPortrait() && !isSmallScreen()); - //m_menu.findItem(R.id.headlines_toggle_sidebar).setVisible(!isPortrait() && !isSmallScreen()); m_menu.findItem(R.id.headlines_toggle_sort_order).setVisible(false); ArticlePager af = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); m_menu.setGroupVisible(R.id.menu_group_article, af != null); - - if (af != null) { - if (af.getSelectedArticle() != null && af.getSelectedArticle().attachments != null && af.getSelectedArticle().attachments.size() > 0) { - /* if (!isCompatMode() && (isSmallScreen() || !isPortrait())) { - m_menu.findItem(R.id.toggle_attachments).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - } */ - m_menu.findItem(R.id.toggle_attachments).setVisible(true); - } else { - /* if (!isCompatMode()) { - m_menu.findItem(R.id.toggle_attachments).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - } */ - m_menu.findItem(R.id.toggle_attachments).setVisible(false); - } - } - + m_menu.findItem(R.id.search).setVisible(false); } } @@ -278,7 +349,6 @@ public class DetailActivity extends OnlineActivity implements HeadlinesEventList //Application.getInstance().m_activeArticle = article; invalidateOptionsMenu(); - } public void showSidebar(boolean show) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java index ef3797b3..7868c6af 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java @@ -3,7 +3,6 @@ package org.fox.ttrss; import android.graphics.Bitmap; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; @@ -14,12 +13,8 @@ import android.view.Window; import android.widget.PopupMenu; import android.widget.ProgressBar; -import androidx.activity.EdgeToEdge; -import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; -import androidx.core.graphics.Insets; -import androidx.core.view.ViewCompat; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsControllerCompat; @@ -51,7 +46,7 @@ public class GalleryActivity extends CommonActivity { protected String m_title; private ArticleImagesPagerAdapter m_adapter; public String m_content; - private ViewPager m_pager; + private ViewPager m_pager; // TODO replace with viewpager2 private ProgressBar m_checkProgress; private static class ArticleImagesPagerAdapter extends FragmentStatePagerAdapter { 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 b6953bc6..352ebab6 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 @@ -68,7 +68,6 @@ import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; import com.bumptech.glide.request.target.Target; import com.google.android.material.button.MaterialButton; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.divider.MaterialDividerItemDecoration; import com.google.android.material.snackbar.Snackbar; import com.google.gson.JsonElement; @@ -157,10 +156,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { if (article == null) return false; int itemId = item.getItemId(); - if (itemId == R.id.set_labels) { + if (itemId == R.id.article_set_labels) { m_activity.editArticleLabels(article); return true; - } else if (itemId == R.id.article_set_note) { + } else if (itemId == R.id.article_edit_note) { m_activity.editArticleNote(article); return true; } else if (itemId == R.id.headlines_article_unread) { @@ -268,8 +267,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { getActivity().getMenuInflater().inflate(R.menu.context_headlines, menu); - menu.findItem(R.id.set_labels).setEnabled(m_activity.getApiLevel() >= 1); - menu.findItem(R.id.article_set_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); } @@ -358,12 +357,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { m_list.setLayoutManager(m_layoutManager); m_list.setItemAnimator(new DefaultItemAnimator()); - if (m_compactLayoutMode) { + /* if (m_compactLayoutMode) { MaterialDividerItemDecoration materialDivider = new MaterialDividerItemDecoration(m_list.getContext(), m_layoutManager.getOrientation()); materialDivider.setDividerInsetStart(dpToPx(80)); m_list.addItemDecoration(materialDivider); - } + } */ ArticleListAdapter adapter = new ArticleListAdapter(getActivity(), R.layout.headlines_row, m_articles); @@ -795,7 +794,6 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { menuButtonView = v.findViewById(R.id.article_menu_button); flavorImageHolder = v.findViewById(R.id.flavorImageHolder); flavorImageLoadingBar = v.findViewById(R.id.flavorImageLoadingBar); - headlineFooter = v.findViewById(R.id.headline_footer); textImage = v.findViewById(R.id.text_image); textChecked = v.findViewById(R.id.text_checked); headlineHeader = v.findViewById(R.id.headline_header); @@ -1191,6 +1189,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { holder.excerptView.setTextSize(TypedValue.COMPLEX_UNIT_SP, headlineFontSize); holder.excerptView.setText(excerpt); + if (!excerpt.isEmpty()) { + holder.excerptView.setVisibility(View.VISIBLE); + } else { + holder.excerptView.setVisibility(View.GONE); + } + if (!canShowFlavorImage()) { holder.excerptView.setPadding(holder.excerptView.getPaddingLeft(), 0, @@ -1531,8 +1535,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment { MenuInflater inflater = popup.getMenuInflater(); inflater.inflate(R.menu.context_headlines, popup.getMenu()); - popup.getMenu().findItem(R.id.set_labels).setEnabled(m_activity.getApiLevel() >= 1); - popup.getMenu().findItem(R.id.article_set_note).setEnabled(m_activity.getApiLevel() >= 1); + 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(new PopupMenu.OnMenuItemClickListener() { @Override 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 60fad0ba..22db9e08 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 @@ -7,7 +7,6 @@ import android.view.MenuItem; import android.widget.ArrayAdapter; import android.widget.ListView; -import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -25,7 +24,6 @@ public class LogcatActivity extends CommonActivity { @Override public void onCreate(Bundle savedInstanceState) { - getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); 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 f06c9a7c..2e73af7f 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 @@ -1,15 +1,12 @@ package org.fox.ttrss; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Dialog; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnMultiChoiceClickListener; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.Point; import android.net.ConnectivityManager; @@ -27,9 +24,9 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.view.ActionMode; import androidx.appcompat.widget.Toolbar; -import androidx.appcompat.app.AlertDialog; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; @@ -37,15 +34,11 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; -import org.fox.ttrss.offline.OfflineActivity; -import org.fox.ttrss.offline.OfflineDownloadService; -import org.fox.ttrss.offline.OfflineUploadService; import org.fox.ttrss.share.SubscribeActivity; import org.fox.ttrss.types.Article; import org.fox.ttrss.types.ArticleList; import org.fox.ttrss.types.Feed; import org.fox.ttrss.types.Label; -import org.fox.ttrss.util.ImageCacheService; import java.lang.reflect.Type; import java.util.Arrays; @@ -165,17 +158,6 @@ public class OnlineActivity extends CommonActivity { public abstract void OnLoginFailed(); } - private BroadcastReceiver m_broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context content, Intent intent) { - if (intent.getAction().equals(OfflineUploadService.INTENT_ACTION_SUCCESS)) { - toast(R.string.offline_sync_success); - } - } - }; - - - @TargetApi(11) private class HeadlinesActionModeCallback implements ActionMode.Callback { @Override @@ -234,9 +216,6 @@ public class OnlineActivity extends CommonActivity { super.onCreate(savedInstanceState); SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE); - boolean isOffline = localPrefs.getBoolean("offline_mode_active", false); - - Log.d(TAG, "m_isOffline=" + isOffline); setContentView(R.layout.activity_login); @@ -244,115 +223,9 @@ public class OnlineActivity extends CommonActivity { setSupportActionBar(toolbar); m_cmgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - - Intent intent = getIntent(); - - Log.d(TAG, "intent action=" + intent.getAction()); - - if (OfflineDownloadService.INTENT_ACTION_CANCEL.equals(intent.getAction())) { - - Intent serviceIntent = new Intent( - OnlineActivity.this, - OfflineDownloadService.class); - - stopService(serviceIntent); - - serviceIntent = new Intent(); - serviceIntent.setAction(ImageCacheService.INTENT_ACTION_ICS_STOP); - serviceIntent.addCategory(Intent.CATEGORY_DEFAULT); - sendBroadcast(serviceIntent); - - } else if (OfflineDownloadService.INTENT_ACTION_SWITCH_OFFLINE.equals(intent.getAction())) { - isOffline = true; - } - - if (isOffline) { - switchOfflineSuccess(); - } else { - m_headlinesActionModeCallback = new HeadlinesActionModeCallback(); - } - } - - protected void switchOffline() { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(R.string.dialog_offline_switch_prompt) - .setPositiveButton(R.string.dialog_offline_go, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - if (getSessionId() != null) { - Log.d(TAG, "offline: starting"); - - Intent intent = new Intent( - OnlineActivity.this, - OfflineDownloadService.class); - intent.putExtra("sessionId", getSessionId()); - - startService(intent); - } - } - }) - .setNegativeButton(R.string.dialog_cancel, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - // - } - }); - - Dialog dlg = builder.create(); - dlg.show(); - } - - - @Override - public void onPause() { - super.onPause(); - - try { - unregisterReceiver(m_broadcastReceiver); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); + m_headlinesActionModeCallback = new HeadlinesActionModeCallback(); } - private void syncOfflineData() { - Log.d(TAG, "offlineSync: starting"); - - Intent intent = new Intent( - OnlineActivity.this, - OfflineUploadService.class); - - intent.putExtra("sessionId", getSessionId()); - - startService(intent); - } - - private void switchOfflineSuccess() { - logout(); - // setLoadingStatus(R.string.blank, false); - - SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = localPrefs.edit(); - editor.putBoolean("offline_mode_active", true); - editor.apply(); - - Intent offline = new Intent(OnlineActivity.this, OfflineActivity.class); - offline.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - offline.putExtra("initial", true); - - startActivity(offline); - - finish(); - } - public void login() { login(false, null); } @@ -415,9 +288,6 @@ public class OnlineActivity extends CommonActivity { startActivityForResult(intent, 0); overridePendingTransition(0, 0); - if (getDatabaseHelper().hasPendingOfflineData()) - syncOfflineData(); - finish(); } @@ -541,16 +411,10 @@ public class OnlineActivity extends CommonActivity { displayAttachments(article); } return true; - /*case R.id.logout: - logout(); - return true;*/ } else if (itemId == R.id.login) { login(); return true; - /*case R.id.go_offline: - switchOffline(); - return true;*/ - } else if (itemId == R.id.article_set_note) { + } else if (itemId == R.id.article_edit_note) { if (ap != null && ap.getSelectedArticle() != null) { editArticleNote(ap.getSelectedArticle()); } @@ -859,7 +723,7 @@ public class OnlineActivity extends CommonActivity { } return true; - } else if (itemId == R.id.set_labels) { + } else if (itemId == R.id.article_set_labels) { if (ap != null && ap.getSelectedArticle() != null) { if (getApiLevel() != 7) { editArticleLabels(ap.getSelectedArticle()); @@ -937,27 +801,24 @@ public class OnlineActivity extends CommonActivity { } */ public void editArticleNote(final Article article) { - String note = ""; - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) .setTitle(article.title); final EditText topicEdit = new EditText(this); - topicEdit.setText(note); + topicEdit.setText(article.note); builder.setView(topicEdit); - builder.setPositiveButton(R.string.article_set_note, new Dialog.OnClickListener() { + builder.setPositiveButton(R.string.article_edit_note, new Dialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { String note = topicEdit.getText().toString().trim(); saveArticleNote(article, note); - article.published = true; - article.note = note; - - saveArticlePublished(article); - + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); if (hf != null) hf.notifyUpdated(); + + ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + if (ap != null) ap.notifyUpdated(); } }); @@ -1058,42 +919,12 @@ public class OnlineActivity extends CommonActivity { protected void loginFailure() { setSessionId(null); initMenu(); - - if (getDatabaseHelper().hasOfflineData()) { - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(R.string.dialog_offline_prompt) - .setPositiveButton(R.string.dialog_offline_go, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - switchOfflineSuccess(); - } - }) - .setNegativeButton(R.string.dialog_cancel, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - // - } - }); - - Dialog dlg = builder.create(); - dlg.show(); - } } @Override public void onResume() { super.onResume(); - IntentFilter filter = new IntentFilter(); - //filter.addAction(OfflineDownloadService.INTENT_ACTION_SUCCESS); - filter.addAction(OfflineUploadService.INTENT_ACTION_SUCCESS); - filter.addCategory(Intent.CATEGORY_DEFAULT); - - registerReceiver(m_broadcastReceiver, filter); - if (getSessionId() == null) { login(); } else { @@ -1130,7 +961,7 @@ public class OnlineActivity extends CommonActivity { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { //toast(R.string.article_set_unread); - initMenu(); + invalidateOptionsMenu(); } }; @@ -1204,7 +1035,7 @@ public class OnlineActivity extends CommonActivity { public void saveArticleNote(final Article article, final String note) { ApiRequest req = new ApiRequest(getApplicationContext()) { protected void onPostExecute(JsonElement result) { - // + article.note = note; } }; @@ -1413,64 +1244,54 @@ public class OnlineActivity extends CommonActivity { } // this may be called after activity has been destroyed (i.e. long asynctask) - // might as well prevent null pointers if menu items are missing protected void initMenu() { - try { - if (m_menu != null) { - if (getSessionId() != null) { - m_menu.setGroupVisible(R.id.menu_group_logged_in, true); - m_menu.setGroupVisible(R.id.menu_group_logged_out, false); - } else { - m_menu.setGroupVisible(R.id.menu_group_logged_in, false); - 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); + if (m_menu != null) { + if (getSessionId() != null) { + m_menu.setGroupVisible(R.id.menu_group_logged_in, true); + m_menu.setGroupVisible(R.id.menu_group_logged_out, false); + } else { + m_menu.setGroupVisible(R.id.menu_group_logged_in, false); + m_menu.setGroupVisible(R.id.menu_group_logged_out, true); + } - m_menu.findItem(R.id.set_labels).setEnabled(getApiLevel() >= 1); - m_menu.findItem(R.id.article_set_note).setEnabled(getApiLevel() >= 1); - m_menu.findItem(R.id.subscribe_to_feed).setEnabled(getApiLevel() >= 5); + 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); - MenuItem search = m_menu.findItem(R.id.search); - search.setEnabled(getApiLevel() >= 2); + m_menu.findItem(R.id.subscribe_to_feed).setEnabled(getApiLevel() >= 5); - ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); + MenuItem search = m_menu.findItem(R.id.search); + search.setEnabled(getApiLevel() >= 2); - if (ap != null) { - Article article = ap.getSelectedArticle(); + ArticlePager ap = (ArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - if (article != null) { - m_menu.findItem(R.id.toggle_marked).setIcon(article.marked ? R.drawable.baseline_star_24 : - R.drawable.baseline_star_outline_24); + if (ap != null) { + Article article = ap.getSelectedArticle(); - m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.baseline_check_box_24 : - R.drawable.baseline_rss_feed_24); + if (article != null) { + m_menu.findItem(R.id.toggle_marked).setIcon(article.marked ? R.drawable.baseline_star_24 : + R.drawable.baseline_star_outline_24); - m_menu.findItem(R.id.toggle_unread).setIcon(article.unread ? R.drawable.baseline_mark_as_unread_24 : - R.drawable.baseline_email_24); - } + m_menu.findItem(R.id.toggle_published).setIcon(article.published ? R.drawable.baseline_check_box_24 : + R.drawable.baseline_rss_feed_24); } + } - HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - if (hf != null && !m_forceDisableActionMode) { - if (hf.getSelectedArticles().size() > 0) { - if (m_headlinesActionMode == null) { - m_headlinesActionMode = startSupportActionMode(m_headlinesActionModeCallback); - } + HeadlinesFragment hf = (HeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - m_headlinesActionMode.setTitle(String.valueOf(hf.getSelectedArticles().size())); - } else if (hf.getSelectedArticles().size() == 0 && m_headlinesActionMode != null) { - m_headlinesActionMode.finish(); + if (hf != null && !m_forceDisableActionMode) { + if (hf.getSelectedArticles().size() > 0) { + if (m_headlinesActionMode == null) { + m_headlinesActionMode = startSupportActionMode(m_headlinesActionModeCallback); } - } else if (m_forceDisableActionMode && m_headlinesActionMode != null) { + + m_headlinesActionMode.setTitle(String.valueOf(hf.getSelectedArticles().size())); + } else if (hf.getSelectedArticles().size() == 0 && m_headlinesActionMode != null) { m_headlinesActionMode.finish(); } + } else if (m_forceDisableActionMode && m_headlinesActionMode != null) { + m_headlinesActionMode.finish(); } - } catch (NullPointerException e) { - e.printStackTrace(); } } 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 af4c6b44..6e4c10f5 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 @@ -6,7 +6,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; -import androidx.activity.EdgeToEdge; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.Preference; 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 1a7c6786..91e0ecf8 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 @@ -1,18 +1,32 @@ package org.fox.ttrss.glide; -import java.io.*; -import java.util.*; - import android.content.Context; -import android.os.*; +import android.os.Handler; +import android.os.Looper; -import com.bumptech.glide.*; +import com.bumptech.glide.Glide; +import com.bumptech.glide.GlideBuilder; import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.module.GlideModule; -import okhttp3.*; -import okio.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSource; +import okio.ForwardingSource; +import okio.Okio; +import okio.Source; public class OkHttpProgressGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { 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 da4a9d86..235acc3c 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 @@ -2,13 +2,13 @@ package org.fox.ttrss.glide; import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; + import com.bumptech.glide.request.Request; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SizeReadyCallback; import com.bumptech.glide.request.target.Target; -import androidx.annotation.NonNull; - public class WrappingTarget<Z> implements Target<Z> { protected final @NonNull Target<? super Z> target; public WrappingTarget(@NonNull Target<? super Z> target) { diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineActivity.java deleted file mode 100755 index 18395778..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineActivity.java +++ /dev/null @@ -1,845 +0,0 @@ -package org.fox.ttrss.offline; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; -import android.net.Uri; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.EditText; - -import androidx.appcompat.view.ActionMode; -import androidx.appcompat.widget.Toolbar; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.fox.ttrss.CommonActivity; -import org.fox.ttrss.OnlineActivity; -import org.fox.ttrss.PreferencesActivity; -import org.fox.ttrss.R; - -public class OfflineActivity extends CommonActivity { - private final String TAG = this.getClass().getSimpleName(); - - protected SharedPreferences m_prefs; - protected Menu m_menu; - - private ActionMode m_headlinesActionMode; - private HeadlinesActionModeCallback m_headlinesActionModeCallback; - - private String m_lastImageHitTestUrl; - - public String getFeedTitle(int feedId, boolean isCat) { - try { - SQLiteStatement stmt; - - if (isCat) { - stmt = getDatabase().compileStatement( - "SELECT title FROM categories " + "WHERE " + BaseColumns._ID + " = ?"); - } else { - stmt = getDatabase().compileStatement( - "SELECT title FROM feeds " + "WHERE " + BaseColumns._ID + " = ?"); - } - - stmt.bindLong(1, feedId); - String title = stmt.simpleQueryForString(); - - stmt.close(); - - return title; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - @SuppressLint("NewApi") - 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; - deselectAllArticles(); - } - - @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 onContextItemSelected(android.view.MenuItem item) { - /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); */ - - final OfflineArticlePager ap = (OfflineArticlePager)getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - int itemId = item.getItemId(); - if (itemId == R.id.article_img_open) { - if (getLastContentImageHitTestUrl() != null) { - try { - openUri(Uri.parse(getLastContentImageHitTestUrl())); - } catch (Exception e) { - e.printStackTrace(); - toast(R.string.error_other_error); - } - } - return true; - } else if (itemId == R.id.article_img_copy) { - if (getLastContentImageHitTestUrl() != null) { - copyToClipboard(getLastContentImageHitTestUrl()); - } - return true; - } else if (itemId == R.id.article_img_share) { - if (getLastContentImageHitTestUrl() != null) { - shareImageFromUri(getLastContentImageHitTestUrl()); - } - return true; - } else if (itemId == R.id.article_img_share_url) { - if (getLastContentImageHitTestUrl() != null) { - shareText(getLastContentImageHitTestUrl()); - } - return true; - } else if (itemId == R.id.article_img_view_caption) { - if (getLastContentImageHitTestUrl() != null) { - - String content = ""; - - Cursor article = getArticleById(ap.getSelectedArticleId()); - - if (article != null) { - content = article.getString(article.getColumnIndex("content")); - article.close(); - } - - displayImageCaption(getLastContentImageHitTestUrl(), content); - } - return true; - } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - - } - - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - setAppTheme(m_prefs); - - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_login); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - invalidateOptionsMenu(); - - Intent intent = getIntent(); - - if (intent.getExtras() != null) { - if (intent.getBooleanExtra("initial", false)) { - intent = new Intent(OfflineActivity.this, OfflineMasterActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - - startActivityForResult(intent, 0); - finish(); - } - } - - /* if (savedInstanceState != null) { - - } */ - - m_headlinesActionModeCallback = new HeadlinesActionModeCallback(); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - } - - protected void selectArticles(int feedId, boolean isCat, int mode) { - switch (mode) { - case 0: - SQLiteStatement stmtSelectAll = null; - - if (isCat) { - stmtSelectAll = getDatabase().compileStatement( - "UPDATE articles SET selected = 1 WHERE feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)"); - } else { - stmtSelectAll = getDatabase().compileStatement( - "UPDATE articles SET selected = 1 WHERE feed_id = ?"); - } - - stmtSelectAll.bindLong(1, feedId); - stmtSelectAll.execute(); - stmtSelectAll.close(); - - break; - case 1: - - SQLiteStatement stmtSelectUnread = null; - - if (isCat) { - stmtSelectUnread = getDatabase().compileStatement( - "UPDATE articles SET selected = 1 WHERE feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?) AND unread = 1"); - } else { - stmtSelectUnread = getDatabase().compileStatement( - "UPDATE articles SET selected = 1 WHERE feed_id = ? AND unread = 1"); - } - - stmtSelectUnread.bindLong(1, feedId); - stmtSelectUnread.execute(); - stmtSelectUnread.close(); - - break; - case 2: - deselectAllArticles(); - break; - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - final OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_HEADLINES); - - /* final OfflineFeedsFragment off = (OfflineFeedsFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_FEEDS); */ - - /* final OfflineFeedCategoriesFragment ocf = (OfflineFeedCategoriesFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_CATS); */ - - final OfflineArticlePager oap = (OfflineArticlePager) getSupportFragmentManager() - .findFragmentByTag(FRAG_ARTICLE); - - int itemId = item.getItemId();/* case android.R.id.home: - finish(); - return true; *//* case R.id.headlines_toggle_sidebar: - if (true && !isSmallScreen()) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putBoolean("headlines_hide_sidebar", !m_prefs.getBoolean("headlines_hide_sidebar", false)); - editor.commit(); - - if (ohf != null && ohf.isAdded()) { - ohf.getView().setVisibility(m_prefs.getBoolean("headlines_hide_sidebar", false) ? View.GONE : View.VISIBLE); - } - } - return true; *//*case R.id.go_online: - switchOnline(); - return true;*/ - if (itemId == R.id.search) { - if (ohf != null) { - Dialog dialog = new Dialog(this); - - final EditText edit = new EditText(this); - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(R.string.search) - .setPositiveButton(getString(R.string.search), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - - String query = edit.getText().toString().trim(); - - ohf.setSearchQuery(query); - - } - }) - .setNegativeButton(getString(R.string.cancel), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - - // - - } - }).setView(edit); - - dialog = builder.create(); - dialog.show(); - } - - return true; - } else if (itemId == R.id.preferences) { - Intent intent = new Intent(this, PreferencesActivity.class); - startActivityForResult(intent, 0); - return true; - } else if (itemId == R.id.headlines_view_mode) { - if (ohf != null) { - Dialog dialog = new Dialog(this); - - String viewMode = getViewMode(); - - //Log.d(TAG, "viewMode:" + getViewMode()); - - int selectedIndex = 0; - - if (viewMode.equals("all_articles")) { - selectedIndex = 0; - } else if (viewMode.equals("marked")) { - selectedIndex = 1; - } else if (viewMode.equals("published")) { - selectedIndex = 2; - } else if (viewMode.equals("unread")) { - selectedIndex = 3; - } - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(R.string.headlines_set_view_mode) - .setSingleChoiceItems( - new String[]{ - /* getString(R.string.headlines_adaptive), */ - getString(R.string.headlines_all_articles), - getString(R.string.headlines_starred), - getString(R.string.headlines_published), - getString(R.string.headlines_unread)}, - selectedIndex, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int which) { - switch (which) { - /* case 0: - setViewMode("adaptive"); - break; */ - case 0: - setViewMode("all_articles"); - break; - case 1: - setViewMode("marked"); - break; - case 2: - setViewMode("published"); - break; - case 3: - setViewMode("unread"); - break; - } - dialog.cancel(); - - refresh(); - } - }); - - dialog = builder.create(); - dialog.show(); - - } - return true; - } else if (itemId == R.id.headlines_select) { - if (ohf != null) { - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(R.string.headlines_select_dialog) - .setSingleChoiceItems(new String[]{ - getString(R.string.headlines_select_all), - getString(R.string.headlines_select_unread), - getString(R.string.headlines_select_none)}, 0, - new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int which) { - - selectArticles(ohf.getFeedId(), ohf.getFeedIsCat(), which); - invalidateOptionsMenu(); - refresh(); - - dialog.cancel(); - } - }); - - Dialog dialog = builder.create(); - dialog.show(); - } - return true; - } else if (itemId == R.id.headlines_mark_as_read) { - if (ohf != null) { - final int feedId = ohf.getFeedId(); - final boolean isCat = ohf.getFeedIsCat(); - - int count = getUnreadArticleCount(feedId, isCat); - - if (count > 0) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(getResources().getQuantityString(R.plurals.mark_num_headlines_as_read, count, count)) - .setPositiveButton(R.string.catchup, - new OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - catchupFeed(feedId, isCat); - - } - }) - .setNegativeButton(R.string.dialog_cancel, - new OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - } - }); - - Dialog dlg = builder.create(); - dlg.show(); - } - } - return true; - } else if (itemId == R.id.share_article) { - int articleId = oap.getSelectedArticleId(); - - shareArticle(articleId); - - return true; - } else if (itemId == R.id.toggle_marked) { - if (oap != null) { - int articleId = oap.getSelectedArticleId(); - - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, modified_marked = 1, marked = NOT marked WHERE " - + BaseColumns._ID + " = ?"); - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.toggle_unread) { - if (oap != null) { - int articleId = oap.getSelectedArticleId(); - - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = NOT unread WHERE " - + BaseColumns._ID + " = ?"); - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - /* case R.id.selection_select_none: - deselectAllArticles(); - return true; */ - } else if (itemId == R.id.selection_toggle_unread) { - if (getSelectedArticleCount() > 0) { - SQLiteStatement stmt = getDatabase() - .compileStatement( - "UPDATE articles SET modified = 1, unread = NOT unread WHERE selected = 1"); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.selection_toggle_marked) { - if (getSelectedArticleCount() > 0) { - SQLiteStatement stmt = getDatabase() - .compileStatement( - "UPDATE articles SET modified = 1, modified_marked = 1, marked = NOT marked WHERE selected = 1"); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.selection_toggle_published) { - if (getSelectedArticleCount() > 0) { - SQLiteStatement stmt = getDatabase() - .compileStatement( - "UPDATE articles SET modified = 1, modified_published = 1, published = NOT published WHERE selected = 1"); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.toggle_published) { - if (oap != null) { - int articleId = oap.getSelectedArticleId(); - - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, modified_published = 1, published = NOT published WHERE " - + BaseColumns._ID + " = ?"); - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.catchup_above) { - if (oap != null) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setMessage(R.string.confirm_catchup_above) - .setPositiveButton(R.string.dialog_ok, - new OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - catchupAbove(oap); - - } - }) - .setNegativeButton(R.string.dialog_cancel, - new OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - } - }); - - Dialog dlg = builder.create(); - dlg.show(); - } - return true; - } - Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId()); - return super.onOptionsItemSelected(item); - } - - private void catchupAbove(OfflineArticlePager oap) { - int articleId = oap.getSelectedArticleId(); - int feedId = oap.getFeedId(); - boolean isCat = oap.getFeedIsCat(); - - SQLiteStatement stmt = null; - - if (isCat) { - stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE " + - "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " + - "AND feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)"); - } else { - stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE " + - "updated >= (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " + - "AND feed_id = ?"); - } - - stmt.bindLong(1, articleId); - stmt.bindLong(2, feedId); - stmt.execute(); - stmt.close(); - - refresh(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.activity_offline, menu); - - m_menu = menu; - - initMenu(); - - return true; - } - - @SuppressLint("NewApi") - protected void initMenu() { - if (m_menu != null) { - 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); - - OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - if (hf != null) { - if (hf.getSelectedArticleCount() > 0 && m_headlinesActionMode == null) { - m_headlinesActionMode = startSupportActionMode(m_headlinesActionModeCallback); - } else if (hf.getSelectedArticleCount() == 0 && m_headlinesActionMode != null) { - m_headlinesActionMode.finish(); - } - } - - OfflineArticlePager ap = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - if (ap != null) { - int articleId = ap.getSelectedArticleId(); - - Cursor article = getArticleById(articleId); - - if (article != null) { - boolean unread = article.getInt(article.getColumnIndex("unread")) == 1; - boolean marked = article.getInt(article.getColumnIndex("marked")) == 1; - boolean published = article.getInt(article.getColumnIndex("published")) == 1; - - m_menu.findItem(R.id.toggle_marked).setIcon(marked ? R.drawable.baseline_star_24 : - R.drawable.baseline_star_outline_24); - - m_menu.findItem(R.id.toggle_published).setIcon(published ? R.drawable.baseline_check_box_24 : - R.drawable.baseline_rss_feed_24); - - m_menu.findItem(R.id.toggle_unread).setIcon(unread ? R.drawable.baseline_mark_as_unread_24 : - R.drawable.baseline_email_24); - - article.close(); - } - } - } - } - - public void switchOnline() { - SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = localPrefs.edit(); - editor.putBoolean("offline_mode_active", false); - editor.apply(); - - Intent refresh = new Intent(this, OnlineActivity.class); - refresh.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - - startActivity(refresh); - - finish(); - } - - protected Cursor getArticleById(int articleId) { - Cursor c = getDatabase().query("articles", null, - BaseColumns._ID + "=?", - new String[]{String.valueOf(articleId)}, null, null, null); - - c.moveToFirst(); - - return c; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (m_prefs.getBoolean("use_volume_keys", false)) { - OfflineArticlePager ap = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - if (ap != null && ap.isAdded()) { - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_UP: - ap.selectArticle(false); - return true; - case KeyEvent.KEYCODE_VOLUME_DOWN: - ap.selectArticle(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); - } - - protected Cursor getFeedById(int feedId) { - Cursor c = getDatabase().query("feeds", null, - BaseColumns._ID + "=?", - new String[]{String.valueOf(feedId)}, null, null, null); - - c.moveToFirst(); - - return c; - } - - protected Cursor getCatById(int catId) { - Cursor c = getDatabase().query("categories", null, - BaseColumns._ID + "=?", - new String[]{String.valueOf(catId)}, null, null, null); - - c.moveToFirst(); - - return c; - } - - protected Intent getShareIntent(Cursor article) { - if (article != null) { - String title = article.getString(article.getColumnIndex("title")); - String link = article.getString(article.getColumnIndex("link")); - - return getShareIntent(link, title); - } else { - return null; - } - } - - protected void shareArticle(int articleId) { - - Cursor article = getArticleById(articleId); - - if (article != null) { - shareArticle(article); - article.close(); - } - } - - private void shareArticle(Cursor article) { - if (article != null) { - Intent intent = getShareIntent(article); - - startActivity(Intent.createChooser(intent, - getString(R.string.share_article))); - } - } - - protected int getSelectedArticleCount() { - Cursor c = getDatabase().query("articles", - new String[]{"COUNT(*)"}, "selected = 1", null, null, null, - null); - c.moveToFirst(); - int selected = c.getInt(0); - c.close(); - - return selected; - } - - protected int getUnreadArticleCount(int feedId, boolean isCat) { - - Cursor c; - - if (isCat) { - c = getDatabase().query("articles", - new String[] { "COUNT(*)" }, "unread = 1 AND feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)", - new String[] { String.valueOf(feedId) }, - null, null, null); - } else { - c = getDatabase().query("articles", - new String[] { "COUNT(*)" }, "unread = 1 AND feed_id = ?", - new String[] { String.valueOf(feedId) }, - null, null, null); - } - - c.moveToFirst(); - int selected = c.getInt(0); - c.close(); - - return selected; - } - - protected void deselectAllArticles() { - getDatabase().execSQL("UPDATE articles SET selected = 0 "); - refresh(); - } - - protected void refresh() { - OfflineFeedsFragment ff = (OfflineFeedsFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_FEEDS); - - if (ff != null) { - ff.refresh(); - } - - OfflineFeedCategoriesFragment cf = (OfflineFeedCategoriesFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_CATS); - - if (cf != null) { - cf.refresh(); - } - - OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment) getSupportFragmentManager() - .findFragmentByTag(FRAG_HEADLINES); - - if (ohf != null) { - ohf.refresh(); - } - - invalidateOptionsMenu(); - } - - public void catchupFeed(int feedId, boolean isCat) { - if (isCat) { - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE feed_id IN (SELECT " + - BaseColumns._ID + " FROM feeds WHERE cat_id = ?)"); - stmt.bindLong(1, feedId); - stmt.execute(); - stmt.close(); - } else { - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE feed_id = ?"); - stmt.bindLong(1, feedId); - stmt.execute(); - stmt.close(); - } - - refresh(); - } - - public void setLastContentImageHitTestUrl(String url) { - m_lastImageHitTestUrl = url; - } - - public String getLastContentImageHitTestUrl() { - return m_lastImageHitTestUrl; - } - - public void setViewMode(String viewMode) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putString("offline_view_mode", viewMode); - editor.apply(); - } - - /* public boolean getOldestFirst() { - return getSortMode().equals("oldest_first"); - } - - public String getSortMode() { - return m_prefs.getString("offline_headlines_sort_mode", "default"); - } - - public void setSortMode(String sortMode) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putString("offline_headlines_sort_mode", sortMode); - editor.commit(); - } */ - - public String getViewMode() { - return m_prefs.getString("offline_view_mode", "adaptive"); - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java deleted file mode 100755 index dfc47131..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticleFragment.java +++ /dev/null @@ -1,505 +0,0 @@ -package org.fox.ttrss.offline; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.graphics.Color; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebView.HitTestResult; -import android.webkit.WebViewClient; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -import org.fox.ttrss.R; -import org.fox.ttrss.util.ImageCacheService; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.net.MalformedURLException; -import java.net.URL; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -import androidx.fragment.app.Fragment; - -import com.google.android.material.button.MaterialButton; - -public class OfflineArticleFragment extends Fragment { - private final String TAG = this.getClass().getSimpleName(); - - private SharedPreferences m_prefs; - private int m_articleId; - private boolean m_isCat = false; // FIXME use - private WebView m_web; - private Cursor m_cursor; - private OfflineDetailActivity m_activity; - - protected View m_customView; - protected FrameLayout m_customViewContainer; - protected View m_contentView; - protected FSVideoChromeClient m_chromeClient; - //protected View m_fab; - - public void initialize(int articleId) { - m_articleId = articleId; - } - - private class FSVideoChromeClient extends WebChromeClient { - //protected View m_videoChildView; - - private CustomViewCallback m_callback; - - public FSVideoChromeClient(View container) { - super(); - - } - - @Override - public void onShowCustomView(View view, CustomViewCallback callback) { - m_activity.getSupportActionBar().hide(); - - // if a view already exists then immediately terminate the new one - if (m_customView != null) { - callback.onCustomViewHidden(); - return; - } - m_customView = view; - m_contentView.setVisibility(View.GONE); - - m_customViewContainer.setVisibility(View.VISIBLE); - m_customViewContainer.addView(view); - - //if (m_fab != null) m_fab.setVisibility(View.GONE); - - m_activity.showSidebar(false); - - m_callback = callback; - } - - @Override - public void onHideCustomView() { - super.onHideCustomView(); - - m_activity.getSupportActionBar().show(); - - if (m_customView == null) - return; - - m_contentView.setVisibility(View.VISIBLE); - m_customViewContainer.setVisibility(View.GONE); - - // Hide the custom view. - m_customView.setVisibility(View.GONE); - - // Remove the custom view from its container. - m_customViewContainer.removeView(m_customView); - m_callback.onCustomViewHidden(); - - /*if (m_fab != null && m_prefs.getBoolean("enable_article_fab", true)) - m_fab.setVisibility(View.VISIBLE);*/ - - m_customView = null; - - m_activity.showSidebar(true); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - /* AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); */ - - int itemId = item.getItemId(); - if (itemId == R.id.article_link_share) { - m_activity.shareArticle(m_articleId); - return true; - } else if (itemId == R.id.article_link_copy) { - if (true) { - Cursor article = m_activity.getArticleById(m_articleId); - - if (article != null) { - m_activity.copyToClipboard(article.getString(article.getColumnIndex("link"))); - article.close(); - } - } - return true; - } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - //getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); - //menu.setHeaderTitle(m_cursor.getString(m_cursor.getColumnIndex("title"))); - - String title = m_cursor.getString(m_cursor.getColumnIndex("title")); - - if (v.getId() == R.id.article_content) { - HitTestResult result = ((WebView)v).getHitTestResult(); - - if (result != null && (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); - - /* FIXME I have no idea how to do this correctly ;( */ - - m_activity.setLastContentImageHitTestUrl(result.getExtra()); - - } else { - menu.setHeaderTitle(title); - getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); - } - } else { - menu.setHeaderTitle(title); - getActivity().getMenuInflater().inflate(R.menu.context_article_link, menu); - } - - super.onCreateContextMenu(menu, v, menuInfo); - - } - - @SuppressLint("NewApi") - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - if (savedInstanceState != null) { - m_articleId = savedInstanceState.getInt("articleId"); - } - - View view = inflater.inflate(R.layout.fragment_article, container, false); - - m_cursor = m_activity.getDatabase().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")", - new String[] { "articles.*", "feeds.title AS feed_title" }, "articles." + BaseColumns._ID + "=?", - new String[] { String.valueOf(m_articleId) }, null, null, null); - - m_cursor.moveToFirst(); - - if (m_cursor.isFirst()) { - m_customViewContainer = view.findViewById(R.id.article_fullscreen_video); - - final String link = m_cursor.getString(m_cursor.getColumnIndex("link")); - - int articleFontSize = Integer.parseInt(m_prefs.getString("article_font_size_sp", "16")); - int articleSmallFontSize = Math.max(10, Math.min(18, articleFontSize - 2)); - - TextView title = view.findViewById(R.id.title); - - if (title != null) { - - title.setTextSize(TypedValue.COMPLEX_UNIT_SP, Math.min(21, articleFontSize + 3)); - - String titleStr; - - if (m_cursor.getString(m_cursor.getColumnIndex("title")).length() > 200) - titleStr = m_cursor.getString(m_cursor.getColumnIndex("title")).substring(0, 200) + "..."; - else - titleStr = m_cursor.getString(m_cursor.getColumnIndex("title")); - - title.setText(titleStr); - //title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - title.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - try { - m_activity.openUri(Uri.parse(link)); - } catch (Exception e) { - e.printStackTrace(); - m_activity.toast(R.string.error_other_error); - } - } - }); - - } - - /* MaterialButton score = view.findViewById(R.id.score); - - if (score != null) { - score.setVisibility(View.GONE); - } - - MaterialButton attachments = view.findViewById(R.id.attachments); - - if (attachments != null) { - attachments.setVisibility(View.GONE); - } - - MaterialButton share = view.findViewById(R.id.share); - - if (share != null) { - share.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - m_activity.shareArticle(m_articleId); - } - }); - } */ - - TextView comments = view.findViewById(R.id.comments); - - if (comments != null) { - comments.setVisibility(View.GONE); - } - - TextView note = view.findViewById(R.id.note); - - if (note != null) { - note.setVisibility(View.GONE); - } - - m_web = view.findViewById(R.id.article_content); - - if (m_web != null) { - - m_web.setWebViewClient(new WebViewClient() { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - try { - m_activity.openUri(Uri.parse(url)); - - return true; - - } catch (Exception e){ - e.printStackTrace(); - } - - return false; - } }); - - m_web.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - HitTestResult result = ((WebView) v).getHitTestResult(); - - if (result != null && (result.getType() == HitTestResult.IMAGE_TYPE || result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE)) { - registerForContextMenu(m_web); - m_activity.openContextMenu(m_web); - unregisterForContextMenu(m_web); - return true; - } else { - return false; - } - } - }); - - String content; - - WebSettings ws = m_web.getSettings(); - ws.setSupportZoom(false); - ws.setJavaScriptEnabled(false); - - m_chromeClient = new FSVideoChromeClient(getView()); - m_web.setWebChromeClient(m_chromeClient); - m_web.setBackgroundColor(Color.TRANSPARENT); - - ws.setMediaPlaybackRequiresUserGesture(true); - - // we need to show "insecure" file:// urls - if (m_prefs.getBoolean("offline_image_cache_enabled", false) && - android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - - ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - - TypedValue tvTextColor = new TypedValue(); - getActivity().getTheme().resolveAttribute(R.attr.colorOnSurface, tvTextColor, true); - - String textColor = String.format("#%06X", (0xFFFFFF & tvTextColor.data)); - - 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+";}"; - - String articleContent = m_cursor.getString(m_cursor.getColumnIndex("content")); - Document doc = Jsoup.parse(articleContent); - - if (doc != null) { - if (m_prefs.getBoolean("offline_image_cache_enabled", false)) { - - Elements images = doc.select("img,source"); - - for (Element img : images) { - String url = img.attr("src"); - - Log.d(TAG, "src=" + url); - - if (ImageCacheService.isUrlCached(m_activity, url)) { - img.attr("src", "file://" + ImageCacheService.getCacheFileName(m_activity, url)); - } - } - } - - // thanks webview for crashing on <video> tag - /*Elements videos = doc.select("video"); - - for (Element video : videos) - video.remove();*/ - - articleContent = doc.toString(); - } - - if (m_prefs.getBoolean("justify_article_text", true)) { - cssOverride += "body { text-align : justify; } "; - } - - ws.setDefaultFontSize(articleFontSize); - - content = - "<html>" + - "<head>" + - "<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 : 0px; line-height : 130%; }" + - "img,video { max-width : 100%; width : auto; height : auto; }" + - " table { width : 100%; }" + - cssOverride + - "</style>" + - "</head>" + - "<body>" + articleContent; - - content += "</body></html>"; - - try { - String baseUrl = null; - - try { - URL url = new URL(link); - baseUrl = url.getProtocol() + "://" + url.getHost(); - } catch (MalformedURLException e) { - // - } - - m_web.loadDataWithBaseURL(baseUrl, content, "text/html", "utf-8", null); - } catch (RuntimeException e) { - e.printStackTrace(); - } - - - } - - TextView dv = view.findViewById(R.id.date); - - if (dv != null) { - dv.setTextSize(TypedValue.COMPLEX_UNIT_SP, articleSmallFontSize); - - Date d = new Date(m_cursor.getInt(m_cursor.getColumnIndex("updated")) * 1000L); - DateFormat df = new SimpleDateFormat("MMM dd, HH:mm"); - dv.setText(df.format(d)); - } - - TextView tagv = view.findViewById(R.id.tags); - - if (tagv != null) { - tagv.setTextSize(TypedValue.COMPLEX_UNIT_SP, articleSmallFontSize); - - int feedTitleIndex = m_cursor.getColumnIndex("feed_title"); - - if (feedTitleIndex != -1 /* && m_isCat */) { - String fTitle = m_cursor.getString(feedTitleIndex); - - int authorIndex = m_cursor.getColumnIndex("author"); - - if (authorIndex >= 0) { - String authorStr = m_cursor.getString(authorIndex); - - if (authorStr != null && authorStr.length() > 0) { - fTitle += " (" + getString(R.string.author_formatted, m_cursor.getString(authorIndex)) + ")"; - } - } - - tagv.setText(fTitle); - } else { - String tagsStr = m_cursor.getString(m_cursor.getColumnIndex("tags")); - tagv.setText(tagsStr); - } - } - - } - - return view; - } - - @Override - public void onPause() { - super.onPause(); - - if (m_web != null) m_web.onPause(); - } - - public boolean inCustomView() { - return (m_customView != null); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - m_cursor.close(); - } - - public void hideCustomView() { - if (m_chromeClient != null) { - m_chromeClient.onHideCustomView(); - } - } - - @Override - public void onResume() { - super.onResume(); - - if (m_web != null) m_web.onResume(); - } - - @Override - public void onStop() { - super.onStop(); - - if (inCustomView()) { - hideCustomView(); - } - } - @Override - public void onSaveInstanceState (Bundle out) { - super.onSaveInstanceState(out); - - out.putInt("articleId", m_articleId); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - - m_activity = (OfflineDetailActivity) activity; - - } -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticlePager.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticlePager.java deleted file mode 100755 index ce4f6f72..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineArticlePager.java +++ /dev/null @@ -1,288 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.Bundle; -import android.os.Parcelable; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; - -import org.fox.ttrss.R; - -public class OfflineArticlePager extends Fragment { - private final String TAG = this.getClass().getSimpleName(); - - private PagerAdapter m_adapter; - private OfflineActivity m_activity; - private OfflineHeadlinesEventListener m_listener; - private boolean m_isCat; - private int m_feedId; - private int m_articleId; - private String m_searchQuery = ""; - private Cursor m_cursor; - private SharedPreferences m_prefs; - - public int getFeedId() { - return m_feedId; - } - - public boolean getFeedIsCat() { - return m_isCat; - } - - public Cursor createCursor() { - String feedClause = null; - - if (m_isCat) { - feedClause = "feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)"; - } else { - feedClause = "feed_id = ?"; - } - - String viewMode = m_activity.getViewMode(); - - if ("adaptive".equals(viewMode)) { - // TODO: implement adaptive - } else if ("marked".equals(viewMode)) { - feedClause += "AND (marked = 1)"; - } else if ("published".equals(viewMode)) { - feedClause += "AND (published = 1)"; - } else if ("unread".equals(viewMode)) { - feedClause += "AND (unread = 1)"; - } else { // all_articles - // - } - - String orderBy = (m_prefs.getBoolean("offline_oldest_first", false)) ? "updated" : "updated DESC"; - - if (m_searchQuery == null || m_searchQuery.equals("")) { - return m_activity.getDatabase().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")", - new String[] { "articles."+BaseColumns._ID, "feeds.title AS feed_title" }, feedClause, - new String[] { String.valueOf(m_feedId) }, null, null, orderBy); - } else { - return m_activity.getDatabase().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")", - new String[] { "articles."+BaseColumns._ID }, - feedClause + " AND (articles.title LIKE '%' || ? || '%' OR content LIKE '%' || ? || '%')", - new String[] { String.valueOf(m_feedId), m_searchQuery, m_searchQuery }, null, null, orderBy); - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - } - - private class PagerAdapter extends FragmentStatePagerAdapter { - public PagerAdapter(FragmentManager fm) { - super(fm); - } - - // workaround for possible TransactionTooLarge exception on 8.0+ - // we don't need to save member state anyway, bridge takes care of it - @Override - public Parcelable saveState() { - Bundle bundle = (Bundle) super.saveState(); - - if (bundle != null) - bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out - - return bundle; - } - - @Override - public Fragment getItem(int position) { - Log.d(TAG, "getItem: " + position); - - if (m_cursor.moveToPosition(position)) { - - OfflineArticleFragment oaf = new OfflineArticleFragment(); - oaf.initialize(m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID))); - - return oaf; - } - - return null; - } - - @Override - public int getCount() { - return m_cursor.getCount(); - } - } - - @Override - public void onResume() { - super.onResume(); - } - - public void initialize(int articleId, int feedId, boolean isCat) { - m_feedId = feedId; - m_isCat = isCat; - m_articleId = articleId; - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_article_pager, container, false); - - if (savedInstanceState != null) { - m_articleId = savedInstanceState.getInt("articleId", 0); - m_feedId = savedInstanceState.getInt("feedId", 0); - m_isCat = savedInstanceState.getBoolean("isCat", false); - } - - Log.d(TAG, "feed=" + m_feedId + "; iscat=" + m_isCat); - - m_cursor = createCursor(); - - m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager()); - - int position = 0; - - Log.d(TAG, "maId=" + m_articleId); - - if (m_articleId != 0) { - if (m_cursor.moveToFirst()) { - - while (!m_cursor.isAfterLast()) { - if (m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)) == m_articleId) { - position = m_cursor.getPosition(); - break; - } - m_cursor.moveToNext(); - } - - Log.d(TAG, "(1)maId=" + m_articleId); - m_listener.onArticleSelected(m_articleId, false); - } - } else { - if (m_cursor.moveToFirst()) { - m_articleId = m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)); - m_listener.onArticleSelected(m_articleId, false); - - Log.d(TAG, "(2)maId=" + m_articleId); - } - } - - - ViewPager pager = view.findViewById(R.id.article_pager); - - pager.setAdapter(m_adapter); - - pager.setCurrentItem(position); - pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrollStateChanged(int arg0) { - } - - @Override - public void onPageScrolled(int arg0, float arg1, int arg2) { - } - - @Override - public void onPageSelected(int position) { - if (m_cursor.moveToPosition(position)) { - int articleId = m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)); - - m_articleId = articleId; - m_listener.onArticleSelected(articleId, false); - } - } - }); - - return view; - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_activity = (OfflineActivity)activity; - m_listener = (OfflineHeadlinesEventListener)activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - - } - - public void refresh() { - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - - m_cursor = createCursor(); - - if (m_cursor != null) { - m_adapter.notifyDataSetChanged(); - } - } - - public int getSelectedArticleId() { - return m_articleId; - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putInt("articleId", m_articleId); - out.putInt("feedId", m_feedId); - out.putBoolean("isCat", m_isCat); - - } - - public void setSearchQuery(String searchQuery) { - m_searchQuery = searchQuery; - } - - public void setArticleId(int articleId) { - m_articleId = articleId; - - int position = getArticleIdPosition(articleId); - - ViewPager pager = getView().findViewById(R.id.article_pager); - - pager.setCurrentItem(position); - - } - - public int getArticleIdPosition(int articleId) { - m_cursor.moveToFirst(); - - while (!m_cursor.isAfterLast()) { - if (m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID)) == articleId) { - return m_cursor.getPosition(); - } - m_cursor.moveToNext(); - } - - return -1; - } - - public void selectArticle(boolean next) { - int position = getArticleIdPosition(m_articleId); - - if (position != -1) { - if (next) - position++; - else - position--; - - Log.d(TAG, "pos=" + position); - - if (m_cursor.moveToPosition(position)) { - setArticleId(m_cursor.getInt(m_cursor.getColumnIndex(BaseColumns._ID))); - } - } - } -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDetailActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDetailActivity.java deleted file mode 100755 index 1f8f3dc6..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDetailActivity.java +++ /dev/null @@ -1,267 +0,0 @@ -package org.fox.ttrss.offline; - -import android.annotation.SuppressLint; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; -import android.net.Uri; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -import org.fox.ttrss.Application; -import org.fox.ttrss.R; - -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.widget.Toolbar; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; - -public class OfflineDetailActivity extends OfflineActivity implements OfflineHeadlinesEventListener { - private final String TAG = this.getClass().getSimpleName(); - - protected SharedPreferences m_prefs; - - private ActionBarDrawerToggle m_drawerToggle; - private DrawerLayout m_drawerLayout; - private int m_activeArticleId; - - @SuppressLint("NewApi") - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - setAppTheme(m_prefs); - - super.onCreate(savedInstanceState); - - 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); - - m_drawerLayout = findViewById(R.id.headlines_drawer); - - if (m_drawerLayout != null) { - - m_drawerToggle = new ActionBarDrawerToggle(this, m_drawerLayout, R.string.blank, R.string.blank) { - @Override - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - - getSupportActionBar().show(); - - invalidateOptionsMenu(); - } - - @Override - public void onDrawerClosed(View drawerView) { - super.onDrawerClosed(drawerView); - - invalidateOptionsMenu(); - } - }; - - m_drawerLayout.addDrawerListener(m_drawerToggle); - m_drawerToggle.setDrawerIndicatorEnabled(true); - - } - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - - setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); - - if (isPortrait()) { - View headlines = findViewById(R.id.headlines_fragment); - - if (headlines != null) - headlines.setVisibility(View.GONE); - } - - if (savedInstanceState == null) { - Intent i = getIntent(); - - if (i.getExtras() != null) { - int feedId = i.getIntExtra("feed", 0); - boolean isCat = i.getBooleanExtra("isCat", false); - int articleId = i.getIntExtra("article", 0); - String searchQuery = i.getStringExtra("searchQuery"); - - OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(); - hf.initialize(feedId, isCat, true); - - OfflineArticlePager af = new OfflineArticlePager(); - af.initialize(articleId, feedId, isCat); - - hf.setActiveArticleId(articleId); - - hf.setSearchQuery(searchQuery); - af.setSearchQuery(searchQuery); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - - ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - ft.replace(R.id.article_fragment, af, FRAG_ARTICLE); - - ft.commit(); - - Cursor c; - - if (isCat) { - c = getCatById(feedId); - } else { - c = getFeedById(feedId); - } - - if (c != null) { - setTitle(c.getString(c.getColumnIndex("title"))); - c.close(); - } - - } - } - - FloatingActionButton fab = findViewById(R.id.detail_fab); - - if (fab != null && m_prefs.getBoolean("enable_article_fab", true)) { - fab.show(); - - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (m_activeArticleId != 0) { - Cursor article = getArticleById(m_activeArticleId); - - if (article != null) { - openUri(Uri.parse(article.getString(article.getColumnIndex("link")))); - - article.close(); - } - } - } - }); - } - - } - - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (m_drawerToggle != null) m_drawerToggle.syncState(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (m_drawerToggle != null && m_drawerToggle.onOptionsItemSelected(item)) { - return true; - } - - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } - Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId()); - return super.onOptionsItemSelected(item); - } - - @Override - public void onArticleSelected(int articleId, boolean open) { - - m_activeArticleId = articleId; - - if (!open) { - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 " + "WHERE " + BaseColumns._ID - + " = ?"); - - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - } - - if (open) { - if (m_drawerLayout != null) { - m_drawerLayout.closeDrawers(); - } - - OfflineArticlePager af = (OfflineArticlePager) getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - af.setArticleId(articleId); - } else { - OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - hf.setActiveArticleId(articleId); - } - - Application.getInstance().m_selectedArticleId = articleId; - - invalidateOptionsMenu(); - refresh(); - } - - @Override - protected void initMenu() { - super.initMenu(); - - if (m_menu != null) { - m_menu.setGroupVisible(R.id.menu_group_feeds, false); - - //OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - m_menu.setGroupVisible(R.id.menu_group_headlines, !isPortrait() && !isSmallScreen()); - //m_menu.findItem(R.id.headlines_toggle_sidebar).setVisible(!isPortrait() && !isSmallScreen()); - - Fragment af = getSupportFragmentManager().findFragmentByTag(FRAG_ARTICLE); - - m_menu.setGroupVisible(R.id.menu_group_article, af != null); - - m_menu.findItem(R.id.search).setVisible(false); - } - } - - @Override - public void onArticleSelected(int articleId) { - onArticleSelected(articleId, true); - } - - @Override - public void onBackPressed() { - try { - super.onBackPressed(); - } catch (IllegalStateException e) { - // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - e.printStackTrace(); - } - } - - @Override - public void onPause() { - super.onPause(); - - if (isFinishing()) { - overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); - } - - } - - public void showSidebar(boolean show) { - if (!isSmallScreen() && !isPortrait()) { - findViewById(R.id.headlines_fragment).setVisibility(show ? View.VISIBLE : View.GONE); - invalidateOptionsMenu(); - } - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDownloadService.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDownloadService.java deleted file mode 100755 index c3c31133..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineDownloadService.java +++ /dev/null @@ -1,594 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteStatement; -import android.graphics.BitmapFactory; -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.reflect.TypeToken; - -import org.fox.ttrss.ApiRequest; -import org.fox.ttrss.BuildConfig; -import org.fox.ttrss.CommonActivity; -import org.fox.ttrss.OnlineActivity; -import org.fox.ttrss.R; -import org.fox.ttrss.types.Article; -import org.fox.ttrss.types.Feed; -import org.fox.ttrss.types.FeedCategory; -import org.fox.ttrss.util.DatabaseHelper; -import org.fox.ttrss.util.ImageCacheService; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.List; - -import androidx.core.app.NotificationCompat; - -public class OfflineDownloadService extends Service { - - private final String TAG = this.getClass().getSimpleName(); - - // enable downloading read articles in debug configuration for testing - private static boolean OFFLINE_DEBUG_READ = false; - - public static final int NOTIFY_DOWNLOADING = 1; - public static final int NOTIFY_DOWNLOAD_SUCCESS = 2; - - public static final int PI_GENERIC = 0; - public static final int PI_CANCEL = 1; - public static final int PI_SUCCESS = 2; - - //public static final String INTENT_ACTION_SUCCESS = "org.fox.ttrss.intent.action.DownloadComplete"; - public static final String INTENT_ACTION_CANCEL = "org.fox.ttrss.intent.action.Cancel"; - public static final String INTENT_ACTION_SWITCH_OFFLINE = "org.fox.ttrss.intent.action.SwitchOffline"; - - private static final int OFFLINE_SYNC_SEQ = 50; - private static final int OFFLINE_SYNC_MAX = OFFLINE_SYNC_SEQ * 10; - - private int m_articleOffset = 0; - private String m_sessionId; - private NotificationManager m_nmgr; - - private boolean m_batchMode = false; - private boolean m_downloadInProgress = false; - private boolean m_downloadImages = false; - private int m_syncMax; - private SharedPreferences m_prefs; - private boolean m_canProceed = true; - - private final IBinder m_binder = new LocalBinder(); - private DatabaseHelper m_databaseHelper; - - public class LocalBinder extends Binder { - OfflineDownloadService getService() { - return OfflineDownloadService.this; - } - } - - @Override - public IBinder onBind(Intent intent) { - return m_binder; - } - - @Override - public void onCreate() { - super.onCreate(); - m_nmgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - m_downloadImages = m_prefs.getBoolean("offline_image_cache_enabled", false); - m_syncMax = Integer.parseInt(m_prefs.getString("offline_sync_max", String.valueOf(OFFLINE_SYNC_MAX))); - - initDatabase(); - } - - @SuppressWarnings("deprecation") - private void updateNotification(String msg, int progress, int max, boolean showProgress, boolean isError) { - Intent intent = new Intent(this, OnlineActivity.class); - - PendingIntent contentIntent = PendingIntent.getActivity(this, PI_GENERIC, - intent, PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) - .setContentText(msg) - .setContentTitle(getString(R.string.notify_downloading_title)) - .setContentIntent(contentIntent) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.baseline_cloud_download_24) - .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), - R.drawable.ic_launcher)) - .setOngoing(!isError) - .setOnlyAlertOnce(true); - - if (showProgress) builder.setProgress(max, progress, max == 0); - - intent = new Intent(this, OnlineActivity.class); - intent.setAction(INTENT_ACTION_CANCEL); - - PendingIntent cancelIntent = PendingIntent.getActivity(this, PI_CANCEL, intent, PendingIntent.FLAG_IMMUTABLE); - - builder.setCategory(Notification.CATEGORY_PROGRESS) - .setVibrate(new long[0]) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(0x88b0f0) - .setGroup("org.fox.ttrss") - .addAction(R.drawable.ic_launcher, getString(R.string.cancel), cancelIntent); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(CommonActivity.NOTIFICATION_CHANNEL_NORMAL); - } - - m_nmgr.notify(NOTIFY_DOWNLOADING, builder.build()); - } - - @SuppressWarnings("deprecation") - private void notifyDownloadComplete() { - Intent intent = new Intent(this, OnlineActivity.class); - - if (m_articleOffset > 0) { - intent.setAction(INTENT_ACTION_SWITCH_OFFLINE); - } - - PendingIntent contentIntent = PendingIntent.getActivity(this, PI_SUCCESS, - intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) - .setContentIntent(contentIntent) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.ic_notification) - .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), - R.drawable.ic_launcher)) - .setOnlyAlertOnce(true) - .setPriority(Notification.PRIORITY_HIGH) - .setDefaults(Notification.DEFAULT_ALL) - .setAutoCancel(true); - - if (m_articleOffset > 0) { - builder - .setContentTitle(getString(R.string.dialog_offline_success)) - .setContentText(getString(R.string.offline_tap_to_switch)); - } else { - builder - .setContentTitle(getString(R.string.offline_switch_failed)) - .setContentText(getString(R.string.offline_no_articles)); - - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - builder.setCategory(Notification.CATEGORY_MESSAGE) - .setVibrate(new long[0]) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(0x88b0f0) - .setGroup("org.fox.ttrss"); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(CommonActivity.NOTIFICATION_CHANNEL_PRIORITY); - } - - m_nmgr.notify(NOTIFY_DOWNLOAD_SUCCESS, builder.build()); - } - - private void updateNotification(int msgResId, int progress, int max, boolean showProgress, boolean isError) { - updateNotification(getString(msgResId), progress, max, showProgress, isError); - } - - private void downloadFailed() { - //m_nmgr.cancel(NOTIFY_DOWNLOADING); - - // TODO send notification to activity? - - m_downloadInProgress = false; - stopSelf(); - } - - private boolean isCacheServiceRunning() { - ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); - for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if ("org.fox.ttrss.util.ImageCacheService".equals(service.service.getClassName())) { - return true; - } - } - return false; - } - - public void downloadComplete() { - m_downloadInProgress = false; - - // if cache service is running, it will send a finished intent on its own - if (!isCacheServiceRunning()) { - m_nmgr.cancel(NOTIFY_DOWNLOADING); - - if (m_batchMode) { - - SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = localPrefs.edit(); - editor.putBoolean("offline_mode_active", true); - editor.apply(); - - } else { - - /*Intent intent = new Intent(); - intent.setAction(INTENT_ACTION_SUCCESS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - sendBroadcast(intent);*/ - - notifyDownloadComplete(); - } - } - - stopSelf(); - } - - private void initDatabase() { - m_databaseHelper = DatabaseHelper.getInstance(this); - } - - /* private synchronized SQLiteDatabase getReadableDb() { - return m_readableDb; - } */ - - private synchronized SQLiteDatabase getDatabase() { - return m_databaseHelper.getWritableDatabase(); - } - - @SuppressWarnings("unchecked") - private void downloadArticles() { - Log.d(TAG, "offline: downloading articles... offset=" + m_articleOffset); - - updateNotification(getString(R.string.notify_downloading_articles, m_articleOffset), m_articleOffset, m_syncMax, true, false); - - OfflineArticlesRequest req = new OfflineArticlesRequest(this); - - @SuppressWarnings("serial") - HashMap<String,String> map = new HashMap<String,String>(); - map.put("op", "getHeadlines"); - map.put("sid", m_sessionId); - map.put("feed_id", "-4"); - - if (BuildConfig.DEBUG && OFFLINE_DEBUG_READ) { - map.put("view_mode", "all_articles"); - } else { - map.put("view_mode", "unread"); - } - map.put("show_content", "true"); - map.put("skip", String.valueOf(m_articleOffset)); - map.put("limit", String.valueOf(OFFLINE_SYNC_SEQ)); - - req.execute(map); - } - - private void downloadFeeds() { - - updateNotification(R.string.notify_downloading_feeds, 0, 0, true, false); - - getDatabase().execSQL("DELETE FROM feeds;"); - - ApiRequest req = new ApiRequest(getApplicationContext()) { - @Override - protected JsonElement doInBackground(HashMap<String, String>... params) { - JsonElement content = super.doInBackground(params); - - if (content != null) { - - try { - Type listType = new TypeToken<List<Feed>>() {}.getType(); - List<Feed> feeds = new Gson().fromJson(content, listType); - - SQLiteStatement stmtInsert = getDatabase().compileStatement("INSERT INTO feeds " + - "(" + BaseColumns._ID + ", title, feed_url, has_icon, cat_id) " + - "VALUES (?, ?, ?, ?, ?);"); - - for (Feed feed : feeds) { - stmtInsert.bindLong(1, feed.id); - stmtInsert.bindString(2, feed.title); - stmtInsert.bindString(3, feed.feed_url); - stmtInsert.bindLong(4, feed.has_icon ? 1 : 0); - stmtInsert.bindLong(5, feed.cat_id); - - stmtInsert.execute(); - } - - stmtInsert.close(); - - Log.d(TAG, "offline: done downloading feeds"); - - m_articleOffset = 0; - - getDatabase().execSQL("DELETE FROM articles;"); - } catch (Exception e) { - e.printStackTrace(); - updateNotification(getErrorMessage(), 0, 0, false, true); - downloadFailed(); - } - } - - return content; - } - - @Override - protected void onPostExecute(JsonElement content) { - if (content != null) { - if (m_canProceed) { - downloadArticles(); - } else { - downloadFailed(); - } - } else { - updateNotification(getErrorMessage(), 0, 0, false, true); - downloadFailed(); - } - } - - }; - - @SuppressWarnings("serial") - HashMap<String,String> map = new HashMap<String,String>(); - map.put("op", "getFeeds"); - map.put("sid", m_sessionId); - map.put("cat_id", "-3"); - - if (!BuildConfig.DEBUG && OFFLINE_DEBUG_READ) { - map.put("unread_only", "true"); - } - - req.execute(map); - } - - private void downloadCategories() { - - updateNotification(R.string.notify_downloading_categories, 0, 0, true, false); - - getDatabase().execSQL("DELETE FROM categories;"); - - ApiRequest req = new ApiRequest(getApplicationContext()) { - protected JsonElement doInBackground(HashMap<String, String>... params) { - JsonElement content = super.doInBackground(params); - - if (content != null) { - try { - Type listType = new TypeToken<List<FeedCategory>>() {}.getType(); - List<FeedCategory> cats = new Gson().fromJson(content, listType); - - SQLiteStatement stmtInsert = getDatabase().compileStatement("INSERT INTO categories " + - "(" + BaseColumns._ID + ", title) " + - "VALUES (?, ?);"); - - for (FeedCategory cat : cats) { - stmtInsert.bindLong(1, cat.id); - stmtInsert.bindString(2, cat.title); - - stmtInsert.execute(); - } - - stmtInsert.close(); - - Log.d(TAG, "offline: done downloading categories"); - - } catch (Exception e) { - e.printStackTrace(); - updateNotification(getErrorMessage(), 0, 0, false, true); - downloadFailed(); - } - } - - return content; - } - @Override - protected void onPostExecute(JsonElement content) { - if (content != null) { - if (m_canProceed) { - downloadFeeds(); - } else { - downloadFailed(); - } - } else { - updateNotification(getErrorMessage(), 0, 0, false, true); - downloadFailed(); - } - } - - }; - - @SuppressWarnings("serial") - HashMap<String,String> map = new HashMap<String,String>(); - map.put("op", "getCategories"); - map.put("sid", m_sessionId); - //put("cat_id", "-3"); - - if (!BuildConfig.DEBUG && OFFLINE_DEBUG_READ) { - map.put("unread_only", "true"); - } - - req.execute(map); - } - - - @Override - public void onDestroy() { - super.onDestroy(); - m_nmgr.cancel(NOTIFY_DOWNLOADING); - - m_canProceed = false; - Log.d(TAG, "onDestroy"); - - } - - public class OfflineArticlesRequest extends ApiRequest { - List<Article> m_articles; - - public OfflineArticlesRequest(Context context) { - super(context); - } - - @Override - protected JsonElement doInBackground(HashMap<String, String>... params) { - JsonElement content = super.doInBackground(params); - - if (content != null) { - - try { - Type listType = new TypeToken<List<Article>>() {}.getType(); - m_articles = new Gson().fromJson(content, listType); - - SQLiteStatement stmtInsert = getDatabase().compileStatement("INSERT INTO articles " + - "(" + BaseColumns._ID + ", unread, marked, published, score, updated, is_updated, title, link, feed_id, tags, content, author) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - - for (Article article : m_articles) { - - String tagsString = ""; - - for (String t : article.tags) { - tagsString += t + ", "; - } - - tagsString = tagsString.replaceAll(", $", ""); - - int index = 1; - stmtInsert.bindLong(index++, article.id); - stmtInsert.bindLong(index++, article.unread ? 1 : 0); - stmtInsert.bindLong(index++, article.marked ? 1 : 0); - stmtInsert.bindLong(index++, article.published ? 1 : 0); - stmtInsert.bindLong(index++, article.score); - stmtInsert.bindLong(index++, article.updated); - stmtInsert.bindLong(index++, article.is_updated ? 1 : 0); - stmtInsert.bindString(index++, article.title); - stmtInsert.bindString(index++, article.link); - stmtInsert.bindLong(index++, article.feed_id); - stmtInsert.bindString(index++, tagsString); // comma-separated tags - stmtInsert.bindString(index++, article.content); - stmtInsert.bindString(index++, article.author != null ? article.author : ""); - - if (m_downloadImages) { - Document doc = Jsoup.parse(article.content); - - if (doc != null) { - Elements images = doc.select("img,source"); - - for (Element img : images) { - String url = img.attr("src"); - - if (url.indexOf("://") != -1) { - if (!ImageCacheService.isUrlCached(OfflineDownloadService.this, url)) { - Intent intent = new Intent(OfflineDownloadService.this, - ImageCacheService.class); - - intent.putExtra("url", url); - startService(intent); - } - } - } - - Elements videos = doc.select("video"); - - for (Element vid : videos) { - String url = vid.attr("poster"); - - if (url.indexOf("://") != -1) { - if (!ImageCacheService.isUrlCached(OfflineDownloadService.this, url)) { - Intent intent = new Intent(OfflineDownloadService.this, - ImageCacheService.class); - - intent.putExtra("url", url); - startService(intent); - } - } - } - - } - } - - try { - stmtInsert.execute(); - } catch (Exception e) { - e.printStackTrace(); - } - - } - - m_articleOffset += m_articles.size(); - - Log.d(TAG, "offline: received " + m_articles.size() + " articles; canProc=" + m_canProceed); - - stmtInsert.close(); - - } catch (Exception e) { - updateNotification(R.string.offline_switch_failed, 0, 0, false, true); - Log.d(TAG, "offline: failed: exception when loading articles"); - e.printStackTrace(); - downloadFailed(); - } - - } - - return content; - } - - @Override - protected void onPostExecute(JsonElement content) { - if (content != null) { - - if (m_canProceed && m_articles != null) { - if (m_articles.size() == OFFLINE_SYNC_SEQ && m_articleOffset < m_syncMax) { - downloadArticles(); - } else { - downloadComplete(); - } - } else { - downloadFailed(); - } - - } else { - Log.d(TAG, "offline: failed: " + getErrorMessage()); - updateNotification(getErrorMessage(), 0, 0, false, true); - downloadFailed(); - } - } - } - - @Override - public void onStart(Intent intent, int startId) { - try { - if (getDatabase().isDbLockedByCurrentThread() || getDatabase().isDbLockedByOtherThreads()) { - return; - } - - m_sessionId = intent.getStringExtra("sessionId"); - m_batchMode = intent.getBooleanExtra("batchMode", false); - - if (!m_downloadInProgress) { - if (m_downloadImages) ImageCacheService.cleanupCache(this, false); - - updateNotification(R.string.notify_downloading_init, 0, 0, true, false); - m_downloadInProgress = true; - - downloadCategories(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java deleted file mode 100755 index 372728e4..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedCategoriesFragment.java +++ /dev/null @@ -1,358 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.database.Cursor; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import org.fox.ttrss.BaseFeedlistFragment; -import org.fox.ttrss.R; - -import androidx.cursoradapter.widget.SimpleCursorAdapter; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -public class OfflineFeedCategoriesFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener { - private final String TAG = this.getClass().getSimpleName(); - private SharedPreferences m_prefs; - private FeedCategoryListAdapter m_adapter; - private int m_selectedCatId; - private Cursor m_cursor; - private OfflineMasterActivity m_activity; - private SwipeRefreshLayout m_swipeLayout; - private ListView m_list; - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - getActivity().getMenuInflater().inflate(R.menu.context_category, menu); - - menu.findItem(R.id.create_shortcut).setEnabled(false); - - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - Cursor cursor = getCatAtPosition(info.position); - - if (cursor != null) - menu.setHeaderTitle(cursor.getString(cursor.getColumnIndex("title"))); - - super.onCreateContextMenu(menu, v, menuInfo); - - } - - public Cursor createCursor() { - String unreadOnly = BaseColumns._ID + ">= 0 AND " + (m_activity.getUnreadOnly() ? "unread > 0" : "1"); - - String order = m_prefs.getBoolean("sort_feeds_by_unread", false) ? "unread DESC, title" : "title"; - - return m_activity.getDatabase().query("cats_unread", - null, unreadOnly, null, null, null, order); - } - - public void refresh() { - if (!isAdded()) return; - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - - m_cursor = createCursor(); - - if (m_cursor != null && m_adapter != null) { - m_adapter.changeCursor(m_cursor); - m_adapter.notifyDataSetChanged(); - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - } - } - - @Override - public void onResume() { - super.onResume(); - refresh(); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); - - int itemId = item.getItemId(); - if (itemId == R.id.browse_headlines) { - if (true) { - int catId = getCatIdAtPosition(info.position); - if (catId != -10000) { - m_activity.onCatSelected(catId, true); - } - } - return true; - } else if (itemId == R.id.browse_feeds) { - if (true) { - int catId = getCatIdAtPosition(info.position); - if (catId != -10000) { - m_activity.onCatSelected(catId, false); - } - } - return true; - } else if (itemId == R.id.catchup_category) { - if (true) { - int catId = getCatIdAtPosition(info.position); - if (catId != -10000) { - m_activity.catchupFeed(catId, true); - } - } - return true; - } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - if (savedInstanceState != null) { - m_selectedCatId = savedInstanceState.getInt("selectedFeedId"); - } - - View view = inflater.inflate(R.layout.fragment_feeds, container, false); - - m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); - - m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - refresh(); - } - }); - - m_list = view.findViewById(R.id.feeds); - - m_cursor = createCursor(); - - m_adapter = new FeedCategoryListAdapter(getActivity(), R.layout.feeds_row, m_cursor, - new String[] { "title", "unread" }, new int[] { R.id.title, R.id.unread_counter }, 0); - - initDrawerHeader(inflater, view, m_list, m_activity, m_prefs, true); - - m_list.setAdapter(m_adapter); - m_list.setOnItemClickListener(this); - registerForContextMenu(m_list); - - return view; - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_activity = (OfflineMasterActivity)activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_prefs.registerOnSharedPreferenceChangeListener(this); - - } - - @Override - public void onSaveInstanceState (Bundle out) { - super.onSaveInstanceState(out); - - out.putInt("selectedFeedId", m_selectedCatId); - } - - @Override - public void onItemClick(AdapterView<?> av, View view, int position, long id) { - ListView list = getActivity().findViewById(R.id.feeds); - - if (list != null) { - Cursor cursor = (Cursor) list.getItemAtPosition(position); - - if (cursor != null) { - int feedId = (int) cursor.getLong(0); - Log.d(TAG, "clicked on feed " + feedId); - - m_activity.onCatSelected(feedId); - - m_selectedCatId = feedId; - - m_adapter.notifyDataSetChanged(); - } - } - } - - /* public void setLoadingStatus(int status, boolean showProgress) { - if (getView() != null) { - TextView tv = (TextView)getView().findViewById(R.id.loading_message); - - if (tv != null) { - tv.setText(status); - } - } - - getActivity().setProgressBarIndeterminateVisibility(showProgress); - } */ - - private class FeedCategoryListAdapter extends SimpleCursorAdapter { - - - public FeedCategoryListAdapter(Context context, int layout, Cursor c, - String[] from, int[] to, int flags) { - super(context, layout, c, from, to, flags); - } - - public static final int VIEW_NORMAL = 0; - public static final int VIEW_SELECTED = 1; - - public static final int VIEW_COUNT = VIEW_SELECTED+1; - - @Override - public int getViewTypeCount() { - return VIEW_COUNT; - } - - @Override - public int getItemViewType(int position) { - Cursor cursor = (Cursor) this.getItem(position); - - if (!m_activity.isSmallScreen() && cursor.getLong(0) == m_selectedCatId) { - return VIEW_SELECTED; - } else { - return VIEW_NORMAL; - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - - Cursor cursor = (Cursor)getItem(position); - - if (v == null) { - int layoutId = R.layout.feeds_row; - - switch (getItemViewType(position)) { - case VIEW_SELECTED: - layoutId = R.layout.feeds_row_selected; - break; - } - - LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(layoutId, null); - - } - - ImageView icon = v.findViewById(R.id.icon); - - if (icon != null) { - TypedValue tv = new TypedValue(); - - m_activity.getTheme().resolveAttribute(R.attr.ic_folder_outline, tv, true); - icon.setImageResource(tv.resourceId); - - } - - TextView tt = v.findViewById(R.id.title); - - if (tt != null) { - tt.setText(cursor.getString(cursor.getColumnIndex("title"))); - } - - TextView tu = v.findViewById(R.id.unread_counter); - - if (tu != null) { - tu.setText(String.valueOf(cursor.getInt(cursor.getColumnIndex("unread")))); - tu.setVisibility((cursor.getInt(cursor.getColumnIndex("unread")) > 0) ? View.VISIBLE : View.INVISIBLE); - } - - /*ImageButton ib = (ImageButton) v.findViewById(R.id.feed_menu_button); - - if (ib != null) { - ib.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getActivity().openContextMenu(v); - } - }); - } */ - - - return v; - } - } - - public void sortCategories() { - try { - refresh(); - } catch (NullPointerException e) { - // activity is gone? - } catch (IllegalStateException e) { - // we're probably closing and DB is gone already - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - - sortCategories(); - } - - public int getCatIdAtPosition(int position) { - - /* if (m_list != null) { - Cursor c = (Cursor) m_list.getItemAtPosition(position); - - if (c != null) { - int catId = c.getInt(0); - return catId; - } - } */ - - Cursor tmp = getCatAtPosition(position); - - if (tmp != null) { - int id = tmp.getInt(0); - - return id; - } - - return -10000; - } - - public Cursor getCatAtPosition(int position) { - - if (m_list != null) { - return (Cursor) m_list.getItemAtPosition(position); - } - - return null; - } - - public void setSelectedFeedId(int feedId) { - m_selectedCatId = feedId; - refresh(); - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedsFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedsFragment.java deleted file mode 100755 index c0b63330..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineFeedsFragment.java +++ /dev/null @@ -1,367 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.database.Cursor; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ListView; -import android.widget.TextView; - -import org.fox.ttrss.BaseFeedlistFragment; -import org.fox.ttrss.R; - -import androidx.cursoradapter.widget.SimpleCursorAdapter; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -public class OfflineFeedsFragment extends BaseFeedlistFragment implements OnItemClickListener, OnSharedPreferenceChangeListener { - private final String TAG = this.getClass().getSimpleName(); - private SharedPreferences m_prefs; - private FeedListAdapter m_adapter; - private static final String ICON_PATH = "/data/org.fox.ttrss/icons/"; - private int m_selectedFeedId; - private int m_catId = -1; - private boolean m_enableFeedIcons; - private Cursor m_cursor; - private OfflineMasterActivity m_activity; - private SwipeRefreshLayout m_swipeLayout; - private boolean m_enableParentBtn = false; - private ListView m_list; - - public void initialize(int catId, boolean enableParentBtn) { - m_catId = catId; - m_enableParentBtn = enableParentBtn; - } - - @Override - public void onResume() { - super.onResume(); - refresh(); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); - int itemId = item.getItemId(); - if (itemId == R.id.browse_headlines) { - if (true) { - int feedId = getFeedIdAtPosition(info.position); - if (feedId != -10000) { - m_activity.onFeedSelected(feedId); - } - } - return true; - } else if (itemId == R.id.catchup_feed) { - int feedId = getFeedIdAtPosition(info.position); - if (feedId != -10000) { - m_activity.catchupFeed(feedId, false); - } - return true; - } - Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId()); - return super.onContextItemSelected(item); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - getActivity().getMenuInflater().inflate(R.menu.context_feed, menu); - - menu.findItem(R.id.create_shortcut).setEnabled(false); - - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - Cursor cursor = getFeedAtPosition(info.position); - - if (cursor != null) - menu.setHeaderTitle(cursor.getString(cursor.getColumnIndex("title"))); - - super.onCreateContextMenu(menu, v, menuInfo); - - } - - public Cursor createCursor() { - String unreadOnly = m_activity.getUnreadOnly() ? "unread > 0" : "1"; - String order = m_prefs.getBoolean("sort_feeds_by_unread", false) ? "unread DESC, title" : "title"; - - if (m_catId != -1) { - return m_activity.getDatabase().query("feeds_unread", - null, unreadOnly + " AND cat_id = ?", new String[] { String.valueOf(m_catId) }, null, null, order); - } else { - return m_activity.getDatabase().query("feeds_unread", - null, unreadOnly, null, null, null, order); - } - } - - public void refresh() { - try { - if (!isAdded()) return; - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - - m_cursor = createCursor(); - - if (m_cursor != null && m_adapter != null) { - m_adapter.changeCursor(m_cursor); - m_adapter.notifyDataSetChanged(); - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - } - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - if (savedInstanceState != null) { - m_selectedFeedId = savedInstanceState.getInt("selectedFeedId"); - m_catId = savedInstanceState.getInt("catId"); - m_enableParentBtn = savedInstanceState.getBoolean("enableParentBtn"); - } - - View view = inflater.inflate(R.layout.fragment_feeds, container, false); - - m_swipeLayout = view.findViewById(R.id.feeds_swipe_container); - - m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - refresh(); - } - }); - - m_list = view.findViewById(R.id.feeds); - - initDrawerHeader(inflater, view, m_list, m_activity, m_prefs, !m_enableParentBtn); - - if (m_enableParentBtn) { - View layout = inflater.inflate(R.layout.feeds_goback, container, false); - - layout.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, - ListView.LayoutParams.WRAP_CONTENT)); - - layout.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - m_activity.getSupportFragmentManager().popBackStack(); - } - }); - - m_list.addHeaderView(layout, null, false); - } - - m_cursor = createCursor(); - - m_adapter = new FeedListAdapter(getActivity(), R.layout.feeds_row, m_cursor, - new String[] { "title", "unread" }, new int[] { R.id.title, R.id.unread_counter }, 0); - - m_list.setAdapter(m_adapter); - m_list.setOnItemClickListener(this); - registerForContextMenu(m_list); - - m_enableFeedIcons = m_prefs.getBoolean("download_feed_icons", false); - - return view; - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - m_activity = (OfflineMasterActivity)activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - m_prefs.registerOnSharedPreferenceChangeListener(this); - - } - - @Override - public void onSaveInstanceState (Bundle out) { - super.onSaveInstanceState(out); - - out.putInt("selectedFeedId", m_selectedFeedId); - out.putInt("catId", m_catId); - out.putBoolean("enableParentBtn", m_enableParentBtn); - } - - @Override - public void onItemClick(AdapterView<?> av, View view, int position, long id) { - ListView list = getActivity().findViewById(R.id.feeds); - - if (list != null) { - Cursor cursor = (Cursor) list.getItemAtPosition(position); - - if (cursor != null) { - int feedId = (int) cursor.getLong(0); - Log.d(TAG, "clicked on feed " + feedId); - - m_activity.onFeedSelected(feedId); - - m_selectedFeedId = feedId; - - m_adapter.notifyDataSetChanged(); - } - } - } - - private class FeedListAdapter extends SimpleCursorAdapter { - - - public FeedListAdapter(Context context, int layout, Cursor c, - String[] from, int[] to, int flags) { - super(context, layout, c, from, to, flags); - } - - public static final int VIEW_NORMAL = 0; - public static final int VIEW_SELECTED = 1; - - public static final int VIEW_COUNT = VIEW_SELECTED+1; - - @Override - public int getViewTypeCount() { - return VIEW_COUNT; - } - - @Override - public boolean isEmpty() { - return !m_enableParentBtn && super.isEmpty(); - } - - @Override - public int getItemViewType(int position) { - Cursor cursor = (Cursor) this.getItem(position); - - if (!m_activity.isSmallScreen() && cursor.getLong(0) == m_selectedFeedId) { - return VIEW_SELECTED; - } else { - return VIEW_NORMAL; - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - - Cursor cursor = (Cursor)getItem(position); - - if (v == null) { - int layoutId = R.layout.feeds_row; - - switch (getItemViewType(position)) { - case VIEW_SELECTED: - layoutId = R.layout.feeds_row_selected; - break; - } - - LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(layoutId, null); - - } - - TextView tt = v.findViewById(R.id.title); - - if (tt != null) { - tt.setText(cursor.getString(cursor.getColumnIndex("title"))); - } - - TextView tu = v.findViewById(R.id.unread_counter); - - if (tu != null) { - tu.setText(String.valueOf(cursor.getInt(cursor.getColumnIndex("unread")))); - tu.setVisibility((cursor.getInt(cursor.getColumnIndex("unread")) > 0) ? View.VISIBLE : View.INVISIBLE); - } - - /*ImageButton ib = (ImageButton) v.findViewById(R.id.feed_menu_button); - - if (ib != null) { - ib.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getActivity().openContextMenu(v); - } - }); - }*/ - - return v; - } - } - - public void sortFeeds() { - try { - refresh(); - } catch (NullPointerException e) { - // activity is gone? - } catch (IllegalStateException e) { - // we're probably closing and DB is gone already - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - - sortFeeds(); - m_enableFeedIcons = m_prefs.getBoolean("download_feed_icons", false); - - } - - public int getFeedIdAtPosition(int position) { - /* if (m_list != null) { - Cursor c = (Cursor) m_list.getItemAtPosition(position); - - if (c != null) { - int feedId = c.getInt(0); - return feedId; - } - } */ - - Cursor tmp = getFeedAtPosition(position); - - if (tmp != null) { - int id = tmp.getInt(0); - - return id; - } - - return -10000; - } - - public Cursor getFeedAtPosition(int position) { - - if (m_list != null) { - return (Cursor) m_list.getItemAtPosition(position); - } - - return null; - } - - public void setSelectedFeedId(int feedId) { - m_selectedFeedId = feedId; - refresh(); - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java deleted file mode 100644 index 0818a66b..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesEventListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.fox.ttrss.offline; - - -public interface OfflineHeadlinesEventListener { - void onArticleSelected(int articleId, boolean open); - void onArticleSelected(int articleId); -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesFragment.java deleted file mode 100755 index 7994b576..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineHeadlinesFragment.java +++ /dev/null @@ -1,1311 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.ColorStateList; -import android.database.Cursor; -import android.database.sqlite.SQLiteStatement; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.text.Html; -import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.CheckBox; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.PopupMenu; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.amulyakhare.textdrawable.TextDrawable; -import com.amulyakhare.textdrawable.util.ColorGenerator; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; -import com.google.android.material.button.MaterialButton; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.fox.ttrss.Application; -import org.fox.ttrss.CommonActivity; -import org.fox.ttrss.GalleryActivity; -import org.fox.ttrss.HeadlinesFragment; -import org.fox.ttrss.R; -import org.fox.ttrss.util.ImageCacheService; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.TimeZone; - -import androidx.core.app.ActivityCompat; -import androidx.core.app.ActivityOptionsCompat; -import androidx.core.view.ViewCompat; -import androidx.cursoradapter.widget.SimpleCursorAdapter; -import androidx.fragment.app.Fragment; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import jp.wasabeef.glide.transformations.CropCircleTransformation; - -public class OfflineHeadlinesFragment extends Fragment implements OnItemClickListener, AbsListView.OnScrollListener { - public enum ArticlesSelection { ALL, NONE, UNREAD } - - private final String TAG = this.getClass().getSimpleName(); - - private int m_feedId; - private boolean m_feedIsCat = false; - private String m_feedTitle; - - private int m_activeArticleId; - private String m_searchQuery = ""; - - private SharedPreferences m_prefs; - private ArrayList<Integer> m_readArticleIds = new ArrayList<Integer>(); - private ArrayList<Integer> m_autoMarkedArticleIds = new ArrayList<Integer>(); - - private HashMap<Integer, Integer> m_flavorHeightStorage = new HashMap<>(); - - private Cursor m_cursor; - private ArticleListAdapter m_adapter; - - private OfflineHeadlinesEventListener m_listener; - private OfflineActivity m_activity; - private SwipeRefreshLayout m_swipeLayout; - - private boolean m_compactLayoutMode = false; - private ListView m_list; - - public void initialize(int feedId, boolean isCat, boolean compactMode) { - m_feedId = feedId; - m_feedIsCat = isCat; - m_compactLayoutMode = compactMode; - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - } - - public int getSelectedArticleCount() { - Cursor c = m_activity.getDatabase().query("articles", - new String[] { "COUNT(*)" }, "selected = 1", null, null, null, null); - c.moveToFirst(); - int selected = c.getInt(0); - c.close(); - - return selected; - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - - getActivity().getMenuInflater().inflate(R.menu.context_headlines, menu); - - menu.findItem(R.id.set_labels).setVisible(false); - menu.findItem(R.id.article_set_note).setVisible(false); - - if (m_prefs.getBoolean("offline_sort_by_feed", false)) { - menu.findItem(R.id.catchup_above).setVisible(false); - } - - super.onCreateContextMenu(menu, v, menuInfo); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item - .getMenuInfo(); - - int articleId = getArticleIdAtPosition(info.position); - - if (!onArticleMenuItemSelected(item, articleId)) - return super.onContextItemSelected(item); - else - return true; - } - - private boolean onArticleMenuItemSelected(MenuItem item, final int articleId) { - int itemId = item.getItemId(); - if (itemId == R.id.headlines_article_unread) { - if (true) { - - SQLiteStatement stmt = m_activity.getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = not unread " + "WHERE " + BaseColumns._ID - + " = ?"); - - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - - refresh(); - } - return true; - } else if (itemId == R.id.headlines_article_link_copy) { - if (true) { - Cursor article = m_activity.getArticleById(articleId); - - if (article != null) { - m_activity.copyToClipboard(article.getString(article.getColumnIndex("link"))); - article.close(); - } - } - return true; - } else if (itemId == R.id.headlines_article_link_open) { - if (true) { - Cursor article = m_activity.getArticleById(articleId); - - if (article != null) { - m_activity.openUri(Uri.parse(article.getString(article.getColumnIndex("link")))); - - // TODO: mark article as read, set modified = 1, refresh - - article.close(); - } - } - return true; - } else if (itemId == R.id.headlines_share_article) { - m_activity.shareArticle(articleId); - return true; - } else if (itemId == R.id.catchup_above) { - if (true) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()) - .setMessage(R.string.confirm_catchup_above) - .setPositiveButton(R.string.dialog_ok, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - catchupAbove(articleId); - - } - }) - .setNegativeButton(R.string.dialog_cancel, - new Dialog.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - - } - }); - - Dialog dialog = builder.create(); - dialog.show(); - } - return true; - } - Log.d(TAG, "onArticleMenuItemSelected, unhandled id=" + item.getItemId()); - return false; - - } - - private void catchupAbove(int articleId) { - SQLiteStatement stmt = null; - - String updatedOperator = (m_prefs.getBoolean("offline_oldest_first", false)) ? "<" : ">"; - - if (m_feedIsCat) { - stmt = m_activity.getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE " + - "updated "+updatedOperator+" (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " + - "AND unread = 1 AND feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)"); - } else { - stmt = m_activity.getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 WHERE " + - "updated "+updatedOperator+" (SELECT updated FROM articles WHERE " + BaseColumns._ID + " = ?) " + - "AND unread = 1 AND feed_id = ?"); - } - - stmt.bindLong(1, articleId); - stmt.bindLong(2, m_feedId); - stmt.execute(); - stmt.close(); - - refresh(); - } - - @Override - public void onResume() { - super.onResume(); - - if (Application.getInstance().m_selectedArticleId != 0) { - m_activeArticleId = Application.getInstance().m_selectedArticleId; - Application.getInstance().m_selectedArticleId = 0; - } - - if (m_activeArticleId != 0) { - setActiveArticleId(m_activeArticleId); - } - - //refresh(); - - m_activity.invalidateOptionsMenu(); - } - - public void refresh() { - refresh(true); - } - - public void refresh(boolean keepRemnantIds) { - try { - if (!isAdded()) return; - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true); - - if (m_cursor != null && !m_cursor.isClosed()) m_cursor.close(); - - if (!keepRemnantIds) m_autoMarkedArticleIds.clear(); - - m_cursor = createCursor(); - - if (m_cursor != null && m_adapter != null) { - m_adapter.changeCursor(m_cursor); - m_adapter.notifyDataSetChanged(); - } - - if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false); - - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setRetainInstance(true); - - Glide.get(getContext()).clearMemory(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - if (savedInstanceState != null) { - m_feedId = savedInstanceState.getInt("feedId"); - m_activeArticleId = savedInstanceState.getInt("activeArticleId"); - //m_selectedArticles = savedInstanceState.getParcelableArrayList("selectedArticles"); - m_searchQuery = (String) savedInstanceState.getCharSequence("searchQuery"); - m_feedIsCat = savedInstanceState.getBoolean("feedIsCat"); - m_compactLayoutMode = savedInstanceState.getBoolean("compactLayoutMode"); - m_readArticleIds = savedInstanceState.getIntegerArrayList("autoMarkedIds"); - - } else { - m_activity.getDatabase().execSQL("UPDATE articles SET selected = 0 "); - } - - String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); - - if ("HL_COMPACT".equals(headlineMode) || "HL_COMPACT_NOIMAGES".equals(headlineMode)) - m_compactLayoutMode = true; - - View view = inflater.inflate(R.layout.fragment_headlines_offline, container, false); - - m_swipeLayout = view.findViewById(R.id.headlines_swipe_container); - - m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - refresh(false); - } - }); - - m_cursor = createCursor(); - - m_list = view.findViewById(R.id.headlines_list); - - if (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); - - View layout = inflater.inflate(R.layout.headlines_footer, container, false); - - layout.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, screenHeight)); - - m_list.addFooterView(layout, null, false); - } - - - m_adapter = new ArticleListAdapter(getActivity(), R.layout.headlines_row, m_cursor, - new String[] { "title" }, new int[] { R.id.title }, 0); - m_list.setAdapter(m_adapter); - - m_list.setOnItemClickListener(this); - m_list.setOnScrollListener(this); - registerForContextMenu(m_list); - - m_feedTitle = m_activity.getFeedTitle(m_feedId, m_feedIsCat); - - if (m_feedTitle != null && m_activity.isSmallScreen()) { - m_activity.setTitle(m_feedTitle); - } - - return view; - } - - public Cursor createCursor() { - String feedClause = null; - - if (m_feedIsCat) { - feedClause = "feed_id IN (SELECT "+BaseColumns._ID+" FROM feeds WHERE cat_id = ?)"; - } else { - feedClause = "feed_id = ?"; - } - - String viewMode = m_activity.getViewMode(); - - if ("adaptive".equals(viewMode)) { - // TODO: implement adaptive - } else if ("marked".equals(viewMode)) { - feedClause += "AND (marked = 1)"; - } else if ("published".equals(viewMode)) { - feedClause += "AND (published = 1)"; - } else if ("unread".equals(viewMode)) { - String idsMarkedRead = "articles." + BaseColumns._ID + " in (" + android.text.TextUtils.join(",", m_autoMarkedArticleIds) + ")"; - - feedClause += "AND (unread = 1 OR "+idsMarkedRead+")"; - } else { // all_articles - // - } - - String orderBy = (m_prefs.getBoolean("offline_oldest_first", false)) ? "updated" : "updated DESC"; - - if (m_prefs.getBoolean("offline_sort_by_feed", false)) { - orderBy = "feed_title, " + orderBy; - } - - if (m_searchQuery == null || m_searchQuery.equals("")) { - return m_activity.getDatabase().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")", - new String[] { "articles.*", "feeds.title AS feed_title" }, feedClause, - new String[] { String.valueOf(m_feedId) }, null, null, orderBy); - } else { - return m_activity.getDatabase().query("articles LEFT JOIN feeds ON (feed_id = feeds."+BaseColumns._ID+")", - new String[] { "articles.*", "feeds.title AS feed_title" }, - feedClause + " AND (articles.title LIKE '%' || ? || '%' OR content LIKE '%' || ? || '%')", - new String[] { String.valueOf(m_feedId), m_searchQuery, m_searchQuery }, null, null, orderBy); - } - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - m_listener = (OfflineHeadlinesEventListener) activity; - m_activity = (OfflineActivity) activity; - - m_prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - } - - @Override - public void onItemClick(AdapterView<?> av, View view, int position, long id) { - ListView list = (ListView)av; - - Log.d(TAG, "onItemClick=" + position); - - if (list != null) { - - int articleId = getArticleIdAtPosition(position); - - if (articleId != 0) { - - if (getActivity().findViewById(R.id.article_fragment) != null) { - m_activeArticleId = articleId; - } - - m_listener.onArticleSelected(articleId); - - refresh(); - } - } - } - - @Override - public void onSaveInstanceState (Bundle out) { - super.onSaveInstanceState(out); - - out.putInt("feedId", m_feedId); - out.putInt("activeArticleId", m_activeArticleId); - //out.putParcelableArrayList("selectedArticles", m_selectedArticles); - out.putCharSequence("searchQuery", m_searchQuery); - out.putBoolean("feedIsCat", m_feedIsCat); - out.putIntegerArrayList("autoMarkedIds", m_autoMarkedArticleIds); - - out.putBoolean("compactLayoutMode", m_compactLayoutMode); - } - - /* public void setLoadingStatus(int status, boolean showProgress) { - if (getView() != null) { - TextView tv = (TextView)getView().findViewById(R.id.loading_message); - - if (tv != null) { - tv.setText(status); - } - } - - getActivity().setProgressBarIndeterminateVisibility(showProgress); - } */ - - static class ArticleViewHolder { - - public int articleId; - HashMap<Integer, Integer> flavorHeightStorage; - - public View view; - - public TextView titleView; - public TextView feedTitleView; - public MaterialButton markedView; - public MaterialButton publishedView; - public TextView excerptView; - public ImageView flavorImageView; - 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 ImageView flavorVideoKindView; - public View flavorImageOverflow; - public View headlineHeader; - public MaterialButton attachmentsView; - public MaterialButton scoreView; - - public ArticleViewHolder(View v) { - - view = v; - - view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - View flavorImage = view.findViewById(R.id.flavor_image); - - if (flavorImage != null) { - int height = flavorImage.getMeasuredHeight(); - - if (height > 0) { - //Log.d("ArticleViewHolder", "view measured height: " + flavorImage.getMeasuredHeight() + " for " + articleId); - - flavorHeightStorage.put(articleId, flavorImage.getMeasuredHeight()); - } - } - - return true; - } - }); - - titleView = v.findViewById(R.id.title); - - feedTitleView = v.findViewById(R.id.feed_title); - markedView = v.findViewById(R.id.marked); - publishedView = v.findViewById(R.id.published); - excerptView = v.findViewById(R.id.excerpt); - flavorImageView = v.findViewById(R.id.flavor_image); - 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.flavorImageHolder); - flavorImageLoadingBar = v.findViewById(R.id.flavorImageLoadingBar); - headlineFooter = v.findViewById(R.id.headline_footer); - textImage = v.findViewById(R.id.text_image); - textChecked = v.findViewById(R.id.text_checked); - flavorVideoKindView = v.findViewById(R.id.flavor_video_kind); - headlineHeader = v.findViewById(R.id.headline_header); - flavorImageOverflow = v.findViewById(R.id.gallery_overflow); - attachmentsView = v.findViewById(R.id.attachments); - scoreView = v.findViewById(R.id.score); - } - } - - private class ArticleListAdapter extends SimpleCursorAdapter { - public static final int VIEW_NORMAL = 0; - public static final int VIEW_UNREAD = 1; - public static final int VIEW_SELECTED = 2; - public static final int VIEW_SELECTED_UNREAD = 3; - public static final int VIEW_LOADMORE = 4; - - public static final int VIEW_COUNT = VIEW_LOADMORE+1; - - private final Integer[] origTitleColors = new Integer[VIEW_COUNT]; - - private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT; - private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round(); - - private boolean showFlavorImage; - private int m_screenHeight; - - public ArticleListAdapter(Context context, int layout, Cursor c, - String[] from, int[] to, int flags) { - super(context, layout, c, from, to, flags); - - String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); - showFlavorImage = "HL_DEFAULT".equals(headlineMode) || "HL_COMPACT".equals(headlineMode); - - Display display = m_activity.getWindowManager().getDefaultDisplay(); - Point size = new Point(); - display.getSize(size); - //m_minimumHeightToEmbed = size.y/3; - m_screenHeight = size.y; - } - - public int getViewTypeCount() { - return VIEW_COUNT; - } - - @Override - public int getItemViewType(int position) { - Cursor c = (Cursor) getItem(position); - - //Log.d(TAG, "@gIVT " + position + " " + c.getInt(0) + " vs " + m_activeArticleId); - - if (c.getInt(0) == m_activeArticleId && c.getInt(c.getColumnIndex("unread")) == 1) { - return VIEW_SELECTED_UNREAD; - } else if (c.getInt(0) == m_activeArticleId) { - return VIEW_SELECTED; - } else if (c.getInt(c.getColumnIndex("unread")) == 1) { - return VIEW_UNREAD; - } else { - return VIEW_NORMAL; - } - } - - private void updateTextCheckedState(ArticleViewHolder holder, Cursor item, ArticleFlavorInfo afi) { - String title = item.getString(item.getColumnIndex("title")); - - String tmp = title.length() > 0 ? title.substring(0, 1) : "?"; - - boolean isChecked = item.getInt(item.getColumnIndex("selected")) == 1; - - if (isChecked) { - holder.textImage.setImageDrawable(m_drawableBuilder.build(" ", 0xff616161)); - - holder.textChecked.setVisibility(View.VISIBLE); - } else { - Drawable textDrawable = m_drawableBuilder.build(tmp, m_colorGenerator.getColor(title)); - - if (showFlavorImage && afi.flavorImageUri != null) { - - holder.textImage.setImageDrawable(textDrawable); - - Glide.with(getContext()) - .load(afi.flavorImageUri) - .placeholder(textDrawable) - .bitmapTransform(new CropCircleTransformation(getActivity())) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(false) - .listener(new RequestListener<String, GlideDrawable>() { - @Override - public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) { - return false; - } - - @Override - public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { - - return resource.getIntrinsicWidth() < HeadlinesFragment.THUMB_IMG_MIN_SIZE || - resource.getIntrinsicHeight() < HeadlinesFragment.THUMB_IMG_MIN_SIZE; - } - }) - .into(holder.textImage); - - } else { - holder.textImage.setImageDrawable(textDrawable); - } - - holder.textChecked.setVisibility(View.GONE); - } - } - - @Override - public View getView(int position, final View convertView, ViewGroup parent) { - - View v = convertView; - - final Cursor article = (Cursor)getItem(position); - - final ArticleViewHolder holder; - - final int articleId = article.getInt(0); - - int headlineFontSize = m_prefs.getInt("headlines_font_size_sp_int", 13); - int headlineSmallFontSize = Math.max(10, Math.min(18, headlineFontSize - 2)); - - if (v == null) { - int layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact : R.layout.headlines_row; - - switch (getItemViewType(position)) { - case VIEW_LOADMORE: - layoutId = R.layout.headlines_row_loadmore; - break; - case VIEW_UNREAD: - layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact_unread : R.layout.headlines_row_unread; - break; - case VIEW_SELECTED: - layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact_selected : R.layout.headlines_row; - break; - case VIEW_SELECTED_UNREAD: - layoutId = m_compactLayoutMode ? R.layout.headlines_row_compact_selected_unread : R.layout.headlines_row_unread; - break; - } - - LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - v = vi.inflate(layoutId, null); - - holder = new ArticleViewHolder(v); - v.setTag(holder); - - // http://code.google.com/p/android/issues/detail?id=3414 - ((ViewGroup)v).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - } else { - holder = (ArticleViewHolder) v.getTag(); - } - - holder.articleId = articleId; - holder.flavorHeightStorage = m_flavorHeightStorage; - - // block footer clicks to make button/selection clicking easier - if (holder.headlineFooter != null) { - holder.headlineFooter.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - // - } - }); - } - - if (holder.textImage != null) { - final ArticleFlavorInfo afi = findFlavorImage(article); - - updateTextCheckedState(holder, article, afi); - - holder.textImage.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - Log.d(TAG, "textImage : onclicked"); - - SQLiteStatement stmtUpdate = m_activity.getDatabase().compileStatement("UPDATE articles SET selected = NOT selected " + - "WHERE " + BaseColumns._ID + " = ?"); - - stmtUpdate.bindLong(1, articleId); - stmtUpdate.execute(); - stmtUpdate.close(); - - updateTextCheckedState(holder, article, afi); - - refresh(); - - m_activity.invalidateOptionsMenu(); - } - }); - - ViewCompat.setTransitionName(holder.textImage, "gallery:" + afi.flavorImageUri); - - if (afi.flavorImageUri != null) { - - final String articleContent = article.getString(article.getColumnIndex("content")); - final String articleTitle = article.getString(article.getColumnIndex("title")); - - holder.textImage.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - openGalleryForType(afi, articleTitle, articleContent, holder, holder.textImage); - return true; - } - }); - - } - - - } - - if (holder.titleView != null) { - - holder.titleView.setText(Html.fromHtml(article.getString(article.getColumnIndex("title")))); - holder.titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, Math.min(21, headlineFontSize + 3)); - - int scoreIndex = article.getColumnIndex("score"); - if (scoreIndex >= 0) - adjustTitleTextView(article.getInt(scoreIndex), holder.titleView, position); - } - - int feedTitleIndex = article.getColumnIndex("feed_title"); - - if (holder.feedTitleView != null && feedTitleIndex != -1 && m_feedIsCat) { - String feedTitle = article.getString(feedTitleIndex); - - if (feedTitle.length() > 20) - feedTitle = feedTitle.substring(0, 20) + "..."; - - if (feedTitle.length() > 0) { - holder.feedTitleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, headlineSmallFontSize); - holder.feedTitleView.setText(feedTitle); - } else { - holder.feedTitleView.setVisibility(View.GONE); - } - } else if (holder.feedTitleView != null) { - holder.feedTitleView.setVisibility(View.GONE); - } - - TypedValue tvTertiary = new TypedValue(); - m_activity.getTheme().resolveAttribute(R.attr.colorTertiary, tvTertiary, true); - - TypedValue tvPrimary = new TypedValue(); - m_activity.getTheme().resolveAttribute(R.attr.colorPrimary, tvPrimary, true); - - if (holder.attachmentsView != null) { - holder.attachmentsView.setVisibility(View.GONE); - } - - if (holder.scoreView != null) { - holder.scoreView.setVisibility(View.GONE); - } - - if (holder.markedView != null) { - TypedValue tv = new TypedValue(); - - boolean marked = article.getInt(article.getColumnIndex("marked")) == 1; - - m_activity.getTheme().resolveAttribute(marked ? R.attr.ic_star : R.attr.ic_star_outline, tv, true); - - holder.markedView.setIconResource(tv.resourceId); - - if (marked) - holder.markedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); - else - holder.markedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); - - holder.markedView.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - SQLiteStatement stmtUpdate = m_activity.getDatabase() - .compileStatement("UPDATE articles SET modified = 1, modified_marked = 1, marked = NOT marked " + - "WHERE " + BaseColumns._ID + " = ?"); - - stmtUpdate.bindLong(1, articleId); - stmtUpdate.execute(); - stmtUpdate.close(); - - refresh(); - } - }); - } - - if (holder.publishedView != null) { - TypedValue tv = new TypedValue(); - - boolean published = article.getInt(article.getColumnIndex("published")) == 1; - - m_activity.getTheme().resolveAttribute(published ? R.attr.ic_checkbox_marked : R.attr.ic_rss_box, tv, true); - - holder.publishedView.setIconResource(tv.resourceId); - - if (published) - holder.publishedView.setIconTint(ColorStateList.valueOf(tvTertiary.data)); - else - holder.publishedView.setIconTint(ColorStateList.valueOf(tvPrimary.data)); - - holder.publishedView.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - SQLiteStatement stmtUpdate = m_activity.getDatabase() - .compileStatement("UPDATE articles SET modified = 1, modified_published = 1, published = NOT published " + - "WHERE " + BaseColumns._ID + " = ?"); - - stmtUpdate.bindLong(1, articleId); - stmtUpdate.execute(); - stmtUpdate.close(); - - refresh(); - } - }); - } - - if (holder.excerptView != null) { - if (!m_prefs.getBoolean("headlines_show_content", true)) { - holder.excerptView.setVisibility(View.GONE); - } else { - String articleContent = article.getString(article.getColumnIndex("content")); - - String tmp = articleContent.length() > CommonActivity.EXCERPT_MAX_QUERY_LENGTH ? - articleContent.substring(0, CommonActivity.EXCERPT_MAX_QUERY_LENGTH) : articleContent; - - String excerpt = Jsoup.parse(tmp).text(); - - if (excerpt.length() > CommonActivity.EXCERPT_MAX_LENGTH) excerpt = excerpt.substring(0, CommonActivity.EXCERPT_MAX_LENGTH) + "…"; - - holder.excerptView.setTextSize(TypedValue.COMPLEX_UNIT_SP, headlineFontSize); - holder.excerptView.setText(excerpt); - } - } - - if (holder.authorView != null) { - int authorIndex = article.getColumnIndex("author"); - if (authorIndex >= 0) { - String author = article.getString(authorIndex); - - holder.authorView.setTextSize(TypedValue.COMPLEX_UNIT_SP, headlineSmallFontSize); - - if (author != null && author.length() > 0) - holder.authorView.setText(getString(R.string.author_formatted, author)); - else - holder.authorView.setText(""); - } - } - - if (holder.dateView != null) { - holder.dateView.setTextSize(TypedValue.COMPLEX_UNIT_SP, headlineSmallFontSize); - - Date d = new Date((long)article.getInt(article.getColumnIndex("updated")) * 1000); - DateFormat df = new SimpleDateFormat("MMM dd, HH:mm"); - df.setTimeZone(TimeZone.getDefault()); - holder.dateView.setText(df.format(d)); - } - - if (holder.selectionBoxView != null) { - holder.selectionBoxView.setChecked(article.getInt(article.getColumnIndex("selected")) == 1); - holder.selectionBoxView.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View view) { - CheckBox cb = (CheckBox) view; - - SQLiteStatement stmtUpdate = m_activity.getDatabase().compileStatement("UPDATE articles SET selected = ? " + - "WHERE " + BaseColumns._ID + " = ?"); - - stmtUpdate.bindLong(1, cb.isChecked() ? 1 : 0); - stmtUpdate.bindLong(2, articleId); - stmtUpdate.execute(); - stmtUpdate.close(); - - refresh(); - - m_activity.invalidateOptionsMenu(); - - } - }); - } - - if (holder.flavorImageHolder != null) { - - /* reset to default in case of convertview */ - holder.flavorImageLoadingBar.setVisibility(View.GONE); - holder.flavorImageView.setVisibility(View.GONE); - holder.flavorVideoKindView.setVisibility(View.GONE); - holder.flavorImageOverflow.setVisibility(View.GONE); - - // this is needed if our flavor image goes behind base listview element - holder.headlineHeader.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - m_listener.onArticleSelected(articleId); - } - }); - - holder.headlineHeader.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - m_activity.openContextMenu(v); - - return true; - } - }); - - if (showFlavorImage) { - final ArticleFlavorInfo afi = findFlavorImage(article); - - if (afi.flavorImageUri != null) { - holder.flavorImageView.setVisibility(View.VISIBLE); - - holder.flavorImageView.setMaxHeight((int)(m_screenHeight * 0.8f)); - - int flavorViewHeight = m_flavorHeightStorage.containsKey(articleId) ? m_flavorHeightStorage.get(articleId) : 0; - - //Log.d(TAG, articleId + " IMG: " + afi.flavorImageUri + " STREAM: " + afi.flavorStreamUri + " H:" + flavorViewHeight); - - if (flavorViewHeight > 0) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); - lp.height = flavorViewHeight; - } - - final String articleContent = article.getString(article.getColumnIndex("content")); - final String articleTitle = article.getString(article.getColumnIndex("title")); - - - holder.flavorImageView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - - openGalleryForType(afi, articleTitle, articleContent, holder, holder.flavorImageView); - } - }); - - try { - - Glide.with(getContext()) - .load(afi.flavorImageUri) - //.dontTransform() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(false) - .listener(new RequestListener<String, GlideDrawable>() { - @Override - public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) { - - holder.flavorImageLoadingBar.setVisibility(View.GONE); - holder.flavorImageView.setVisibility(View.GONE); - - return false; - } - - @Override - public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { - - holder.flavorImageLoadingBar.setVisibility(View.GONE); - - if (resource.getIntrinsicWidth() > HeadlinesFragment.FLAVOR_IMG_MIN_SIZE && - resource.getIntrinsicHeight() > HeadlinesFragment.FLAVOR_IMG_MIN_SIZE) { - - holder.flavorImageView.setVisibility(View.VISIBLE); - - adjustVideoKindView(holder, afi); - - return false; - } else { - - holder.flavorImageOverflow.setVisibility(View.GONE); - holder.flavorImageView.setVisibility(View.GONE); - - return true; - } - } - }) - .into(holder.flavorImageView); - } catch (OutOfMemoryError e) { - e.printStackTrace(); - } - - } - } - } - - if (holder.menuButtonView != null) { - //if (m_activity.isDarkTheme()) - // ib.setImageResource(R.drawable.ic_mailbox_collapsed_holo_dark); - - holder.menuButtonView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - PopupMenu popup = new PopupMenu(getActivity(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.context_headlines, popup.getMenu()); - - popup.getMenu().findItem(R.id.set_labels).setVisible(false); - popup.getMenu().findItem(R.id.article_set_note).setVisible(false); - - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - return onArticleMenuItemSelected(item, articleId); - } - }); - - popup.show(); - } - }); - } - - return v; - } - - private void adjustVideoKindView(ArticleViewHolder holder, ArticleFlavorInfo afi) { - if (afi.flavorImageUri != null) { - if (afi.flavorStreamUri != null) { - holder.flavorVideoKindView.setImageResource(R.drawable.baseline_play_circle_24); - holder.flavorVideoKindView.setVisibility(View.VISIBLE); - } else if (afi.mediaList.size() > 1) { - holder.flavorVideoKindView.setImageResource(R.drawable.baseline_photo_album_24); - holder.flavorVideoKindView.setVisibility(View.VISIBLE); - } else { - holder.flavorVideoKindView.setVisibility(View.INVISIBLE); - } - } else { - holder.flavorVideoKindView.setVisibility(View.INVISIBLE); - } - } - - private void openGalleryForType(ArticleFlavorInfo afi, String title, String content, ArticleViewHolder viewHolder, View transitionView) { - - Intent intent = new Intent(m_activity, GalleryActivity.class); - - intent.putExtra("firstSrc", afi.flavorStreamUri != null ? afi.flavorStreamUri : afi.flavorImageUri); - intent.putExtra("title", title); - intent.putExtra("content", rewriteUrlsToLocal(content)); - - ActivityOptionsCompat options = - ActivityOptionsCompat.makeSceneTransitionAnimation(m_activity, - transitionView != null ? transitionView : viewHolder.flavorImageView, - "gallery:" + (afi.flavorStreamUri != null ? afi.flavorStreamUri : afi.flavorImageUri)); - - ActivityCompat.startActivity(m_activity, intent, options.toBundle()); - - } - - private String rewriteUrlsToLocal(String content) { - Document doc = Jsoup.parse(content); - - if (doc != null) { - List<Element> mediaList = doc.select("img,source"); - - for (Element e : mediaList) { - String url = e.attr("src"); - - if (url != null && ImageCacheService.isUrlCached(m_activity, url)) { - e.attr("src", "file://" + ImageCacheService.getCacheFileName(m_activity, url)); - } - } - - content = doc.html(); - } - - return content; - } - - private class ArticleFlavorInfo { - String flavorImageUri; - String flavorStreamUri; - public List<Element> mediaList = new ArrayList<>(); - } - - private ArticleFlavorInfo findFlavorImage(Cursor article) { - ArticleFlavorInfo afi = new ArticleFlavorInfo(); - - String content = article.getString(article.getColumnIndex("content")); - - if (content != null) { - Document articleDoc = Jsoup.parse(content); - - if (articleDoc != null) { - - Element flavorImage = null; - afi.mediaList = articleDoc.select("img,video"); - - for (Element e : afi.mediaList) { - flavorImage = e; - break; - } - - if (flavorImage != null) { - - try { - - if ("video".equals(flavorImage.tagName().toLowerCase())) { - Element source = flavorImage.select("source").first(); - - if (source != null) { - afi.flavorStreamUri = source.attr("src"); - afi.flavorImageUri = flavorImage.attr("poster"); - } - } else { - afi.flavorImageUri = flavorImage.attr("src"); - - if (afi.flavorImageUri.length() > 0 && afi.flavorImageUri.startsWith("//")) { - afi.flavorImageUri = "https:" + afi.flavorImageUri; - } - - afi.flavorStreamUri = null; - } - } catch (Exception e) { - e.printStackTrace(); - - afi.flavorImageUri = null; - afi.flavorStreamUri = null; - } - } - } - } - - if (afi.flavorImageUri != null && afi.flavorImageUri.length() > 0 && ImageCacheService.isUrlCached(m_activity, afi.flavorImageUri)) { - afi.flavorImageUri = "file://" + ImageCacheService.getCacheFileName(m_activity, afi.flavorImageUri); - } else { - afi.flavorImageUri = null; - } - - if (afi.flavorStreamUri != null && afi.flavorImageUri.length() > 0 && ImageCacheService.isUrlCached(m_activity, afi.flavorStreamUri)) { - afi.flavorStreamUri = "file://" + ImageCacheService.getCacheFileName(m_activity, afi.flavorStreamUri); - } else { - afi.flavorStreamUri = null; - } - - return afi; - } - - private void adjustTitleTextView(int score, TextView tv, int position) { - int viewType = getItemViewType(position); - if (origTitleColors[viewType] == null) - // store original color - origTitleColors[viewType] = Integer.valueOf(tv.getCurrentTextColor()); - - if (score < -500) { - tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } else { - tv.setTextColor(origTitleColors[viewType].intValue()); - tv.setPaintFlags(tv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); - } - } - } - - public void notifyUpdated() { - m_adapter.notifyDataSetChanged(); - } - - public void setActiveArticleId(int articleId) { - m_activeArticleId = articleId; - try { - m_adapter.notifyDataSetChanged(); - - ListView list = getView().findViewById(R.id.headlines_list); - - Log.d(TAG, articleId + " position " + getArticleIdPosition(articleId)); - - if (list != null) { - list.setSelection(getArticleIdPosition(articleId)); - } - } catch (NullPointerException e) { - // invoked before view is created, nvm - } - } - - public Cursor getArticleAtPosition(int position) { - return (Cursor) m_list.getItemAtPosition(position); - } - - public int getArticleIdAtPosition(int position) { - Cursor c = getArticleAtPosition(position); - - if (c != null) { - int id = c.getInt(0); - return id; - } - - return 0; - - //return (int) m_adapter.getItemId(position + m_list.getHeaderViewsCount()); - } - - public int getActiveArticleId() { - return m_activeArticleId; - } - - public int getArticleIdPosition(int articleId) { - for (int i = 0; i < m_adapter.getCount(); i++) { - if (articleId == m_adapter.getItemId(i)) - return i + m_list.getHeaderViewsCount(); - } - - return -1; - } - - public int getArticleCount() { - return m_adapter.getCount(); - } - - public void setSearchQuery(String query) { - if (!m_searchQuery.equals(query)) { - m_searchQuery = query; - } - } - - public int getFeedId() { - return m_feedId; - } - - public boolean getFeedIsCat() { - return m_feedIsCat; - } - - public String getSearchQuery() { - return m_searchQuery; - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (m_prefs.getBoolean("headlines_mark_read_scroll", false) && firstVisibleItem > (m_activity.isSmallScreen() ? 1 : 0)) { - //Article a = m_articles.get(firstVisibleItem - 1); - - Cursor article = getArticleAtPosition(firstVisibleItem - 1); - - if (article != null) { - Integer id = article.getInt(article.getColumnIndex(BaseColumns._ID)); - boolean unread = article.getInt(article.getColumnIndex("unread")) != 0; - - if (unread && !m_readArticleIds.contains(id)) { - m_readArticleIds.add(id); - } - } - } - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - if (scrollState == SCROLL_STATE_IDLE && m_prefs.getBoolean("headlines_mark_read_scroll", false)) { - if (!m_readArticleIds.isEmpty()) { - - SQLiteStatement stmt = m_activity.getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 " + "WHERE " + BaseColumns._ID - + " = ?"); - - for (int articleId : m_readArticleIds) { - stmt.bindLong(1, articleId); - stmt.execute(); - } - - stmt.close(); - - m_autoMarkedArticleIds.addAll(m_readArticleIds); - m_readArticleIds.clear(); - - refresh(); - - } - } - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineMasterActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineMasterActivity.java deleted file mode 100755 index ecff22ca..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineMasterActivity.java +++ /dev/null @@ -1,376 +0,0 @@ -package org.fox.ttrss.offline; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteStatement; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.provider.BaseColumns; -import android.util.Log; -import android.view.Gravity; -import android.view.MenuItem; -import android.view.View; - -import org.fox.ttrss.Application; -import org.fox.ttrss.R; - -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.widget.Toolbar; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentTransaction; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import java.util.HashMap; - -public class OfflineMasterActivity extends OfflineActivity implements OfflineHeadlinesEventListener { - private final String TAG = this.getClass().getSimpleName(); - - private static final int OFFLINE_HEADLINES_REQUEST = 1; - - //private boolean m_actionbarUpEnabled = false; - //private int m_actionbarRevertDepth = 0; - private boolean m_feedIsSelected = false; - //private boolean m_feedWasSelected = false; - - private ActionBarDrawerToggle m_drawerToggle; - private DrawerLayout m_drawerLayout; - - @SuppressLint("NewApi") - @Override - public void onCreate(Bundle savedInstanceState) { - m_prefs = PreferenceManager - .getDefaultSharedPreferences(getApplicationContext()); - - setAppTheme(m_prefs); - - super.onCreate(savedInstanceState); - - if (m_prefs.getBoolean("force_phone_layout", false)) { - setContentView(R.layout.activity_master_phone); - } else { - setContentView(R.layout.activity_master); - } - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - setSmallScreen(findViewById(R.id.sw600dp_anchor) == null); - - Application.getInstance().load(savedInstanceState); - - m_drawerLayout = findViewById(R.id.headlines_drawer); - - if (m_drawerLayout != null) { - - m_drawerToggle = new ActionBarDrawerToggle(this, m_drawerLayout, R.string.blank, R.string.blank) { - @Override - public void onDrawerOpened(View drawerView) { - invalidateOptionsMenu(); - } - - @Override - public void onDrawerClosed(View drawerView) { - invalidateOptionsMenu(); - } - }; - - m_drawerLayout.addDrawerListener(m_drawerToggle); - m_drawerToggle.setDrawerIndicatorEnabled(true); - } - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - - if (savedInstanceState != null) { - - //m_actionbarUpEnabled = savedInstanceState.getBoolean("actionbarUpEnabled"); - //m_actionbarRevertDepth = savedInstanceState.getInt("actionbarRevertDepth"); - m_feedIsSelected = savedInstanceState.getBoolean("feedIsSelected"); - //m_feedWasSelected = savedInstanceState.getBoolean("feedWasSelected"); - - /* if (findViewById(R.id.sw600dp_port_anchor) != null && m_feedWasSelected && m_slidingMenu != null) { - m_slidingMenu.setBehindWidth(getScreenWidthInPixel() * 2/3); - } */ - - if (m_drawerLayout != null && m_feedIsSelected == false) { - m_drawerLayout.openDrawer(Gravity.START); - } - - } else { - if (m_drawerLayout != null) { - m_drawerLayout.openDrawer(Gravity.START); - } - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - - if (m_prefs.getBoolean("enable_cats", false)) { - ft.replace(R.id.feeds_fragment, new OfflineFeedCategoriesFragment(), FRAG_CATS); - } else { - ft.replace(R.id.feeds_fragment, new OfflineFeedsFragment(), FRAG_FEEDS); - } - - ft.commit(); - } - } - - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (m_drawerToggle != null) m_drawerToggle.syncState(); - } - - /*public void openFeedArticles(int feedId, boolean isCat) { - if (isSmallScreen()) { - Intent intent = new Intent(OfflineMasterActivity.this, OfflineDetailActivity.class); - - intent.putExtra("feed", feedId); - intent.putExtra("isCat", isCat); - intent.putExtra("article", 0); - startActivityForResult(intent, 0); - } - }*/ - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (m_drawerToggle != null && m_drawerToggle.onOptionsItemSelected(item)) { - return true; - } - - if (item.getItemId() == R.id.headlines_toggle_sort_order) {/* SharedPreferences.Editor editor = m_prefs.edit(); - editor.putBoolean("offline_oldest_first", !m_prefs.getBoolean("offline_oldest_first", false)); - editor.commit(); - refresh(); */ - - Dialog dialog = new Dialog(this); - - int selectedIndex = m_prefs.getBoolean("offline_oldest_first", false) ? 1 : 0; - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this) - .setTitle(getString(R.string.headlines_sort_articles_title)) - .setSingleChoiceItems( - new String[]{ - getString(R.string.headlines_sort_default), - getString(R.string.headlines_sort_oldest_first) - }, - selectedIndex, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int which) { - switch (which) { - case 0: - if (true) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putBoolean("offline_oldest_first", false); - editor.apply(); - } - break; - case 1: - if (true) { - SharedPreferences.Editor editor = m_prefs.edit(); - editor.putBoolean("offline_oldest_first", true); - editor.apply(); - } - break; - } - dialog.cancel(); - - refresh(); - } - }); - - dialog = builder.create(); - dialog.show(); - - return true; - /* case R.id.show_feeds: - setUnreadOnly(!getUnreadOnly()); - invalidateOptionsMenu(); - refresh(); - return true; */ - } - Log.d(TAG, "onOptionsItemSelected, unhandled id=" + item.getItemId()); - return super.onOptionsItemSelected(item); - } - - @Override - public void onSaveInstanceState(Bundle out) { - super.onSaveInstanceState(out); - - out.putBoolean("feedIsSelected", m_feedIsSelected); - - Application.getInstance().save(out); - } - - public void initMenu() { - super.initMenu(); - - if (m_menu != null) { - Fragment ff = getSupportFragmentManager().findFragmentByTag(FRAG_FEEDS); - Fragment cf = getSupportFragmentManager().findFragmentByTag(FRAG_CATS); - OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - /* if (m_drawerLayout != null) { - boolean isDrawerOpen = m_drawerLayout.isDrawerOpen(Gravity.START); - - m_menu.setGroupVisible(R.id.menu_group_feeds, isDrawerOpen); - m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded() && !isDrawerOpen); - } else { - m_menu.setGroupVisible(R.id.menu_group_feeds, (ff != null && ff.isAdded()) || (cf != null && cf.isAdded())); - m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded()); - } */ - - m_menu.setGroupVisible(R.id.menu_group_feeds, (ff != null && ff.isAdded()) || (cf != null && cf.isAdded())); - m_menu.setGroupVisible(R.id.menu_group_headlines, hf != null && hf.isAdded()); - - //m_menu.findItem(R.id.headlines_toggle_sidebar).setVisible(false); - - /* MenuItem item = m_menu.findItem(R.id.show_feeds); - - if (getUnreadOnly()) { - item.setTitle(R.string.menu_all_feeds); - } else { - item.setTitle(R.string.menu_unread_feeds); - } */ - } - } - - public void onCatSelected(int catId) { - onCatSelected(catId, m_prefs.getBoolean("browse_cats_like_feeds", false)); - } - - public void onCatSelected(int catId, boolean openAsFeed) { - OfflineFeedCategoriesFragment fc = (OfflineFeedCategoriesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_CATS); - - if (openAsFeed) { - if (fc != null) { - fc.setSelectedFeedId(catId); - } - - onFeedSelected(catId, true, true); - } else { - if (fc != null) { - fc.setSelectedFeedId(-1); - } - - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - - OfflineFeedsFragment ff = new OfflineFeedsFragment(); - ff.initialize(catId, true); - - ft.replace(R.id.feeds_fragment, ff, FRAG_FEEDS); - ft.addToBackStack(null); - - //getSupportActionBar().setDisplayHomeAsUpEnabled(true); - //m_actionbarUpEnabled = true; - //m_actionbarRevertDepth = m_actionbarRevertDepth + 1; - - ft.commit(); - } - } - - @Override - public void onBackPressed() { - if (m_drawerLayout != null && !m_drawerLayout.isDrawerOpen(Gravity.START) && - (getSupportFragmentManager().getBackStackEntryCount() > 0 || m_feedIsSelected)) { - - m_drawerLayout.openDrawer(Gravity.START); - } else { - try { - super.onBackPressed(); - } catch (IllegalStateException e) { - // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState - e.printStackTrace(); - } - } - } - - public void onFeedSelected(int feedId) { - onFeedSelected(feedId, false, true); - } - - public void onFeedSelected(final int feedId, final boolean isCat, boolean open) { - - if (open) { - - if (m_drawerLayout != null) { - m_drawerLayout.closeDrawers(); - } - - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - FragmentTransaction ft = getSupportFragmentManager() - .beginTransaction(); - - OfflineHeadlinesFragment hf = new OfflineHeadlinesFragment(); - hf.initialize(feedId, isCat, false); - ft.replace(R.id.headlines_fragment, hf, FRAG_HEADLINES); - - ft.commit(); - - m_feedIsSelected = true; - //m_feedWasSelected = true; - - } - }, 250); - } - } - - @Override - public void onArticleSelected(int articleId, boolean open) { - - if (!open) { - SQLiteStatement stmt = getDatabase().compileStatement( - "UPDATE articles SET modified = 1, unread = 0 " + "WHERE " + BaseColumns._ID - + " = ?"); - - stmt.bindLong(1, articleId); - stmt.execute(); - stmt.close(); - } - - invalidateOptionsMenu(); - - if (open) { - OfflineHeadlinesFragment hf = (OfflineHeadlinesFragment) getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - Intent intent = new Intent(OfflineMasterActivity.this, OfflineDetailActivity.class); - intent.putExtra("feed", hf.getFeedId()); - intent.putExtra("isCat", hf.getFeedIsCat()); - intent.putExtra("article", articleId); - - startActivityForResult(intent, OFFLINE_HEADLINES_REQUEST); - overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); - - } else { - refresh(); - } - - } - - @Override - public void onArticleSelected(int articleId) { - onArticleSelected(articleId, true); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == OFFLINE_HEADLINES_REQUEST) { - - OfflineHeadlinesFragment ohf = (OfflineHeadlinesFragment)getSupportFragmentManager().findFragmentByTag(FRAG_HEADLINES); - - if (ohf != null) { - ohf.refresh(); - } - } - - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineUploadService.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineUploadService.java deleted file mode 100755 index efbeef2c..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/offline/OfflineUploadService.java +++ /dev/null @@ -1,282 +0,0 @@ -package org.fox.ttrss.offline; - -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.BitmapFactory; -import android.os.Build; -import android.util.Log; - -import com.google.gson.JsonElement; - -import org.fox.ttrss.ApiRequest; -import org.fox.ttrss.CommonActivity; -import org.fox.ttrss.OnlineActivity; -import org.fox.ttrss.R; -import org.fox.ttrss.util.DatabaseHelper; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import androidx.core.app.NotificationCompat; - -public class OfflineUploadService extends IntentService { - private final String TAG = this.getClass().getSimpleName(); - - public static final int NOTIFY_UPLOADING = 2; - public static final String INTENT_ACTION_SUCCESS = "org.fox.ttrss.intent.action.UploadComplete"; - - private String m_sessionId; - private NotificationManager m_nmgr; - private boolean m_uploadInProgress = false; - private boolean m_batchMode = false; - private DatabaseHelper m_databaseHelper; - - public OfflineUploadService() { - super("OfflineUploadService"); - } - - @Override - public void onCreate() { - super.onCreate(); - m_nmgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - - initDatabase(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - m_nmgr.cancel(NOTIFY_UPLOADING); - } - - @SuppressWarnings("deprecation") - private void updateNotification(String msg, int progress, int max, boolean showProgress, boolean isError) { - PendingIntent contentIntent = PendingIntent.getActivity(this, 0, - new Intent(this, OnlineActivity.class), PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) - .setContentText(msg) - .setContentTitle(getString(R.string.notify_uploading_title)) - .setContentIntent(contentIntent) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.baseline_cloud_upload_24) - .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), - R.drawable.ic_launcher)) - .setOngoing(!isError) - .setOnlyAlertOnce(true) - .setVibrate(new long[0]); - - if (showProgress) builder.setProgress(max, progress, max == 0); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - builder.setCategory(Notification.CATEGORY_PROGRESS) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(0x88b0f0) - .setGroup("org.fox.ttrss") - .addAction(R.drawable.ic_launcher, getString(R.string.offline_sync_try_again), contentIntent); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(CommonActivity.NOTIFICATION_CHANNEL_NORMAL); - } - - m_nmgr.notify(NOTIFY_UPLOADING, builder.build()); - } - - private void updateNotification(int msgResId, int progress, int max, boolean showProgress, boolean isError) { - updateNotification(getString(msgResId), progress, max, showProgress, isError); - } - - private void initDatabase() { - m_databaseHelper = DatabaseHelper.getInstance(this); - } - - private synchronized SQLiteDatabase getDatabase() { - return m_databaseHelper.getWritableDatabase(); - } - - private enum ModifiedCriteria { - READ, MARKED, UNMARKED, PUBLISHED, UNPUBLISHED - } - - private List<Integer> getModifiedIds(ModifiedCriteria criteria) { - - String criteriaStr = ""; - - switch (criteria) { - case READ: - criteriaStr = "unread = 0"; - break; - case MARKED: - criteriaStr = "modified_marked = 1 AND marked = 1"; - break; - case UNMARKED: - criteriaStr = "modified_marked = 1 AND marked = 0"; - break; - case PUBLISHED: - criteriaStr = "modified_published = 1 AND published = 1"; - break; - case UNPUBLISHED: - criteriaStr = "modified_published = 1 AND published = 0"; - break; - } - - Cursor c = getDatabase().query("articles", null, - "modified = 1 AND " + criteriaStr, null, null, null, null); - - List<Integer> tmp = new ArrayList<>(); - - while (c.moveToNext()) { - tmp.add(c.getInt(0)); - } - - c.close(); - - return tmp; - } - - private void uploadFailed() { - m_uploadInProgress = false; - } - - private void uploadSuccess() { - getDatabase().execSQL("UPDATE articles SET modified = 0"); - - if (m_batchMode) { - - SharedPreferences localPrefs = getSharedPreferences("localprefs", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = localPrefs.edit(); - editor.putBoolean("offline_mode_active", false); - editor.apply(); - - } else { - Intent intent = new Intent(); - intent.setAction(INTENT_ACTION_SUCCESS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - sendBroadcast(intent); - } - - m_uploadInProgress = false; - - m_nmgr.cancel(NOTIFY_UPLOADING); - } - - interface CriteriaCallback { - void onUploadSuccess(); - } - - private void uploadByCriteria(final ModifiedCriteria criteria, final CriteriaCallback callback) { - - final List<Integer> ids = getModifiedIds(criteria); - - Log.d(TAG, "syncing modified offline data for " + criteria + ": " + ids); - - if (ids.size() > 0) { - ApiRequest req = new ApiRequest(getApplicationContext()) { - @Override - protected void onPostExecute(JsonElement result) { - if (result != null) { - callback.onUploadSuccess(); - } else { - Log.d(TAG, "syncing failed: " + getErrorMessage()); - - updateNotification(getErrorMessage(), 0, 0, false, true); - uploadFailed(); - } - } - }; - - @SuppressWarnings("serial") - HashMap<String, String> map = new HashMap<String, String>(); - map.put("sid", m_sessionId); - map.put("op", "updateArticle"); - map.put("article_ids", android.text.TextUtils.join(",", ids)); - - switch (criteria) { - case READ: - map.put("mode", "0"); - map.put("field", "2"); - break; - case PUBLISHED: - map.put("mode", "1"); - map.put("field", "1"); - break; - case UNPUBLISHED: - map.put("mode", "0"); - map.put("field", "1"); - break; - case MARKED: - map.put("mode", "1"); - map.put("field", "0"); - break; - case UNMARKED: - map.put("mode", "0"); - map.put("field", "0"); - break; - } - - req.execute(map); - } else { - callback.onUploadSuccess(); - } - } - - @Override - protected void onHandleIntent(Intent intent) { - try { - if (getDatabase().isDbLockedByCurrentThread() || getDatabase().isDbLockedByOtherThreads()) { - return; - } - - m_sessionId = intent.getStringExtra("sessionId"); - m_batchMode = intent.getBooleanExtra("batchMode", false); - - if (!m_uploadInProgress) { - m_uploadInProgress = true; - - updateNotification(R.string.notify_uploading_sending_data, 0, 0, true, true); - - uploadByCriteria(ModifiedCriteria.READ, new CriteriaCallback() { - @Override - public void onUploadSuccess() { - uploadByCriteria(ModifiedCriteria.MARKED, new CriteriaCallback() { - @Override - public void onUploadSuccess() { - uploadByCriteria(ModifiedCriteria.UNMARKED, new CriteriaCallback() { - @Override - public void onUploadSuccess() { - uploadByCriteria(ModifiedCriteria.PUBLISHED, new CriteriaCallback() { - @Override - public void onUploadSuccess() { - uploadByCriteria(ModifiedCriteria.UNPUBLISHED, new CriteriaCallback() { - @Override - public void onUploadSuccess() { - Log.d(TAG, "upload complete"); - - uploadSuccess(); - } - }); - } - }); - } - }); - } - }); - } - }); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - -} diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DetailActivityScrollingViewBehavior.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DetailActivityScrollingViewBehavior.java index 85e8ae4b..27682b13 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DetailActivityScrollingViewBehavior.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/DetailActivityScrollingViewBehavior.java @@ -6,15 +6,16 @@ import android.preference.PreferenceManager; import android.util.AttributeSet; import android.view.View; +import androidx.annotation.NonNull; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.view.ViewCompat; + import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.bottomappbar.BottomAppBar; import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.util.List; -import androidx.annotation.NonNull; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.view.ViewCompat; - public class DetailActivityScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior { private SharedPreferences m_prefs; @@ -29,7 +30,7 @@ public class DetailActivityScrollingViewBehavior extends AppBarLayout.ScrollingV @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { return super.layoutDependsOn(parent, child, dependency) || - dependency instanceof FloatingActionButton; + dependency instanceof FloatingActionButton || dependency instanceof BottomAppBar; } @Override @@ -54,6 +55,8 @@ public class DetailActivityScrollingViewBehavior extends AppBarLayout.ScrollingV for (View view : dependencies) { if (view instanceof FloatingActionButton) { ((FloatingActionButton) view).hide(); + } else if (view instanceof BottomAppBar) { + ((BottomAppBar) view).performHide(); } } } else if (dy < 0) { @@ -62,6 +65,8 @@ public class DetailActivityScrollingViewBehavior extends AppBarLayout.ScrollingV for (View view : dependencies) { if (view instanceof FloatingActionButton) { ((FloatingActionButton) view).show(); + } else if (view instanceof BottomAppBar) { + ((BottomAppBar) view).performShow(); } } } diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeaderViewRecyclerAdapter.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeaderViewRecyclerAdapter.java index 519fded5..fb2218c1 100755 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeaderViewRecyclerAdapter.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/HeaderViewRecyclerAdapter.java @@ -19,13 +19,13 @@ package org.fox.ttrss.util; import android.view.View; import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.recyclerview.widget.RecyclerView; - /** * <p> * RecyclerView adapter designed to wrap an existing adapter allowing the addition of diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ImageCacheService.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ImageCacheService.java deleted file mode 100755 index b2ae08f7..00000000 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/util/ImageCacheService.java +++ /dev/null @@ -1,323 +0,0 @@ -package org.fox.ttrss.util; - -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.BitmapFactory; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -import org.fox.ttrss.CommonActivity; -import org.fox.ttrss.OnlineActivity; -import org.fox.ttrss.R; -import org.fox.ttrss.offline.OfflineDownloadService; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Date; - -import androidx.core.app.NotificationCompat; - -public class ImageCacheService extends IntentService { - - @SuppressWarnings("unused") - private final String TAG = this.getClass().getSimpleName(); - - public static final int NOTIFY_DOWNLOADING = 1; - public static final int NOTIFY_DOWNLOAD_SUCCESS = 2; - - public static final String INTENT_ACTION_ICS_STOP = "org.fox.ttrss.intent.action.ICSStop"; - - private static final String CACHE_PATH = "/image-cache/"; - - private int m_imagesDownloaded = 0; - private boolean m_canProceed = true; - - private NotificationManager m_nmgr; - private BroadcastReceiver m_receiver; - private int m_queueSize = 0; - - public ImageCacheService() { - super("ImageCacheService"); - } - - private boolean isDownloadServiceRunning() { - ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); - for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if ("org.fox.ttrss.OfflineDownloadService".equals(service.service.getClassName())) { - return true; - } - } - return false; - } - - - @Override - public void onCreate() { - super.onCreate(); - m_nmgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - - m_receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "received broadcast action: " + intent.getAction()); - - if (INTENT_ACTION_ICS_STOP.equals(intent.getAction())) { - m_canProceed = false; - } - } - }; - - IntentFilter filter = new IntentFilter(); - filter.addAction(INTENT_ACTION_ICS_STOP); - filter.addCategory(Intent.CATEGORY_DEFAULT); - - registerReceiver(m_receiver, filter); - } - - public static boolean isUrlCached(Context context, String url) { - String hashedUrl = md5(url); - - File storage = context.getExternalCacheDir(); - - File file = new File(storage.getAbsolutePath() + CACHE_PATH + "/" + hashedUrl); - - return file.exists(); - } - - public static String getCacheFileName(Context context, String url) { - String hashedUrl = md5(url); - - File storage = context.getExternalCacheDir(); - - File file = new File(storage.getAbsolutePath() + CACHE_PATH + "/" + hashedUrl); - - return file.getAbsolutePath(); - } - - public static void cleanupCache(Context context, boolean deleteAll) { - if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - File storage = context.getExternalCacheDir(); - File cachePath = new File(storage.getAbsolutePath() + CACHE_PATH); - - long now = new Date().getTime(); - - if (cachePath.isDirectory()) { - for (File file : cachePath.listFiles()) { - if (deleteAll || now - file.lastModified() > 1000*60*60*24*7) { - file.delete(); - } - } - } - } - } - - protected static String md5(String s) { - try { - MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); - digest.update(s.getBytes()); - byte messageDigest[] = digest.digest(); - - StringBuilder hexString = new StringBuilder(); - for (int i=0; i<messageDigest.length; i++) - hexString.append(Integer.toHexString(0xFF & messageDigest[i])); - - return hexString.toString(); - - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - - return null; - } - - private InputStream getStream(String urlString) { - try { - URL url = new URL(urlString); - URLConnection urlConnection = url.openConnection(); - urlConnection.setConnectTimeout(250); - urlConnection.setReadTimeout(5*1000); - return urlConnection.getInputStream(); - } catch (Exception ex) { - return null; - } - } - - @SuppressWarnings("deprecation") - private void notifyDownloadSuccess() { - Intent intent = new Intent(this, OnlineActivity.class); - intent.setAction(OfflineDownloadService.INTENT_ACTION_SWITCH_OFFLINE); - - PendingIntent contentIntent = PendingIntent.getActivity(this, OfflineDownloadService.PI_SUCCESS, - intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) - .setContentTitle(getString(R.string.dialog_offline_success)) - .setContentText(getString(R.string.offline_tap_to_switch)) - .setContentIntent(contentIntent) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.ic_notification) - .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), - R.drawable.ic_launcher)) - .setOnlyAlertOnce(true) - .setPriority(Notification.PRIORITY_HIGH) - .setDefaults(Notification.DEFAULT_ALL) - .setAutoCancel(true); - - builder.setCategory(Notification.CATEGORY_MESSAGE) - .setVibrate(new long[0]) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setColor(0x88b0f0) - .setGroup("org.fox.ttrss"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(CommonActivity.NOTIFICATION_CHANNEL_PRIORITY); - } - - m_nmgr.notify(NOTIFY_DOWNLOAD_SUCCESS, builder.build()); - } - - - @SuppressWarnings("deprecation") - private void updateNotification(String msg, int progress, int max, boolean showProgress) { - Intent intent = new Intent(this, OnlineActivity.class); - - PendingIntent contentIntent = PendingIntent.getActivity(this, OfflineDownloadService.PI_GENERIC, - intent, PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) - .setContentText(msg) - .setContentTitle(getString(R.string.notify_downloading_title)) - .setContentIntent(contentIntent) - .setWhen(System.currentTimeMillis()) - .setSmallIcon(R.drawable.baseline_cloud_download_24) - .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), - R.drawable.ic_launcher)) - .setOngoing(true) - .setOnlyAlertOnce(true); - - if (showProgress) builder.setProgress(max, progress, max == 0); - - intent = new Intent(this, OnlineActivity.class); - intent.setAction(OfflineDownloadService.INTENT_ACTION_CANCEL); - - PendingIntent cancelIntent = PendingIntent.getActivity(this, - OfflineDownloadService.PI_CANCEL, intent, PendingIntent.FLAG_IMMUTABLE); - - builder.setCategory(Notification.CATEGORY_PROGRESS) - .setVibrate(new long[0]) - .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) - .setColor(0x88b0f0) - .setGroup("org.fox.ttrss") - .addAction(R.drawable.ic_launcher, getString(R.string.cancel), cancelIntent); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(CommonActivity.NOTIFICATION_CHANNEL_NORMAL); - } - - m_nmgr.notify(NOTIFY_DOWNLOADING, builder.build()); - } - - /* private void updateNotification(int msgResId) { - updateNotification(getString(msgResId)); - } */ - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - - m_queueSize++; - - return super.onStartCommand(intent, flags, startId); - } - - @Override - protected void onHandleIntent(Intent intent) { - - m_queueSize--; - m_imagesDownloaded++; - - String url = intent.getStringExtra("url"); - - Log.d(TAG, "got request to download URL=" + url + "; canProceed=" + m_canProceed); - - if (!m_canProceed || !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) - return; - - String hashedUrl = md5(url); - - File storage = getExternalCacheDir(); - File cachePath = new File(storage.getAbsolutePath() + CACHE_PATH); - if (!cachePath.exists()) cachePath.mkdirs(); - - if (cachePath.isDirectory() && hashedUrl != null) { - File outputFile = new File(cachePath.getAbsolutePath() + "/" + hashedUrl); - - if (!outputFile.exists()) { - - //Log.d(TAG, "downloading to " + outputFile.getAbsolutePath()); - - InputStream is = getStream(url); - - if (is != null) { - try { - FileOutputStream fos = new FileOutputStream(outputFile); - - byte[] buffer = new byte[1024]; - int len = 0; - while ((len = is.read(buffer)) != -1) { - fos.write(buffer, 0, len); - } - - fos.close(); - is.close(); - - updateNotification(getString(R.string.notify_downloading_media), m_imagesDownloaded, - m_imagesDownloaded+m_queueSize, true); - - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - } - - @Override - public void onDestroy() { - super.onDestroy(); - - if (!isDownloadServiceRunning()) { - m_nmgr.cancel(NOTIFY_DOWNLOADING); - - /*Intent success = new Intent(); - success.setAction(OfflineDownloadService.INTENT_ACTION_SUCCESS); - success.addCategory(Intent.CATEGORY_DEFAULT); - sendBroadcast(success);*/ - - try { - unregisterReceiver(m_receiver); - } catch (Exception e) { - e.printStackTrace(); - } - - if (m_canProceed) { - notifyDownloadSuccess(); - } - } - } - -} 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 b9ec704e..3e2d2eaf 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 @@ -14,6 +14,9 @@ import android.preference.PreferenceManager; import android.util.Log; import android.widget.RemoteViews; +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -24,9 +27,6 @@ import org.fox.ttrss.util.SimpleLoginManager; import java.util.HashMap; -import androidx.annotation.NonNull; -import androidx.core.app.JobIntentService; - public class WidgetUpdateService extends JobIntentService { private final String TAG = this.getClass().getSimpleName(); private SharedPreferences m_prefs; diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_mark_as_unread_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_mark_as_unread_24.xml deleted file mode 100644 index 94381df3..00000000 --- a/org.fox.ttrss/src/main/res/drawable/baseline_mark_as_unread_24.xml +++ /dev/null @@ -1,5 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> - - <path android:fillColor="@android:color/white" android:pathData="M18.83,7h-2.6L10.5,4 4,7.4L4,17c-1.1,0 -2,-0.9 -2,-2L2,7.17c0,-0.53 0.32,-1.09 0.8,-1.34L10.5,2l7.54,3.83c0.43,0.23 0.73,0.7 0.79,1.17zM20,8L7,8c-1.1,0 -2,0.9 -2,2v9c0,1.1 0.9,2 2,2h13c1.1,0 2,-0.9 2,-2v-9c0,-1.1 -0.9,-2 -2,-2zM20,11.67L13.5,15 7,11.67L7,10l6.5,3.33L20,10v1.67z"/> - -</vector> diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_mark_email_unread_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_mark_email_unread_24.xml new file mode 100644 index 00000000..929f5ece --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/baseline_mark_email_unread_24.xml @@ -0,0 +1,5 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M22,8.98V18c0,1.1 -0.9,2 -2,2H4c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h10.1C14.04,4.32 14,4.66 14,5c0,1.48 0.65,2.79 1.67,3.71L12,11L4,6v2l8,5l5.3,-3.32C17.84,9.88 18.4,10 19,10C20.13,10 21.16,9.61 22,8.98zM16,5c0,1.66 1.34,3 3,3s3,-1.34 3,-3s-1.34,-3 -3,-3S16,3.34 16,5z"/> + +</vector> diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_new_label_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_new_label_24.xml new file mode 100644 index 00000000..3eb7690c --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/baseline_new_label_24.xml @@ -0,0 +1,5 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M21,12l-4.37,6.16C16.26,18.68 15.65,19 15,19h-3l0,-6H9v-3H3V7c0,-1.1 0.9,-2 2,-2h10c0.65,0 1.26,0.31 1.63,0.84L21,12zM10,15H7v-3H5v3H2v2h3v3h2v-3h3V15z"/> + +</vector> diff --git a/org.fox.ttrss/src/main/res/drawable/baseline_note_add_24.xml b/org.fox.ttrss/src/main/res/drawable/baseline_note_add_24.xml new file mode 100644 index 00000000..e1ffd854 --- /dev/null +++ b/org.fox.ttrss/src/main/res/drawable/baseline_note_add_24.xml @@ -0,0 +1,5 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,16h-3v3h-2v-3L8,16v-2h3v-3h2v3h3v2zM13,9L13,3.5L18.5,9L13,9z"/> + +</vector> diff --git a/org.fox.ttrss/src/main/res/layout-sw600dp-land/activity_detail.xml b/org.fox.ttrss/src/main/res/layout-sw600dp-land/activity_detail.xml index 09534bc7..3078f77e 100644 --- a/org.fox.ttrss/src/main/res/layout-sw600dp-land/activity_detail.xml +++ b/org.fox.ttrss/src/main/res/layout-sw600dp-land/activity_detail.xml @@ -57,14 +57,22 @@ app:layout_behavior=".util.DetailActivityScrollingViewBehavior" android:layout_height="match_parent"/> + <com.google.android.material.bottomappbar.BottomAppBar + android:id="@+id/detail_bottom_appbar" + app:backgroundTint="?colorSurfaceContainerHigh" + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_gravity="bottom" + app:menu="@menu/bottombar_detail" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/detail_fab" + android:layout_gravity="end|bottom" + android:layout_marginEnd="16dp" + android:layout_marginBottom="38dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="end|bottom" - android:visibility="gone" - android:src="@drawable/baseline_open_in_browser_24" - android:layout_margin="16dp" /> + app:srcCompat="@drawable/baseline_open_in_browser_24" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> </LinearLayout> diff --git a/org.fox.ttrss/src/main/res/layout/fragment_article.xml b/org.fox.ttrss/src/main/res/layout/fragment_article.xml index e75980b0..eba96d75 100755 --- a/org.fox.ttrss/src/main/res/layout/fragment_article.xml +++ b/org.fox.ttrss/src/main/res/layout/fragment_article.xml @@ -1,157 +1,145 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.otaliastudios.nestedscrollcoordinatorlayout.NestedScrollCoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/article_fragment" - android:layout_width="fill_parent" android:background="?colorSurfaceContainer" + android:layout_width="match_parent" android:layout_height="match_parent"> - <FrameLayout - android:id="@+id/article_fullscreen_video" - android:visibility="invisible" - android:layout_width="fill_parent" - android:layout_height="fill_parent"/> - - <com.otaliastudios.nestedscrollcoordinatorlayout.NestedScrollCoordinatorLayout + <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" + android:background="?colorSurfaceContainer" android:layout_height="wrap_content"> - <com.google.android.material.appbar.AppBarLayout + <com.google.android.material.card.MaterialCardView + app:layout_scrollFlags="scroll|enterAlways" + app:strokeWidth="0dp" + app:shapeAppearance="@style/ShapeAppearanceArticleHeader" android:layout_width="match_parent" - android:background="?colorSurfaceContainer" - android:layout_height="match_parent"> + android:layout_height="wrap_content"> - <com.google.android.material.card.MaterialCardView - app:layout_scrollFlags="scroll|enterAlways" - app:strokeWidth="0dp" - app:shapeAppearance="@style/ShapeAppearanceArticleHeader" - android:layout_width="match_parent" - android:layout_height="wrap_content"> + <LinearLayout + android:id="@+id/article_header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> <LinearLayout - android:id="@+id/article_header" + android:orientation="horizontal" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> + android:layout_height="match_parent"> - <LinearLayout - android:orientation="horizontal" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" + <TextView + android:id="@+id/title" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="wrap_content" + android:layout_weight="1" + android:textColor="?colorPrimary" + tools:text="Unreasonably long article title blah blah blah blah blah blah" + android:textSize="18sp" + android:gravity="start" /> - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="1" - android:textColor="?colorPrimary" - tools:text="Unreasonably long article title blah blah blah blah blah blah" - android:textSize="18sp" - android:gravity="start" /> + </LinearLayout> - </LinearLayout> + <TextView + android:id="@+id/comments" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + tools:text="comments" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="viewStart" + android:layout_weight="1" + android:fontFamily="sans-serif-light" + android:layout_marginTop="8dp" + android:textColor="?colorPrimary" + android:textSize="12sp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="8dp" + android:layout_marginTop="8dp"> <TextView - android:id="@+id/comments" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - tools:text="comments" - android:layout_width="match_parent" + android:id="@+id/tags" + tools:text="alpha, beta, gamma" + android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_weight="0.7" + android:ellipsize="middle" android:textAlignment="viewStart" - android:layout_weight="1" + android:singleLine="true" android:fontFamily="sans-serif-light" - android:layout_marginTop="8dp" - android:textColor="?colorPrimary" + android:textColor="?colorSecondary" android:textSize="12sp" /> - <LinearLayout - android:layout_width="match_parent" + <TextView + android:id="@+id/date" + tools:text="Jan 01, 1970" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:layout_marginBottom="8dp" - android:layout_marginTop="8dp"> - - <TextView - android:id="@+id/tags" - tools:text="alpha, beta, gamma" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="0.7" - android:ellipsize="middle" - android:textAlignment="viewStart" - android:singleLine="true" - android:fontFamily="sans-serif-light" - android:textColor="?colorSecondary" - android:textSize="12sp" /> - - <TextView - android:id="@+id/date" - tools:text="Jan 01, 1970" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="0.3" - android:textAlignment="viewEnd" - android:fontFamily="sans-serif-light" - android:textAppearance="?android:attr/textAppearanceSmall" - android:textColor="?colorSecondary" - android:textSize="12sp" /> - - </LinearLayout> - - <LinearLayout + android:layout_weight="0.3" + android:textAlignment="viewEnd" + android:fontFamily="sans-serif-light" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?colorSecondary" + android:textSize="12sp" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.google.android.material.card.MaterialCardView + android:id="@+id/note_container" android:layout_width="match_parent" - android:layout_height="wrap_content"> + app:cardBackgroundColor="?colorTertiaryContainer" + android:layout_margin="8dp" + app:strokeWidth="0dp" + android:layout_height="match_parent"> - <com.google.android.material.card.MaterialCardView - android:id="@+id/note_container" + <LinearLayout android:layout_width="match_parent" - app:cardBackgroundColor="?colorTertiaryContainer" android:layout_margin="8dp" - app:strokeWidth="0dp" - android:layout_height="match_parent"> + android:layout_height="wrap_content"> - <LinearLayout + <TextView + android:id="@+id/note" + tools:text="Article note (if any...)" + android:layout_gravity="center_vertical" android:layout_width="match_parent" - android:layout_margin="8dp" - android:layout_height="wrap_content"> - - <TextView - android:id="@+id/note" - tools:text="Article note (if any...)" - android:layout_gravity="center_vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="8dp" - android:textColor="?colorOnTertiaryContainer" - android:layout_weight="1" - android:textSize="12sp"/> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - app:tint="?colorOnTertiaryContainer" - android:src="@drawable/baseline_note_24"/> - - </LinearLayout> - </com.google.android.material.card.MaterialCardView> - - </LinearLayout> - </LinearLayout> - </com.google.android.material.card.MaterialCardView> + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textColor="?colorOnTertiaryContainer" + android:layout_weight="1" + android:textSize="12sp"/> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + app:tint="?colorOnTertiaryContainer" + android:src="@drawable/baseline_note_24"/> + + </LinearLayout> + </com.google.android.material.card.MaterialCardView> - </com.google.android.material.appbar.AppBarLayout> + </LinearLayout> + </LinearLayout> + </com.google.android.material.card.MaterialCardView> - <com.telefonica.nestedscrollwebview.NestedScrollWebView - android:id="@+id/article_content" - app:layout_behavior="@string/appbar_scrolling_view_behavior" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + </com.google.android.material.appbar.AppBarLayout> - </com.otaliastudios.nestedscrollcoordinatorlayout.NestedScrollCoordinatorLayout> + <com.telefonica.nestedscrollwebview.NestedScrollWebView + android:id="@+id/article_content" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + android:layout_width="match_parent" + android:layout_height="match_parent" /> -</FrameLayout>
\ No newline at end of file +</com.otaliastudios.nestedscrollcoordinatorlayout.NestedScrollCoordinatorLayout> diff --git a/org.fox.ttrss/src/main/res/layout/fragment_article_pager.xml b/org.fox.ttrss/src/main/res/layout/fragment_article_pager.xml index 1a687b1d..2f962418 100755 --- a/org.fox.ttrss/src/main/res/layout/fragment_article_pager.xml +++ b/org.fox.ttrss/src/main/res/layout/fragment_article_pager.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.viewpager.widget.ViewPager +<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/article_pager" android:layout_width="match_parent" diff --git a/org.fox.ttrss/src/main/res/layout/fragment_headlines_offline.xml b/org.fox.ttrss/src/main/res/layout/fragment_headlines_offline.xml deleted file mode 100755 index 4ce8c1dd..00000000 --- a/org.fox.ttrss/src/main/res/layout/fragment_headlines_offline.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/headlines_fragment" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <androidx.swiperefreshlayout.widget.SwipeRefreshLayout - android:id="@+id/headlines_swipe_container" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <ListView - android:id="@+id/headlines_list" - android:background="?colorSurfaceContainer" - android:drawSelectorOnTop="true" - android:padding="8dp" - android:dividerHeight="8dp" - android:divider="@null" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> - -</FrameLayout>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml index fd328276..352aad91 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact.xml @@ -4,21 +4,23 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/headlines_row" - android:padding="@dimen/activity_horizontal_margin" + android:paddingTop="16dp" + android:paddingBottom="16dp" + android:paddingStart="16dp" + android:paddingEnd="8dp" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - tools:ignore="HardcodedText" > + android:orientation="horizontal"> <FrameLayout - android:layout_marginRight="16dp" - android:layout_gravity="center_vertical|left" + android:layout_gravity="center_vertical|start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="16dp"> <ImageView android:clickable="true" + android:focusable="true" android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" @@ -36,7 +38,6 @@ <LinearLayout android:layout_weight="1" android:orientation="vertical" - android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -44,94 +45,64 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" - android:textAlignment="viewStart" android:maxLines="2" android:ellipsize="end" tools:text="Sample entry title" android:textColor="?colorPrimary" - android:textSize="18sp" - android:layout_span="2" /> + android:textSize="18sp" /> + + <TextView + android:id="@+id/feed_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:fontFamily="sans-serif-light" + android:singleLine="true" + tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" + android:textColor="?colorSecondary" + android:textSize="12sp" + android:layout_marginTop="4dp" /> <TextView android:id="@+id/excerpt" android:maxLines="2" - android:textAlignment="viewStart" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="match_parent" tools:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." android:textColor="?colorSecondary" android:textSize="13sp" - android:layout_span="2" - android:paddingTop="4dp" /> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <TextView - android:id="@+id/feed_title" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:ellipsize="middle" - android:fontFamily="sans-serif-light" - android:gravity="center_vertical" - android:singleLine="true" - tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" - android:textAlignment="viewStart" - android:textColor="?colorSecondary" - android:textSize="12sp" - android:layout_weight="0.5" - android:paddingTop="4dp" /> - - </LinearLayout> - + android:layout_marginTop="4dp" /> </LinearLayout> <LinearLayout - android:id="@+id/headline_footer" android:orientation="vertical" android:layout_weight="0" - android:layout_width="wrap_content" + android:layout_width="48dp" android:layout_height="match_parent"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:singleLine="true" android:ellipsize="none" - android:layout_weight="1" + android:textAlignment="viewEnd" + android:layout_weight="0.5" tools:text="Jan 01" - android:textColor="?colorPrimary" + android:textColor="?colorSecondary" android:textSize="12sp" /> - <com.google.android.material.button.MaterialButton android:id="@+id/marked" style="?attr/materialIconButtonStyle" android:layout_width="wrap_content" - android:layout_gravity="bottom|end" - android:layout_height="wrap_content" - android:layout_weight="0" - android:clickable="true" + android:paddingEnd="0dp" + android:layout_gravity="end" + android:layout_height="24dp" + android:layout_weight="0.5" app:icon="?ic_star_outline" /> - </LinearLayout> - - <!-- <ImageView - android:id="@+id/article_menu_button" - android:layout_gravity="center" - android:layout_width="wrap_content" - android:layout_height="16dp" - android:layout_weight="0" - android:paddingLeft="4dp" - android:paddingRight="4dp" - android:src="@drawable/ic_action_overflow" /> --> - - </LinearLayout>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected.xml index 6030df54..5721dcb8 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected.xml @@ -4,16 +4,16 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/headlines_row" - android:padding="@dimen/activity_horizontal_margin" + android:paddingTop="16dp" + android:paddingBottom="16dp" + android:paddingStart="16dp" + android:paddingEnd="8dp" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="horizontal" android:background="?colorTertiaryContainer" - android:textColor="?colorOnTertiaryContainer" - tools:ignore="HardcodedText" > + android:orientation="horizontal"> <FrameLayout - android:layout_marginRight="16dp" android:layout_gravity="center_vertical|start" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -21,6 +21,7 @@ <ImageView android:clickable="true" + android:focusable="true" android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" @@ -38,7 +39,6 @@ <LinearLayout android:layout_weight="1" android:orientation="vertical" - android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -46,91 +46,63 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" - android:textAlignment="viewStart" android:maxLines="2" android:ellipsize="end" tools:text="Sample entry title" - android:textSize="18sp" - android:layout_span="2" /> + android:textColor="?colorOnTertiaryContainer" + android:textSize="18sp" /> + + <TextView + android:id="@+id/feed_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:fontFamily="sans-serif-light" + android:singleLine="true" + tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" + android:textColor="?colorOnTertiaryContainer" + android:textSize="12sp" + android:layout_marginTop="4dp" /> <TextView android:id="@+id/excerpt" android:maxLines="2" - android:textAlignment="viewStart" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="match_parent" tools:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + android:textColor="?colorOnTertiaryContainer" android:textSize="13sp" - android:layout_span="2" - android:paddingTop="4dp" /> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <TextView - android:id="@+id/feed_title" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:ellipsize="middle" - android:fontFamily="sans-serif-light" - android:gravity="center_vertical" - android:singleLine="true" - tools:text="Example Feed" - android:textAlignment="viewStart" - android:textSize="12sp" - android:layout_weight="0.5" - android:paddingTop="4dp" /> - - </LinearLayout> - + android:layout_marginTop="4dp" /> </LinearLayout> <LinearLayout - android:id="@+id/headline_footer" android:orientation="vertical" android:layout_weight="0" - android:layout_width="wrap_content" + android:layout_width="48dp" android:layout_height="match_parent"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:singleLine="true" android:ellipsize="none" - android:layout_weight="1" + android:textAlignment="viewEnd" + android:layout_weight="0.5" tools:text="Jan 01" android:textColor="?colorSecondary" android:textSize="12sp" /> - <com.google.android.material.button.MaterialButton android:id="@+id/marked" style="?attr/materialIconButtonStyle" android:layout_width="wrap_content" - android:layout_gravity="bottom|end" - android:layout_height="wrap_content" - android:layout_weight="0" - android:clickable="true" + android:paddingEnd="0dp" + android:layout_height="24dp" + android:layout_weight="0.5" app:icon="?ic_star_outline" /> - </LinearLayout> - - <!-- <ImageView - android:id="@+id/article_menu_button" - android:layout_gravity="center" - android:layout_width="wrap_content" - android:layout_height="16dp" - android:layout_weight="0" - android:paddingLeft="4dp" - android:paddingRight="4dp" - android:src="@drawable/ic_action_overflow" /> --> - - </LinearLayout>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected_unread.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected_unread.xml index 0d8d930d..06813e04 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected_unread.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_selected_unread.xml @@ -4,38 +4,41 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/headlines_row" - android:padding="@dimen/activity_horizontal_margin" + android:paddingTop="16dp" + android:paddingBottom="16dp" + android:paddingStart="16dp" + android:paddingEnd="8dp" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="horizontal" android:background="?colorTertiaryContainer" - tools:ignore="HardcodedText" > + android:orientation="horizontal"> <FrameLayout - android:layout_marginRight="8dp" android:layout_gravity="center_vertical|start" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="8dp"> + android:layout_marginEnd="16dp"> <ImageView android:clickable="true" + android:focusable="true" android:layout_width="48dp" android:layout_height="48dp" + android:layout_gravity="center" android:id="@+id/text_image"/> <ImageView android:layout_width="48dp" android:layout_height="48dp" android:src="@drawable/check_sm" - android:id="@+id/text_checked"/> + android:id="@+id/text_checked" + android:layout_gravity="center" /> </FrameLayout> <LinearLayout android:layout_weight="1" android:orientation="vertical" - android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -43,89 +46,65 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" - android:textAlignment="viewStart" android:maxLines="2" android:ellipsize="end" tools:text="Sample entry title" + android:textColor="?colorOnTertiaryContainer" android:textStyle="bold" - android:textSize="18sp" - android:layout_span="2" /> + android:textSize="18sp" /> + + <TextView + android:id="@+id/feed_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:fontFamily="sans-serif-light" + android:singleLine="true" + tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" + android:textColor="?colorOnTertiaryContainer" + android:textSize="12sp" + android:layout_marginTop="4dp" /> <TextView android:id="@+id/excerpt" android:maxLines="2" - android:textAlignment="viewStart" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="match_parent" tools:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + android:textColor="?colorOnTertiaryContainer" android:textSize="13sp" - android:layout_span="2" - android:paddingTop="4dp" /> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <TextView - android:id="@+id/feed_title" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:ellipsize="middle" - android:fontFamily="sans-serif-light" - android:gravity="center_vertical" - android:singleLine="true" - tools:text="Example Feed" - android:textAlignment="viewStart" - android:textSize="12sp" - android:layout_weight="0.5" - android:paddingTop="4dp" /> - - </LinearLayout> - + android:layout_marginTop="4dp" /> </LinearLayout> <LinearLayout - android:id="@+id/headline_footer" android:orientation="vertical" android:layout_weight="0" - android:layout_width="wrap_content" + android:layout_width="48dp" android:layout_height="match_parent"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" android:singleLine="true" android:ellipsize="none" - android:layout_weight="1" + android:textAlignment="viewEnd" + android:layout_weight="0.5" tools:text="Jan 01" android:textColor="?colorSecondary" - android:textSize="12sp" /> + android:textSize="12sp" + /> <com.google.android.material.button.MaterialButton android:id="@+id/marked" style="?attr/materialIconButtonStyle" android:layout_width="wrap_content" - android:layout_gravity="bottom|end" - android:layout_height="wrap_content" - android:layout_weight="0" + android:paddingEnd="0dp" + android:layout_gravity="end" + android:layout_height="24dp" + android:layout_weight="0.5" app:icon="?ic_star_outline" /> - </LinearLayout> - - <!-- <ImageView - android:id="@+id/article_menu_button" - android:layout_gravity="center" - android:layout_width="wrap_content" - android:layout_height="16dp" - android:layout_weight="0" - android:paddingLeft="4dp" - android:paddingRight="4dp" - android:src="@drawable/ic_action_overflow" /> --> - - </LinearLayout>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml index 222d19d1..6aa1bdcd 100755 --- a/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml +++ b/org.fox.ttrss/src/main/res/layout/headlines_row_compact_unread.xml @@ -4,15 +4,16 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/headlines_row" - android:padding="@dimen/activity_horizontal_margin" + android:paddingTop="16dp" + android:paddingBottom="16dp" + android:paddingStart="16dp" + android:paddingEnd="8dp" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="horizontal" android:background="?colorSurfaceContainerLowest" - tools:ignore="HardcodedText" > + android:orientation="horizontal"> <FrameLayout - android:layout_marginRight="16dp" android:layout_gravity="center_vertical|start" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -20,6 +21,7 @@ <ImageView android:clickable="true" + android:focusable="true" android:layout_width="48dp" android:layout_height="48dp" android:layout_gravity="center" @@ -37,7 +39,6 @@ <LinearLayout android:layout_weight="1" android:orientation="vertical" - android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -45,78 +46,65 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" - android:textAlignment="viewStart" android:maxLines="2" android:ellipsize="end" tools:text="Sample entry title" android:textStyle="bold" - android:textSize="18sp" - android:layout_span="2" /> + android:textSize="18sp" /> + + <TextView + android:id="@+id/feed_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:fontFamily="sans-serif-light" + android:singleLine="true" + tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" + android:textColor="?colorSecondary" + android:textSize="12sp" + android:layout_marginTop="4dp" /> <TextView android:id="@+id/excerpt" android:maxLines="2" - android:textAlignment="viewStart" android:ellipsize="end" android:layout_width="match_parent" android:layout_height="match_parent" tools:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + android:textColor="?colorSecondary" android:textSize="13sp" - android:layout_span="2" - android:paddingTop="4dp" /> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> - - <TextView - android:id="@+id/feed_title" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:ellipsize="middle" - android:fontFamily="sans-serif-light" - android:gravity="center_vertical" - android:singleLine="true" - tools:text="Example Feed AAA AAA AAAAAA AAAA AAAAA AA A A AA AA" - android:textAlignment="viewStart" - android:textSize="12sp" - android:layout_weight="0.5" - android:paddingTop="4dp" /> - - </LinearLayout> + android:layout_marginTop="4dp" /> </LinearLayout> <LinearLayout - android:id="@+id/headline_footer" android:orientation="vertical" android:layout_weight="0" - android:layout_width="wrap_content" + android:layout_width="48dp" android:layout_height="match_parent"> <TextView android:id="@+id/date" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:fontFamily="sans-serif-light" - android:textColor="?colorSecondary" android:singleLine="true" android:ellipsize="none" - android:layout_weight="1" + android:textAlignment="viewEnd" + android:layout_weight="0.5" tools:text="Jan 01" - android:textSize="12sp" /> + android:textColor="?colorSecondary" + android:textSize="12sp" + /> <com.google.android.material.button.MaterialButton android:id="@+id/marked" style="?attr/materialIconButtonStyle" android:layout_width="wrap_content" - android:layout_gravity="bottom|end" - android:layout_height="wrap_content" - android:layout_weight="0" + android:paddingEnd="0dp" + android:layout_gravity="end" + android:layout_height="24dp" + android:layout_weight="0.5" app:icon="?ic_star_outline" /> - </LinearLayout> - </LinearLayout>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/layout/layout_detail_phone.xml b/org.fox.ttrss/src/main/res/layout/layout_detail_phone.xml index 429b27c0..420b6f3a 100644 --- a/org.fox.ttrss/src/main/res/layout/layout_detail_phone.xml +++ b/org.fox.ttrss/src/main/res/layout/layout_detail_phone.xml @@ -2,16 +2,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <com.google.android.material.floatingactionbutton.FloatingActionButton - android:id="@+id/detail_fab" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end|bottom" - android:visibility="gone" - android:src="@drawable/baseline_open_in_browser_24" - android:layout_margin="16dp" /> + android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" @@ -25,7 +16,7 @@ android:id="@+id/article_fragment" app:layout_behavior=".util.DetailActivityScrollingViewBehavior" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="match_parent"> </FrameLayout> <FrameLayout @@ -35,5 +26,22 @@ android:layout_width="0dp"> </FrameLayout> + <com.google.android.material.bottomappbar.BottomAppBar + android:id="@+id/detail_bottom_appbar" + app:backgroundTint="?colorSurfaceContainerHigh" + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_gravity="bottom" + app:menu="@menu/bottombar_detail" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/detail_fab" + android:layout_gravity="end|bottom" + android:layout_marginEnd="16dp" + android:layout_marginBottom="38dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:srcCompat="@drawable/baseline_open_in_browser_24" /> + </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/org.fox.ttrss/src/main/res/menu/action_mode_headlines.xml b/org.fox.ttrss/src/main/res/menu/action_mode_headlines.xml index 35981c01..ccd2c266 100644 --- a/org.fox.ttrss/src/main/res/menu/action_mode_headlines.xml +++ b/org.fox.ttrss/src/main/res/menu/action_mode_headlines.xml @@ -4,7 +4,7 @@ android:id="@+id/selection_toggle_unread" app:showAsAction="always" app:iconTint="?attr/colorControlNormal" - android:icon="@drawable/baseline_mark_as_unread_24" + android:icon="@drawable/baseline_mark_email_unread_24" android:title="@string/selection_toggle_unread"/> <item diff --git a/org.fox.ttrss/src/main/res/menu/activity_main.xml b/org.fox.ttrss/src/main/res/menu/activity_main.xml index 68dec353..1650f366 100755 --- a/org.fox.ttrss/src/main/res/menu/activity_main.xml +++ b/org.fox.ttrss/src/main/res/menu/activity_main.xml @@ -58,40 +58,14 @@ android:id="@+id/toggle_published" android:icon="@drawable/baseline_rss_feed_24" app:iconTint="?attr/colorControlNormal" - android:title="@string/article_toggle_published"/> - <item - android:id="@+id/toggle_unread" - android:icon="@drawable/baseline_email_24" - app:iconTint="?attr/colorControlNormal" app:showAsAction="ifRoom" - android:title="@string/article_toggle_unread"/> - <item - android:id="@+id/toggle_attachments" - android:icon="@drawable/baseline_attach_file_24" - app:showAsAction="" - android:title="@string/attachments_prompt"/> + android:title="@string/article_toggle_published"/> <item android:id="@+id/share_article" android:icon="@drawable/baseline_share_24" app:iconTint="?attr/colorControlNormal" app:showAsAction="ifRoom" android:title="@string/share_article"/> - <item - android:id="@+id/set_labels" - app:showAsAction="" - android:title="@string/article_set_labels"/> - <item - android:id="@+id/article_set_note" - app:showAsAction="" - android:title="@string/article_set_note"/> - <item - android:id="@+id/article_set_score" - app:showAsAction="" - android:title="@string/set_score"/> - - <item - android:id="@+id/catchup_above" - android:title="@string/article_mark_read_above"/> </group> diff --git a/org.fox.ttrss/src/main/res/menu/bottombar_detail.xml b/org.fox.ttrss/src/main/res/menu/bottombar_detail.xml new file mode 100644 index 00000000..744ae79b --- /dev/null +++ b/org.fox.ttrss/src/main/res/menu/bottombar_detail.xml @@ -0,0 +1,38 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> + + <item + android:id="@+id/article_set_score" + app:showAsAction="always" + app:iconTint="?attr/colorControlNormal" + android:icon="@drawable/baseline_trending_flat_24" + android:title="@string/set_score"/> + + <item + android:id="@+id/toggle_unread" + android:icon="@drawable/baseline_email_24" + app:iconTint="?attr/colorControlNormal" + app:showAsAction="always" + android:title="@string/article_toggle_unread"/> + + <item + android:id="@+id/article_set_labels" + app:showAsAction="always" + android:icon="@drawable/baseline_new_label_24" + app:iconTint="?attr/colorControlNormal" + android:title="@string/article_set_labels"/> + + <item + android:id="@+id/article_edit_note" + app:showAsAction="always" + android:icon="@drawable/baseline_note_add_24" + app:iconTint="?attr/colorControlNormal" + android:title="@string/article_edit_note"/> + + <item + android:id="@+id/toggle_attachments" + android:icon="@drawable/baseline_attachment_24" + app:iconTint="?attr/colorControlNormal" + app:showAsAction="always" + android:title="@string/attachments_prompt"/> + +</menu>
\ No newline at end of file diff --git a/org.fox.ttrss/src/main/res/menu/context_headlines.xml b/org.fox.ttrss/src/main/res/menu/context_headlines.xml index dffc9e47..e5a892ab 100644 --- a/org.fox.ttrss/src/main/res/menu/context_headlines.xml +++ b/org.fox.ttrss/src/main/res/menu/context_headlines.xml @@ -18,12 +18,12 @@ app:showAsAction="" android:title="@string/article_mark_read_above"/> <item - android:id="@+id/set_labels" + android:id="@+id/article_set_labels" android:title="@string/article_set_labels"/> <item - android:id="@+id/article_set_note" + android:id="@+id/article_edit_note" app:showAsAction="" - android:title="@string/article_set_note"/> + android:title="@string/article_edit_note"/> <item android:id="@+id/headlines_article_unread" app:showAsAction="" diff --git a/org.fox.ttrss/src/main/res/values/strings.xml b/org.fox.ttrss/src/main/res/values/strings.xml index 0de65e12..6a67013a 100755 --- a/org.fox.ttrss/src/main/res/values/strings.xml +++ b/org.fox.ttrss/src/main/res/values/strings.xml @@ -106,7 +106,7 @@ <string name="dialog_offline_sync_in_progress">Offline sync in progress</string> <string name="dialog_offline_sync_stop">Stop syncing</string> <string name="dialog_offline_sync_continue">Continue</string> - <string name="article_set_note">Publish with note</string> + <string name="article_edit_note">Edit note</string> <string name="dialog_open_preferences">Settings</string> <string name="dialog_need_configure_prompt">Please fill in your tt-rss server information such as URL, login, and password.</string> <!-- <string name="update_headlines">Refresh</string> --> diff --git a/org.fox.ttrss/src/main/res/xml/preferences.xml b/org.fox.ttrss/src/main/res/xml/preferences.xml index 3c422e7e..81fbd5f1 100755 --- a/org.fox.ttrss/src/main/res/xml/preferences.xml +++ b/org.fox.ttrss/src/main/res/xml/preferences.xml @@ -181,26 +181,6 @@ android:summary="@string/prefs_always_open_uri_summary" android:title="@string/prefs_always_open_uri" /> </PreferenceCategory> - <PreferenceCategory android:title="@string/offline_mode" > - <ListPreference - android:defaultValue="250" - android:entries="@array/pref_offline_amounts" - android:entryValues="@array/pref_offline_amounts" - android:key="offline_sync_max" - android:summary="@string/offline_articles_to_download_long" - android:title="@string/offline_articles_to_download" /> - - <SwitchPreferenceCompat - android:defaultValue="false" - android:key="offline_sort_by_feed" - android:title="@string/prefs_offline_sort_by_feed" /> - - <SwitchPreferenceCompat - android:defaultValue="false" - android:key="offline_image_cache_enabled" - android:summary="@string/offline_media_cache_enabled_summary" - android:title="@string/offline_media_cache_enabled" /> - </PreferenceCategory> <PreferenceCategory android:title="@string/prefs_widget" > <ListPreference android:defaultValue="15" |