Merge pull request #869 from nextcloud/add-media-thumbnails

Add media thumbnails
This commit is contained in:
David Luhmer 2020-08-02 10:04:50 +02:00 committed by GitHub
commit f7228e2175
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 150 additions and 58 deletions

View file

@ -121,11 +121,11 @@ repositories {
maven { url "https://dl.bintray.com/lukaville/maven" } //Needed for com.nbsp:library:1.8 in Material File Picker
}
final DAGGER_VERSION = '2.26'
final DAGGER_VERSION = '2.27'
final BUTTERKNIFE_VERSION = '10.2.1'
final ESPRESSO_VERSION = '3.2.0'
final OKHTTP_VERSION = '3.12.10'
final MOCKITO_VERSION = '3.3.1'
final MOCKITO_VERSION = '3.3.3'
final RETROFIT_VERSION = '2.6.4'
dependencies {
@ -134,18 +134,18 @@ dependencies {
// You must install or update the Google Repository through the SDK manager to use this dependency.
// The Google Repository (separate from the corresponding library) can be found in the Extras category.
// implementation 'com.google.android.gms:play-services:4.2.42'
// implementation project(':Android-SingleSignOn')
//implementation project(':Android-SingleSignOn')
// implementation project(path: ':MaterialShowcaseView:library', configuration: 'default')
implementation 'com.github.nextcloud:Android-SingleSignOn:0.5.0'
implementation 'com.github.nextcloud:Android-SingleSignOn:0.5.2'
implementation 'com.github.David-Development:MaterialShowcaseView:bf6afa225d'
implementation "androidx.core:core:1.2.0"
implementation "androidx.core:core:1.3.1"
implementation 'androidx.annotation:annotation:1.1.0'
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.preference:preference:1.1.1"
// https://mvnrepository.com/artifact/com.google.android.material/material
implementation "com.google.android.material:material:1.2.0-alpha05"
implementation "com.google.android.material:material:1.3.0-alpha01"
//implementation "com.google.android.material:material:1.0.0"
implementation "androidx.palette:palette:1.0.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
@ -165,7 +165,7 @@ dependencies {
implementation ('de.greenrobot:greendao-generator:2.0.0') {
exclude group: 'org.freemarker'
}
//implementation 'org.freemarker:freemarker:2.3.23' //Required for DAO generation
//implementation 'org.freemarker:freemarker:2.3.23' // Required for DAO generation
//implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'com.github.gabrielemariotti.changeloglib:changelog:2.1.0'
implementation 'org.jsoup:jsoup:1.13.1'
@ -180,7 +180,7 @@ dependencies {
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
implementation 'io.reactivex.rxjava2:rxjava:2.2.18'
implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
implementation "com.squareup.retrofit2:adapter-rxjava2:$RETROFIT_VERSION"
implementation "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION"
@ -208,7 +208,7 @@ dependencies {
//androidTestImplementation "com.squareup.okhttp3:mockwebserver:${OKHTTP_VERSION}"
androidTestImplementation 'tools.fastlane:screengrab:1.2.0'
androidTestImplementation 'tools.fastlane:screengrab:2.0.0'
//androidTestImplementation "org.mockito:mockito-core:MOCKITO_VERSION"
androidTestImplementation "org.mockito:mockito-android:$MOCKITO_VERSION"

View file

@ -440,7 +440,7 @@ public class LoginDialogActivity extends AppCompatActivity {
public static void ShowAlertDialog(String title, String text, Activity activity)
{
// Linkify the message
final SpannableString s = new SpannableString(text);
final SpannableString s = new SpannableString(text != null ? text : activity.getString(R.string.select_account_unknown_error_toast));
Linkify.addLinks(s, Linkify.ALL);
AlertDialog aDialog = new AlertDialog.Builder(activity)

View file

@ -412,7 +412,7 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
resumeVideoPlayersOnCurrentPage();
progressIndicator.setProgress(position + 1);
if(rssItems.get(position).getFeed() != null) {
if(rssItems.get(position).getFeed() != null) {
// Try getting the feed title and use it for the action bar title
getSupportActionBar().setTitle(rssItems.get(position).getFeed().getFeedTitle());
} else {
@ -428,9 +428,8 @@ public class NewsDetailActivity extends PodcastFragmentActivity {
mPostDelayHandler.delayTimer();
Log.v("PAGE CHANGED", "PAGE: " + position + " - IDFEED: " + rssItems.get(position).getId());
} else {
updateActionBarIcons();
}
updateActionBarIcons();
}

View file

@ -21,7 +21,6 @@
package de.luhmer.owncloudnewsreader;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
@ -30,9 +29,7 @@ import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebBackForwardList;
@ -89,7 +86,7 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
private int section_number;
protected String html;
private GestureDetector mGestureDetector;
// private GestureDetector mGestureDetector;
public NewsDetailFragment() { }
@ -185,7 +182,7 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
this.addBottomPaddingForFastActions(mWebView);
}
setUpGestureDetector();
// setUpGestureDetector();
return rootView;
}
@ -202,6 +199,10 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
mWebView.saveState(outState);
}
/**
* Double tap to star listener (double tap the webview to mark the current item as read)
*/
/*
private void setUpGestureDetector() {
mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener());
@ -233,6 +234,8 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
}
});
}
*/
protected void startLoadRssItemToWebViewTask() {
Log.d(TAG, "startLoadRssItemToWebViewTask() called");
@ -342,17 +345,12 @@ public class NewsDetailFragment extends Fragment implements RssItemToHtmlTask.Li
}
});
/*
mWebView.setOnTouchListener((v, event) -> {
mGestureDetector.onTouchEvent(event);
/*
if (v.getId() == R.id.webview && event.getAction() == MotionEvent.ACTION_DOWN) {
changedUrl = true;
}
*/
return false;
});
*/
}
/**

View file

@ -163,8 +163,6 @@ public class NewsListRecyclerAdapter extends RecyclerView.Adapter {
layout = R.layout.subscription_detail_list_item_thumbnail;
break;
case 1:
layout = R.layout.subscription_detail_list_item_text;
break;
case 3:
layout = R.layout.subscription_detail_list_item_text;
break;

View file

@ -29,6 +29,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@ -280,19 +281,21 @@ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickL
}
if(textViewBody != null) {
String body = rssItem.getBody();
String body = rssItem.getMediaDescription();
if(body == null || body.isEmpty()) {
body = rssItem.getBody();
}
boolean limitLength = true;
// Strip html from String
if(selectedListLayout == 0) {
textViewBody.setMaxLines(scaleTextLines(mPrefs));
body = getBodyText(body, false);
limitLength = false;
} else if(selectedListLayout == 3) {
textViewBody.setMaxLines(200);
body = getBodyText(body, false);
} else {
body = getBodyText(body, true);
limitLength = false;
}
body = getBodyText(body, limitLength);
textViewBody.setText(Html.fromHtml(body));
scaleTextSize(textViewBody, textSizeBody, false, mPrefs);
}
@ -311,7 +314,13 @@ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickL
if(imgViewThumbnail != null) {
imgViewThumbnail.setColorFilter(null);
String body = rssItem.getBody();
List<String> images = ImageHandler.getImageLinksFromText(body);
List<String> images;
if(rssItem.getMediaThumbnail() != null && !rssItem.getMediaThumbnail().isEmpty()) {
images = new ArrayList();
images.add(rssItem.getMediaThumbnail());
} else {
images = ImageHandler.getImageLinksFromText(body);
}
if(images.size() > 0) {
imgViewThumbnail.setVisibility(View.VISIBLE);

View file

@ -125,6 +125,11 @@ public class RssItemToHtmlTask extends AsyncTask<Void, Void, String> {
}
String description = rssItem.getBody();
if(description.isEmpty() && rssItem.getMediaDescription() != null) {
// in case the rss body is empty, fallback to the media description (e.g. youtube / ted talks)
description = rssItem.getMediaDescription();
}
if(!incognitoMode) {
// If incognito mode is disabled, try getting images from cache
description = getDescriptionWithCachedImages(description).trim();

View file

@ -26,7 +26,7 @@ public class DatabaseOrmGenerator {
public static void main(String[] args) throws Exception {
List<SchemaVersion> versions = new ArrayList<>();
versions.add(new Version6(true));
versions.add(new Version7(true));
validateSchemas(versions);

View file

@ -4,14 +4,14 @@ import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Property;
import de.greenrobot.daogenerator.Schema;
public class Version6 extends SchemaVersion {
public class Version7 extends SchemaVersion {
/**
* Constructor
*
* @param current
*/
public Version6(boolean current) {
public Version7(boolean current) {
super(current);
Schema schema = getSchema();
@ -23,7 +23,7 @@ public class Version6 extends SchemaVersion {
*/
@Override
public int getVersionNumber() {
return 6;
return 7;
}
@SuppressWarnings("unused") // id properties (folderId, etc.) need to be in database
@ -69,6 +69,11 @@ public class Version6 extends SchemaVersion {
rssItem.addStringProperty("enclosureLink");
rssItem.addStringProperty("enclosureMime");
rssItem.addStringProperty("mediaThumbnail");
rssItem.addStringProperty("mediaDescription");
rssItem.addBooleanProperty("rtl");
feed.addToOne(folder, folderIdProperty);
folder.addToMany(feed, folderIdProperty);

View file

@ -11,10 +11,10 @@ import de.greenrobot.dao.identityscope.IdentityScopeType;
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* Master of DAO (schema version 6): knows all DAOs.
* Master of DAO (schema version 7): knows all DAOs.
*/
public class DaoMaster extends AbstractDaoMaster {
public static final int SCHEMA_VERSION = 6;
public static final int SCHEMA_VERSION = 7;
/** Creates underlying database table using DAOs. */
public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {

View file

@ -33,6 +33,9 @@ public class RssItem implements HasId<Long> {
private java.util.Date pubDate;
private String enclosureLink;
private String enclosureMime;
private String mediaThumbnail;
private String mediaDescription;
private Boolean rtl;
/** Used to resolve relations */
private transient DaoSession daoSession;
@ -54,7 +57,7 @@ public class RssItem implements HasId<Long> {
this.id = id;
}
public RssItem(long id, long feedId, String link, String title, String body, Boolean read, Boolean starred, String author, String guid, String guidHash, String fingerprint, Boolean read_temp, Boolean starred_temp, java.util.Date lastModified, java.util.Date pubDate, String enclosureLink, String enclosureMime) {
public RssItem(long id, long feedId, String link, String title, String body, Boolean read, Boolean starred, String author, String guid, String guidHash, String fingerprint, Boolean read_temp, Boolean starred_temp, java.util.Date lastModified, java.util.Date pubDate, String enclosureLink, String enclosureMime, String mediaThumbnail, String mediaDescription, Boolean rtl) {
this.id = id;
this.feedId = feedId;
this.link = link;
@ -72,6 +75,9 @@ public class RssItem implements HasId<Long> {
this.pubDate = pubDate;
this.enclosureLink = enclosureLink;
this.enclosureMime = enclosureMime;
this.mediaThumbnail = mediaThumbnail;
this.mediaDescription = mediaDescription;
this.rtl = rtl;
}
/** called by internal mechanisms, do not call yourself. */
@ -224,6 +230,30 @@ public class RssItem implements HasId<Long> {
this.enclosureMime = enclosureMime;
}
public String getMediaThumbnail() {
return mediaThumbnail;
}
public void setMediaThumbnail(String mediaThumbnail) {
this.mediaThumbnail = mediaThumbnail;
}
public String getMediaDescription() {
return mediaDescription;
}
public void setMediaDescription(String mediaDescription) {
this.mediaDescription = mediaDescription;
}
public Boolean getRtl() {
return rtl;
}
public void setRtl(Boolean rtl) {
this.rtl = rtl;
}
/** To-one relationship, resolved on first access. */
public Feed getFeed() {
long __key = this.feedId;

View file

@ -44,6 +44,9 @@ public class RssItemDao extends AbstractDao<RssItem, Long> {
public final static Property PubDate = new Property(14, java.util.Date.class, "pubDate", false, "PUB_DATE");
public final static Property EnclosureLink = new Property(15, String.class, "enclosureLink", false, "ENCLOSURE_LINK");
public final static Property EnclosureMime = new Property(16, String.class, "enclosureMime", false, "ENCLOSURE_MIME");
public final static Property MediaThumbnail = new Property(17, String.class, "mediaThumbnail", false, "MEDIA_THUMBNAIL");
public final static Property MediaDescription = new Property(18, String.class, "mediaDescription", false, "MEDIA_DESCRIPTION");
public final static Property Rtl = new Property(19, Boolean.class, "rtl", false, "RTL");
};
private DaoSession daoSession;
@ -79,7 +82,10 @@ public class RssItemDao extends AbstractDao<RssItem, Long> {
"\"LAST_MODIFIED\" INTEGER," + // 13: lastModified
"\"PUB_DATE\" INTEGER," + // 14: pubDate
"\"ENCLOSURE_LINK\" TEXT," + // 15: enclosureLink
"\"ENCLOSURE_MIME\" TEXT);"); // 16: enclosureMime
"\"ENCLOSURE_MIME\" TEXT," + // 16: enclosureMime
"\"MEDIA_THUMBNAIL\" TEXT," + // 17: mediaThumbnail
"\"MEDIA_DESCRIPTION\" TEXT," + // 18: mediaDescription
"\"RTL\" INTEGER);"); // 19: rtl
// Add Indexes
db.execSQL("CREATE INDEX " + constraint + "IDX_RSS_ITEM_FEED_ID ON RSS_ITEM" +
" (\"FEED_ID\");");
@ -156,6 +162,21 @@ public class RssItemDao extends AbstractDao<RssItem, Long> {
if (enclosureMime != null) {
stmt.bindString(17, enclosureMime);
}
String mediaThumbnail = entity.getMediaThumbnail();
if (mediaThumbnail != null) {
stmt.bindString(18, mediaThumbnail);
}
String mediaDescription = entity.getMediaDescription();
if (mediaDescription != null) {
stmt.bindString(19, mediaDescription);
}
Boolean rtl = entity.getRtl();
if (rtl != null) {
stmt.bindLong(20, rtl ? 1L: 0L);
}
}
@Override
@ -190,7 +211,10 @@ public class RssItemDao extends AbstractDao<RssItem, Long> {
cursor.isNull(offset + 13) ? null : new java.util.Date(cursor.getLong(offset + 13)), // lastModified
cursor.isNull(offset + 14) ? null : new java.util.Date(cursor.getLong(offset + 14)), // pubDate
cursor.isNull(offset + 15) ? null : cursor.getString(offset + 15), // enclosureLink
cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16) // enclosureMime
cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16), // enclosureMime
cursor.isNull(offset + 17) ? null : cursor.getString(offset + 17), // mediaThumbnail
cursor.isNull(offset + 18) ? null : cursor.getString(offset + 18), // mediaDescription
cursor.isNull(offset + 19) ? null : cursor.getShort(offset + 19) != 0 // rtl
);
return entity;
}
@ -215,6 +239,9 @@ public class RssItemDao extends AbstractDao<RssItem, Long> {
entity.setPubDate(cursor.isNull(offset + 14) ? null : new java.util.Date(cursor.getLong(offset + 14)));
entity.setEnclosureLink(cursor.isNull(offset + 15) ? null : cursor.getString(offset + 15));
entity.setEnclosureMime(cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16));
entity.setMediaThumbnail(cursor.isNull(offset + 17) ? null : cursor.getString(offset + 17));
entity.setMediaDescription(cursor.isNull(offset + 18) ? null : cursor.getString(offset + 18));
entity.setRtl(cursor.isNull(offset + 19) ? null : cursor.getShort(offset + 19) != 0);
}
/** @inheritdoc */

View file

@ -92,7 +92,7 @@ public class ThemeUtils {
mSelectHandleRight.setAccessible(true);
mSelectHandleRight.set(editor, drawable);
} catch (Exception e) {
Log.e(TAG, "Couldn't apply color to search view cursor", e);
Log.w(TAG, "Couldn't apply color to search view cursor", e);
}
}

View file

@ -51,6 +51,11 @@ class InsertRssItemIntoDatabase {
String enclosureLink = getStringOrEmpty("enclosureLink", e);
String enclosureMime = getStringOrEmpty("enclosureMime", e);
String mediaThumbnail = getStringOrEmpty("mediaThumbnail", e);
String mediaDescription = getStringOrEmpty("mediaDescription", e);
Boolean rtl = getBooleanOrDefault("rtl", false, e);
if(enclosureLink.trim().equals("") && url.matches("^https?://(www.)?youtube.com/.*")) {
enclosureLink = url;
enclosureMime = "youtube";
@ -69,6 +74,7 @@ class InsertRssItemIntoDatabase {
rssItem.setStarred(e.get("starred").getAsBoolean());
rssItem.setStarred_temp(rssItem.getStarred());
rssItem.setPubDate(pubDate);
rssItem.setRtl(rtl);
//Possible XSS fields
rssItem.setTitle(e.get("title").getAsString());
@ -76,6 +82,8 @@ class InsertRssItemIntoDatabase {
rssItem.setLink(url);
rssItem.setEnclosureLink(enclosureLink);
rssItem.setEnclosureMime(enclosureMime);
rssItem.setMediaDescription(mediaDescription);
rssItem.setMediaThumbnail(mediaThumbnail);
if(rssItem.getFingerprint() == null) {
rssItem.setFingerprint(UUID.randomUUID().toString());
@ -96,4 +104,11 @@ class InsertRssItemIntoDatabase {
}
}
private static Boolean getBooleanOrDefault(String key, Boolean defaultValue, JsonObject jObj) {
if(jObj.has(key) && !jObj.get(key).isJsonNull()) {
return jObj.get(key).getAsBoolean();
} else {
return defaultValue;
}
}
}

View file

@ -1,5 +1,7 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt" >
<animated-vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="lollipop">
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
@ -31,7 +33,7 @@
</vector>
</aapt:attr>
<target android:name="scaleGroup"> *
<target android:name="scaleGroup">
<aapt:attr name="android:animation">
<set>
<objectAnimator

View file

@ -1,5 +1,7 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt" >
<animated-vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="lollipop">
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"

View file

@ -1,6 +1,7 @@
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="lollipop">
<aapt:attr name="android:drawable">
<vector
android:name="vector"

View file

@ -1,6 +1,7 @@
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<animated-vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
tools:targetApi="lollipop">
<aapt:attr name="android:drawable">
<vector
android:name="vector"

View file

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.android.tools.build:gradle:4.0.0'
}
}