From e463fce83848a6eaa22d27759d06315a7e8d1b00 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 31 Oct 2017 14:49:37 +0300 Subject: offline headlines: implement gallery support for cached files --- .../main/java/org/fox/ttrss/GalleryActivity.java | 5 + .../java/org/fox/ttrss/GalleryVideoFragment.java | 7 +- .../fox/ttrss/offline/OfflineDownloadService.java | 17 ++ .../ttrss/offline/OfflineHeadlinesFragment.java | 198 ++++++++++++++++++++- 4 files changed, 224 insertions(+), 3 deletions(-) (limited to 'org.fox.ttrss') 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 4d689bda..ae36e8de 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java @@ -205,6 +205,8 @@ public class GalleryActivity extends CommonActivity { Element source = elem.select("source").first(); String src = source.attr("src"); + //Log.d(TAG, "vid/src=" + src); + if (src.startsWith("//")) { src = "https:" + src; } @@ -226,6 +228,9 @@ public class GalleryActivity extends CommonActivity { if (imgSrcFirst.equals(src)) firstFound = true; + Log.d(TAG, "img/fir=" + imgSrcFirst); + Log.d(TAG, "img/src=" + src + "; ff=" + firstFound); + try { Uri checkUri = Uri.parse(src); diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java index 35a4ca09..5bcd0e62 100644 --- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java +++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java @@ -54,7 +54,7 @@ public class GalleryVideoFragment extends GalleryBaseFragment { registerForContextMenu(imgView); - final GlideDrawableImageViewTarget glideImage = new GlideDrawableImageViewTarget(imgView); + /*final GlideDrawableImageViewTarget glideImage = new GlideDrawableImageViewTarget(imgView); Glide.with(this) .load(m_coverUrl) @@ -78,7 +78,10 @@ public class GalleryVideoFragment extends GalleryBaseFragment { return false; } }) - .into(glideImage); + .into(glideImage); */ + + ActivityCompat.startPostponedEnterTransition(m_activity); + initializeVideoPlayer(view); return view; } 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 index 87c8ceca..d9f70c40 100644 --- 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 @@ -476,6 +476,23 @@ public class OfflineDownloadService extends Service { } } } + + 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); + } + } + } + } } 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 index a27fe420..a864fa78 100755 --- 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 @@ -5,6 +5,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources.Theme; import android.database.Cursor; @@ -14,6 +15,8 @@ import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.BaseColumns; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.Fragment; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v4.widget.SwipeRefreshLayout; @@ -39,22 +42,36 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.ProgressBar; +import android.widget.RelativeLayout; 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.shamanland.fab.FloatingActionButton; 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.List; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class OfflineHeadlinesFragment extends Fragment implements OnItemClickListener, AbsListView.OnScrollListener { public enum ArticlesSelection { ALL, NONE, UNREAD } @@ -468,6 +485,8 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis private ColorGenerator m_colorGenerator = ColorGenerator.DEFAULT; private TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round(); + + private boolean showFlavorImage; public ArticleListAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { @@ -477,6 +496,9 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis TypedValue tv = new TypedValue(); theme.resolveAttribute(R.attr.headlineTitleHighScoreUnreadTextColor, tv, true); titleHighScoreUnreadColor = tv.data; + + String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT"); + showFlavorImage = "HL_DEFAULT".equals(headlineMode) || "HL_COMPACT".equals(headlineMode); } public int getViewTypeCount() { @@ -519,7 +541,7 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis } @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, final View convertView, ViewGroup parent) { View v = convertView; @@ -780,6 +802,94 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis return true; } }); + + if (showFlavorImage) { + final ArticleFlavorInfo afi = findFlavorImage(m_cursor); + + /*Log.d(TAG, "flavor image=" + afi.flavorImageUri); + Log.d(TAG, "flavor stream=" + afi.flavorImageUri);*/ + + if (afi.flavorImageUri != null) { + + final String articleContent = article.getString(article.getColumnIndex("content")); + final String articleTitle = article.getString(article.getColumnIndex("title")); + + holder.flavorImageView.setVisibility(View.VISIBLE); + + holder.flavorImageView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(m_activity, GalleryActivity.class); + + intent.putExtra("firstSrc", afi.flavorStreamUri != null ? afi.flavorStreamUri : afi.flavorImageUri); + intent.putExtra("title", articleTitle); + intent.putExtra("content", rewriteUrlsToLocal(articleContent)); + + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(m_activity, + holder.flavorImageView, + "gallery:" + (afi.flavorStreamUri != null ? afi.flavorStreamUri : afi.flavorImageUri)); + + ActivityCompat.startActivity(m_activity, intent, options.toBundle()); + + } + }); + + + Glide.with(OfflineHeadlinesFragment.this) + .load(afi.flavorImageUri) + .dontTransform() + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(false) + .listener(new RequestListener() { + @Override + public boolean onException(Exception e, String model, Target target, boolean isFirstResource) { + + holder.flavorImageLoadingBar.setVisibility(View.GONE); + holder.flavorImageView.setVisibility(View.GONE); + + return false; + } + + @Override + public boolean onResourceReady(GlideDrawable resource, String model, Target 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); + holder.flavorImageOverflow.setVisibility(View.VISIBLE); + + /*boolean forceDown = article.flavorImage != null && "video".equals(article.flavorImage.tagName().toLowerCase()); + + maybeRepositionFlavorImage(holder.flavorImageView, resource, holder, forceDown); + adjustVideoKindView(holder, article);*/ + + /* we don't support image embedding in offline */ + + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) holder.flavorImageView.getLayoutParams(); + lp.addRule(RelativeLayout.BELOW, R.id.headline_header); + //lp.height = RelativeLayout.LayoutParams.WRAP_CONTENT; + holder.flavorImageView.setLayoutParams(lp); + + holder.headlineHeader.setBackgroundDrawable(null); + + return false; + } else { + + holder.flavorImageOverflow.setVisibility(View.GONE); + holder.flavorImageView.setVisibility(View.GONE); + + return true; + } + } + }) + .into(holder.flavorImageView); + + } + } } if (holder.menuButtonView != null) { @@ -813,6 +923,92 @@ public class OfflineHeadlinesFragment extends Fragment implements OnItemClickLis return v; } + private String rewriteUrlsToLocal(String content) { + Document doc = Jsoup.parse(content); + + if (doc != null) { + List 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; + } + + 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; + List mediaList = articleDoc.select("img,video"); + + for (Element e : mediaList) { + flavorImage = e; + break; + } + + if (flavorImage != null) { + + try { + + if ("video".equals(flavorImage.tagName().toLowerCase())) { + Element source = flavorImage.select("source").first(); + afi.flavorStreamUri = source.attr("src"); + + afi.flavorImageUri = flavorImage.attr("poster"); + } else { + afi.flavorImageUri = flavorImage.attr("src"); + + if (afi.flavorImageUri != null && 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 && ImageCacheService.isUrlCached(m_activity, afi.flavorImageUri)) { + afi.flavorImageUri = "file://" + ImageCacheService.getCacheFileName(m_activity, afi.flavorImageUri); + } else { + afi.flavorImageUri = null; + } + + if (afi.flavorStreamUri != null && 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) -- cgit v1.2.3