Add search and watch history (#626)

Add search and watch history

* Make MainActicity a single task
* Remove some casting
* SearchFragment: start searching when created with query
* Handle settings change in onResume
* History: Log pop up and background playback
* History: Add swipe to remove functionallity
* Enable history by default
* Use stream item
* Store more information about the stream
* Integrate history database into AppDatabase
* Remove redundant casts
* Re-enable date converters
* History: Use Rx Java and run DB in background
 * Also make HistoryDAO extend BasicDAO
* History: RX-ify swipe to remove
* Sort history entries by creation date
* History: Set toolbar title
* Don't repeat history entries
  * Introduced setters so we can update entries in the database
  * If the latest entry has the same (main) values, just update it
This commit is contained in:
Cyril Müller 2017-08-12 06:50:25 +02:00 committed by Mauricio Colli
parent 09159ec245
commit c0515de6b7
37 changed files with 1470 additions and 33 deletions

View file

@ -2,14 +2,25 @@ package org.schabi.newpipe.database;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;
import android.arch.persistence.room.TypeConverters;
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
import org.schabi.newpipe.database.history.dao.WatchHistoryDAO;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.database.history.model.WatchHistoryEntry;
import org.schabi.newpipe.database.subscription.SubscriptionDAO;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.database.history.Converters;
@Database(entities = {SubscriptionEntity.class}, version = 1, exportSchema = false)
@TypeConverters({Converters.class})
@Database(entities = {SubscriptionEntity.class, WatchHistoryEntry.class, SearchHistoryEntry.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase{
public static final String DATABASE_NAME = "newpipe.db";
public abstract SubscriptionDAO subscriptionDAO();
public abstract WatchHistoryDAO watchHistoryDAO();
public abstract SearchHistoryDAO searchHistoryDAO();
}

View file

@ -9,7 +9,6 @@ import android.arch.persistence.room.Update;
import java.util.Collection;
import java.util.List;
import io.reactivex.Completable;
import io.reactivex.Flowable;
@Dao
@ -39,6 +38,8 @@ public interface BasicDAO<Entity> {
@Delete
int delete(final Collection<Entity> entities);
int deleteAll();
/* Updates */
@Update
int update(final Entity entity);

View file

@ -0,0 +1,28 @@
package org.schabi.newpipe.database.history;
import android.arch.persistence.room.TypeConverter;
import java.util.Date;
public class Converters {
/**
* Convert a long value to a date
* @param value the long value
* @return the date
*/
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
/**
* Convert a date to a long value
* @param date the date
* @return the long value
*/
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}

View file

@ -0,0 +1,7 @@
package org.schabi.newpipe.database.history.dao;
import org.schabi.newpipe.database.BasicDAO;
public interface HistoryDAO<T> extends BasicDAO<T> {
T getLatestEntry();
}

View file

@ -0,0 +1,37 @@
package org.schabi.newpipe.database.history.dao;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import java.util.List;
import io.reactivex.Flowable;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME;
@Dao
public interface SearchHistoryDAO extends HistoryDAO<SearchHistoryEntry> {
String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC";
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")")
@Override
SearchHistoryEntry getLatestEntry();
@Query("DELETE FROM " + TABLE_NAME)
@Override
int deleteAll();
@Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<SearchHistoryEntry>> findAll();
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<SearchHistoryEntry>> listByService(int serviceId);
}

View file

@ -0,0 +1,37 @@
package org.schabi.newpipe.database.history.dao;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import org.schabi.newpipe.database.history.model.WatchHistoryEntry;
import java.util.List;
import io.reactivex.Flowable;
import static org.schabi.newpipe.database.history.model.WatchHistoryEntry.CREATION_DATE;
import static org.schabi.newpipe.database.history.model.WatchHistoryEntry.ID;
import static org.schabi.newpipe.database.history.model.WatchHistoryEntry.SERVICE_ID;
import static org.schabi.newpipe.database.history.model.WatchHistoryEntry.TABLE_NAME;
@Dao
public interface WatchHistoryDAO extends HistoryDAO<WatchHistoryEntry> {
String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC";
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")")
@Override
WatchHistoryEntry getLatestEntry();
@Query("DELETE FROM " + TABLE_NAME)
@Override
int deleteAll();
@Query("SELECT * FROM " + TABLE_NAME + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<WatchHistoryEntry>> findAll();
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + SERVICE_ID + " = :serviceId" + ORDER_BY_CREATION_DATE)
@Override
Flowable<List<WatchHistoryEntry>> listByService(int serviceId);
}

View file

@ -0,0 +1,60 @@
package org.schabi.newpipe.database.history.model;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import java.util.Date;
@Entity
public abstract class HistoryEntry {
public static final String ID = "id";
public static final String SERVICE_ID = "service_id";
public static final String CREATION_DATE = "creation_date";
@ColumnInfo(name = CREATION_DATE)
private Date creationDate;
@ColumnInfo(name = SERVICE_ID)
private int serviceId;
@ColumnInfo(name = ID)
@PrimaryKey(autoGenerate = true)
private long id;
public HistoryEntry(Date creationDate, int serviceId) {
this.serviceId = serviceId;
this.creationDate = creationDate;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public int getServiceId() {
return serviceId;
}
public void setServiceId(int serviceId) {
this.serviceId = serviceId;
}
@Ignore
public boolean hasEqualValues(HistoryEntry otherEntry) {
return otherEntry != null && getServiceId() == otherEntry.getServiceId();
}
}

View file

@ -0,0 +1,37 @@
package org.schabi.newpipe.database.history.model;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import java.util.Date;
@Entity(tableName = SearchHistoryEntry.TABLE_NAME)
public class SearchHistoryEntry extends HistoryEntry {
public static final String TABLE_NAME = "search_history";
public static final String SEARCH = "search";
@ColumnInfo(name = SEARCH)
private String search;
public SearchHistoryEntry(Date creationDate, int serviceId, String search) {
super(creationDate, serviceId);
this.search = search;
}
public String getSearch() {
return search;
}
public void setSearch(String search) {
this.search = search;
}
@Ignore
@Override
public boolean hasEqualValues(HistoryEntry otherEntry) {
return otherEntry instanceof SearchHistoryEntry && super.hasEqualValues(otherEntry)
&& getSearch().equals(((SearchHistoryEntry) otherEntry).getSearch());
}
}

View file

@ -0,0 +1,109 @@
package org.schabi.newpipe.database.history.model;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import java.util.Date;
@Entity(tableName = WatchHistoryEntry.TABLE_NAME)
public class WatchHistoryEntry extends HistoryEntry {
public static final String TABLE_NAME = "watch_history";
public static final String TITLE = "title";
public static final String URL = "url";
public static final String STREAM_ID = "stream_id";
public static final String THUMBNAIL_URL = "thumbnail_url";
public static final String UPLOADER = "uploader";
public static final String DURATION = "duration";
@ColumnInfo(name = TITLE)
private String title;
@ColumnInfo(name = URL)
private String url;
@ColumnInfo(name = STREAM_ID)
private String streamId;
@ColumnInfo(name = THUMBNAIL_URL)
private String thumbnailURL;
@ColumnInfo(name = UPLOADER)
private String uploader;
@ColumnInfo(name = DURATION)
private int duration;
public WatchHistoryEntry(Date creationDate, int serviceId, String title, String url, String streamId, String thumbnailURL, String uploader, int duration) {
super(creationDate, serviceId);
this.title = title;
this.url = url;
this.streamId = streamId;
this.thumbnailURL = thumbnailURL;
this.uploader = uploader;
this.duration = duration;
}
public WatchHistoryEntry(StreamInfo streamInfo) {
this(new Date(), streamInfo.service_id, streamInfo.title, streamInfo.webpage_url,
streamInfo.id, streamInfo.thumbnail_url, streamInfo.uploader, streamInfo.duration);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStreamId() {
return streamId;
}
public void setStreamId(String streamId) {
this.streamId = streamId;
}
public String getThumbnailURL() {
return thumbnailURL;
}
public void setThumbnailURL(String thumbnailURL) {
this.thumbnailURL = thumbnailURL;
}
public String getUploader() {
return uploader;
}
public void setUploader(String uploader) {
this.uploader = uploader;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
@Ignore
@Override
public boolean hasEqualValues(HistoryEntry otherEntry) {
return otherEntry instanceof WatchHistoryEntry && super.hasEqualValues(otherEntry)
&& getUrl().equals(((WatchHistoryEntry) otherEntry).getUrl());
}
}

View file

@ -19,6 +19,10 @@ public interface SubscriptionDAO extends BasicDAO<SubscriptionEntity> {
@Query("SELECT * FROM " + SUBSCRIPTION_TABLE)
Flowable<List<SubscriptionEntity>> findAll();
@Override
@Query("DELETE FROM " + SUBSCRIPTION_TABLE)
int deleteAll();
@Override
@Query("SELECT * FROM " + SUBSCRIPTION_TABLE + " WHERE " + SUBSCRIPTION_SERVICE_ID + " = :serviceId")
Flowable<List<SubscriptionEntity>> listByService(int serviceId);