-Condensed repeating entries on stream history.

-Changed search history to show service name and stream history to show repeat count.
-Removed history entry abstract and unused info items.
This commit is contained in:
John Zhen Mo 2018-01-28 18:26:19 -08:00
parent 84c5d27416
commit d31eeac49e
22 changed files with 140 additions and 140 deletions

View file

@ -23,7 +23,7 @@ public class Migrations {
database.execSQL("CREATE INDEX `index_search_history_search` ON `search_history` (`search`)"); database.execSQL("CREATE INDEX `index_search_history_search` ON `search_history` (`search`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `streams` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `title` TEXT, `stream_type` TEXT, `duration` INTEGER, `uploader` TEXT, `thumbnail_url` TEXT)"); database.execSQL("CREATE TABLE IF NOT EXISTS `streams` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `service_id` INTEGER NOT NULL, `url` TEXT, `title` TEXT, `stream_type` TEXT, `duration` INTEGER, `uploader` TEXT, `thumbnail_url` TEXT)");
database.execSQL("CREATE UNIQUE INDEX `index_streams_service_id_url` ON `streams` (`service_id`, `url`)"); database.execSQL("CREATE UNIQUE INDEX `index_streams_service_id_url` ON `streams` (`service_id`, `url`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `stream_history` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )"); database.execSQL("CREATE TABLE IF NOT EXISTS `stream_history` (`stream_id` INTEGER NOT NULL, `access_date` INTEGER NOT NULL, `repeat_count` INTEGER NOT NULL, PRIMARY KEY(`stream_id`, `access_date`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )");
database.execSQL("CREATE INDEX `index_stream_history_stream_id` ON `stream_history` (`stream_id`)"); database.execSQL("CREATE INDEX `index_stream_history_stream_id` ON `stream_history` (`stream_id`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `stream_state` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )"); database.execSQL("CREATE TABLE IF NOT EXISTS `stream_state` (`stream_id` INTEGER NOT NULL, `progress_time` INTEGER NOT NULL, PRIMARY KEY(`stream_id`), FOREIGN KEY(`stream_id`) REFERENCES `streams`(`uid`) ON UPDATE CASCADE ON DELETE CASCADE )");
database.execSQL("CREATE TABLE IF NOT EXISTS `playlists` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `thumbnail_url` TEXT)"); database.execSQL("CREATE TABLE IF NOT EXISTS `playlists` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `thumbnail_url` TEXT)");
@ -45,8 +45,8 @@ public class Migrations {
// Once the streams have PKs, join them with the normalized history table // Once the streams have PKs, join them with the normalized history table
// and populate it with the remaining data from watch history // and populate it with the remaining data from watch history
database.execSQL("INSERT INTO stream_history (stream_id, access_date)" + database.execSQL("INSERT INTO stream_history (stream_id, access_date, repeat_count)" +
"SELECT uid, creation_date " + "SELECT uid, creation_date, 1 " +
"FROM watch_history INNER JOIN streams " + "FROM watch_history INNER JOIN streams " +
"ON watch_history.service_id == streams.service_id " + "ON watch_history.service_id == streams.service_id " +
"AND watch_history.url == streams.url " + "AND watch_history.url == streams.url " +

View file

@ -4,6 +4,7 @@ import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import java.util.List; import java.util.List;
@ -21,8 +22,8 @@ public interface SearchHistoryDAO extends HistoryDAO<SearchHistoryEntry> {
String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC"; String ORDER_BY_CREATION_DATE = " ORDER BY " + CREATION_DATE + " DESC";
@Query("SELECT * FROM " + TABLE_NAME + " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")") @Query("SELECT * FROM " + TABLE_NAME +
@Override " WHERE " + ID + " = (SELECT MAX(" + ID + ") FROM " + TABLE_NAME + ")")
@Nullable @Nullable
SearchHistoryEntry getLatestEntry(); SearchHistoryEntry getLatestEntry();

View file

@ -14,6 +14,7 @@ import java.util.List;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_REPEAT_COUNT;
import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_LATEST_DATE; import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_LATEST_DATE;
import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT; import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID;
@ -59,7 +60,7 @@ public abstract class StreamHistoryDAO implements HistoryDAO<StreamHistoryEntity
" INNER JOIN " + " INNER JOIN " +
"(SELECT " + JOIN_STREAM_ID + ", " + "(SELECT " + JOIN_STREAM_ID + ", " +
" MAX(" + STREAM_ACCESS_DATE + ") AS " + STREAM_LATEST_DATE + ", " + " MAX(" + STREAM_ACCESS_DATE + ") AS " + STREAM_LATEST_DATE + ", " +
" COUNT(*) AS " + STREAM_WATCH_COUNT + " SUM(" + STREAM_REPEAT_COUNT + ") AS " + STREAM_WATCH_COUNT +
" FROM " + STREAM_HISTORY_TABLE + " GROUP BY " + JOIN_STREAM_ID + ")" + " FROM " + STREAM_HISTORY_TABLE + " GROUP BY " + JOIN_STREAM_ID + ")" +
" ON " + STREAM_ID + " = " + JOIN_STREAM_ID) " ON " + STREAM_ID + " = " + JOIN_STREAM_ID)

View file

@ -1,60 +0,0 @@
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

@ -4,6 +4,7 @@ import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity; import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore; import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index; import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import java.util.Date; import java.util.Date;
@ -11,19 +12,57 @@ import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARC
@Entity(tableName = SearchHistoryEntry.TABLE_NAME, @Entity(tableName = SearchHistoryEntry.TABLE_NAME,
indices = {@Index(value = SEARCH)}) indices = {@Index(value = SEARCH)})
public class SearchHistoryEntry extends HistoryEntry { public class SearchHistoryEntry {
public static final String ID = "id";
public static final String TABLE_NAME = "search_history"; public static final String TABLE_NAME = "search_history";
public static final String SERVICE_ID = "service_id";
public static final String CREATION_DATE = "creation_date";
public static final String SEARCH = "search"; public static final String SEARCH = "search";
@ColumnInfo(name = ID)
@PrimaryKey(autoGenerate = true)
private long id;
@ColumnInfo(name = CREATION_DATE)
private Date creationDate;
@ColumnInfo(name = SERVICE_ID)
private int serviceId;
@ColumnInfo(name = SEARCH) @ColumnInfo(name = SEARCH)
private String search; private String search;
public SearchHistoryEntry(Date creationDate, int serviceId, String search) { public SearchHistoryEntry(Date creationDate, int serviceId, String search) {
super(creationDate, serviceId); this.serviceId = serviceId;
this.creationDate = creationDate;
this.search = search; this.search = search;
} }
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;
}
public String getSearch() { public String getSearch() {
return search; return search;
} }
@ -33,9 +72,8 @@ public class SearchHistoryEntry extends HistoryEntry {
} }
@Ignore @Ignore
@Override public boolean hasEqualValues(SearchHistoryEntry otherEntry) {
public boolean hasEqualValues(HistoryEntry otherEntry) { return getServiceId() == otherEntry.getServiceId() &&
return otherEntry instanceof SearchHistoryEntry && super.hasEqualValues(otherEntry) getSearch().equals(otherEntry.getSearch());
&& getSearch().equals(((SearchHistoryEntry) otherEntry).getSearch());
} }
} }

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.database.history.model;
import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity; import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey; import android.arch.persistence.room.ForeignKey;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.Index; import android.arch.persistence.room.Index;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -29,6 +30,7 @@ public class StreamHistoryEntity {
final public static String STREAM_HISTORY_TABLE = "stream_history"; final public static String STREAM_HISTORY_TABLE = "stream_history";
final public static String JOIN_STREAM_ID = "stream_id"; final public static String JOIN_STREAM_ID = "stream_id";
final public static String STREAM_ACCESS_DATE = "access_date"; final public static String STREAM_ACCESS_DATE = "access_date";
final public static String STREAM_REPEAT_COUNT = "repeat_count";
@ColumnInfo(name = JOIN_STREAM_ID) @ColumnInfo(name = JOIN_STREAM_ID)
private long streamUid; private long streamUid;
@ -37,9 +39,18 @@ public class StreamHistoryEntity {
@ColumnInfo(name = STREAM_ACCESS_DATE) @ColumnInfo(name = STREAM_ACCESS_DATE)
private Date accessDate; private Date accessDate;
public StreamHistoryEntity(long streamUid, @NonNull Date accessDate) { @ColumnInfo(name = STREAM_REPEAT_COUNT)
private long repeatCount;
public StreamHistoryEntity(long streamUid, @NonNull Date accessDate, long repeatCount) {
this.streamUid = streamUid; this.streamUid = streamUid;
this.accessDate = accessDate; this.accessDate = accessDate;
this.repeatCount = repeatCount;
}
@Ignore
public StreamHistoryEntity(long streamUid, @NonNull Date accessDate) {
this(streamUid, accessDate, 1);
} }
public long getStreamUid() { public long getStreamUid() {
@ -57,4 +68,12 @@ public class StreamHistoryEntity {
public void setAccessDate(@NonNull Date accessDate) { public void setAccessDate(@NonNull Date accessDate) {
this.accessDate = accessDate; this.accessDate = accessDate;
} }
public long getRepeatCount() {
return repeatCount;
}
public void setRepeatCount(long repeatCount) {
this.repeatCount = repeatCount;
}
} }

View file

@ -28,10 +28,13 @@ public class StreamHistoryEntry {
final public long streamId; final public long streamId;
@ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE) @ColumnInfo(name = StreamHistoryEntity.STREAM_ACCESS_DATE)
final public Date accessDate; final public Date accessDate;
@ColumnInfo(name = StreamHistoryEntity.STREAM_REPEAT_COUNT)
final public long repeatCount;
public StreamHistoryEntry(long uid, int serviceId, String url, String title, public StreamHistoryEntry(long uid, int serviceId, String url, String title,
StreamType streamType, long duration, String uploader, StreamType streamType, long duration, String uploader,
String thumbnailUrl, long streamId, Date accessDate) { String thumbnailUrl, long streamId, Date accessDate,
long repeatCount) {
this.uid = uid; this.uid = uid;
this.serviceId = serviceId; this.serviceId = serviceId;
this.url = url; this.url = url;
@ -42,9 +45,15 @@ public class StreamHistoryEntry {
this.thumbnailUrl = thumbnailUrl; this.thumbnailUrl = thumbnailUrl;
this.streamId = streamId; this.streamId = streamId;
this.accessDate = accessDate; this.accessDate = accessDate;
this.repeatCount = repeatCount;
} }
public StreamHistoryEntity toStreamHistoryEntity() { public StreamHistoryEntity toStreamHistoryEntity() {
return new StreamHistoryEntity(streamId, accessDate); return new StreamHistoryEntity(streamId, accessDate, repeatCount);
}
public boolean hasEqualValues(StreamHistoryEntry other) {
return this.uid == other.uid && streamId == other.streamId &&
accessDate.compareTo(other.accessDate) == 0;
} }
} }

View file

@ -3,7 +3,6 @@ package org.schabi.newpipe.database.playlist;
import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.ColumnInfo;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
@ -28,13 +27,6 @@ public class PlaylistMetadataEntry implements LocalItem {
this.streamCount = streamCount; this.streamCount = streamCount;
} }
public LocalPlaylistInfoItem toStoredPlaylistInfoItem() {
LocalPlaylistInfoItem storedPlaylistInfoItem = new LocalPlaylistInfoItem(uid, name);
storedPlaylistInfoItem.setThumbnailUrl(thumbnailUrl);
storedPlaylistInfoItem.setStreamCount(streamCount);
return storedPlaylistInfoItem;
}
@Override @Override
public LocalItemType getLocalItemType() { public LocalItemType getLocalItemType() {
return LocalItemType.PLAYLIST_ITEM; return LocalItemType.PLAYLIST_ITEM;

View file

@ -32,7 +32,7 @@ public class LocalItemBuilder {
private final Context context; private final Context context;
private ImageLoader imageLoader = ImageLoader.getInstance(); private ImageLoader imageLoader = ImageLoader.getInstance();
private OnCustomItemGesture<LocalItem> onSelectedListener; private OnLocalItemGesture<LocalItem> onSelectedListener;
public LocalItemBuilder(Context context) { public LocalItemBuilder(Context context) {
this.context = context; this.context = context;
@ -46,11 +46,11 @@ public class LocalItemBuilder {
return imageLoader; return imageLoader;
} }
public OnCustomItemGesture<LocalItem> getOnItemSelectedListener() { public OnLocalItemGesture<LocalItem> getOnItemSelectedListener() {
return onSelectedListener; return onSelectedListener;
} }
public void setOnItemSelectedListener(OnCustomItemGesture<LocalItem> listener) { public void setOnItemSelectedListener(OnLocalItemGesture<LocalItem> listener) {
this.onSelectedListener = listener; this.onSelectedListener = listener;
} }
} }

View file

@ -66,7 +66,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
Localization.getPreferredLocale(activity)); Localization.getPreferredLocale(activity));
} }
public void setSelectedListener(OnCustomItemGesture<LocalItem> listener) { public void setSelectedListener(OnLocalItemGesture<LocalItem> listener) {
localItemBuilder.setOnItemSelectedListener(listener); localItemBuilder.setOnItemSelectedListener(listener);
} }

View file

@ -151,7 +151,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
itemTouchHelper.attachToRecyclerView(itemsList); itemTouchHelper.attachToRecyclerView(itemsList);
itemListAdapter.setSelectedListener(new OnCustomItemGesture<LocalItem>() { itemListAdapter.setSelectedListener(new OnLocalItemGesture<LocalItem>() {
@Override @Override
public void selected(LocalItem selectedItem) { public void selected(LocalItem selectedItem) {
if (selectedItem instanceof PlaylistStreamEntry) { if (selectedItem instanceof PlaylistStreamEntry) {

View file

@ -5,7 +5,7 @@ import android.support.v7.widget.RecyclerView;
import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
public abstract class OnCustomItemGesture<T extends LocalItem> { public abstract class OnLocalItemGesture<T extends LocalItem> {
public abstract void selected(T selectedItem); public abstract void selected(T selectedItem);

View file

@ -13,15 +13,11 @@ import android.widget.Toast;
import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.InfoListAdapter;
import org.schabi.newpipe.info_list.OnInfoItemGesture;
import org.schabi.newpipe.info_list.stored.LocalPlaylistInfoItem;
import org.schabi.newpipe.playlist.PlayQueueItem; import org.schabi.newpipe.playlist.PlayQueueItem;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,7 +30,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName(); private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
private RecyclerView playlistRecyclerView; private RecyclerView playlistRecyclerView;
private InfoListAdapter playlistAdapter; private LocalItemListAdapter playlistAdapter;
public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) { public static PlaylistAppendDialog fromStreamInfo(final StreamInfo info) {
PlaylistAppendDialog dialog = new PlaylistAppendDialog(); PlaylistAppendDialog dialog = new PlaylistAppendDialog();
@ -69,8 +65,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
playlistAdapter = new InfoListAdapter(getActivity()); playlistAdapter = new LocalItemListAdapter(getActivity());
playlistAdapter.useMiniItemVariants(true);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -97,13 +92,13 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog()); newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
playlistAdapter.setOnPlaylistSelectedListener(new OnInfoItemGesture<PlaylistInfoItem>() { playlistAdapter.setSelectedListener(new OnLocalItemGesture<LocalItem>() {
@Override @Override
public void selected(PlaylistInfoItem selectedItem) { public void selected(LocalItem selectedItem) {
if (!(selectedItem instanceof LocalPlaylistInfoItem) || getStreams() == null) if (!(selectedItem instanceof PlaylistMetadataEntry) || getStreams() == null)
return; return;
final long playlistId = ((LocalPlaylistInfoItem) selectedItem).getPlaylistId(); final long playlistId = ((PlaylistMetadataEntry) selectedItem).uid;
final Toast successToast = final Toast successToast =
Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT); Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT);
@ -123,13 +118,8 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
return; return;
} }
List<InfoItem> playlistInfoItems = new ArrayList<>(metadataEntries.size());
for (final PlaylistMetadataEntry metadataEntry : metadataEntries) {
playlistInfoItems.add(metadataEntry.toStoredPlaylistInfoItem());
}
playlistAdapter.clearStreamItemList(); playlistAdapter.clearStreamItemList();
playlistAdapter.addInfoItemList(playlistInfoItems); playlistAdapter.addInfoItemList(metadataEntries);
playlistRecyclerView.setVisibility(View.VISIBLE); playlistRecyclerView.setVisibility(View.VISIBLE);
}); });
} }

View file

@ -22,7 +22,7 @@ import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.local.LocalItemListAdapter; import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
import org.schabi.newpipe.fragments.local.LocalPlaylistManager; import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
import org.schabi.newpipe.fragments.local.OnCustomItemGesture; import org.schabi.newpipe.fragments.local.OnLocalItemGesture;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
@ -136,7 +136,7 @@ public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEnt
protected void initListeners() { protected void initListeners() {
super.initListeners(); super.initListeners();
itemListAdapter.setSelectedListener(new OnCustomItemGesture<LocalItem>() { itemListAdapter.setSelectedListener(new OnLocalItemGesture<LocalItem>() {
@Override @Override
public void selected(LocalItem selectedItem) { public void selected(LocalItem selectedItem) {
// Requires the parent fragment to find holder for fragment replacement // Requires the parent fragment to find holder for fragment replacement

View file

@ -18,7 +18,7 @@ import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.local.BaseLocalListFragment; import org.schabi.newpipe.fragments.local.BaseLocalListFragment;
import org.schabi.newpipe.fragments.local.OnCustomItemGesture; import org.schabi.newpipe.fragments.local.OnLocalItemGesture;
import org.schabi.newpipe.history.HistoryRecordManager; import org.schabi.newpipe.history.HistoryRecordManager;
import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoItemDialog;
import org.schabi.newpipe.playlist.PlayQueue; import org.schabi.newpipe.playlist.PlayQueue;
@ -122,7 +122,7 @@ public abstract class StatisticsPlaylistFragment
protected void initListeners() { protected void initListeners() {
super.initListeners(); super.initListeners();
itemListAdapter.setSelectedListener(new OnCustomItemGesture<LocalItem>() { itemListAdapter.setSelectedListener(new OnLocalItemGesture<LocalItem>() {
@Override @Override
public void selected(LocalItem selectedItem) { public void selected(LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) { if (selectedItem instanceof StreamStatisticsEntry) {

View file

@ -1,6 +1,7 @@
package org.schabi.newpipe.history; package org.schabi.newpipe.history;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@ -22,11 +23,13 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
private final ArrayList<E> mEntries; private final ArrayList<E> mEntries;
private final DateFormat mDateFormat; private final DateFormat mDateFormat;
private final Context mContext;
private OnHistoryItemClickListener<E> onHistoryItemClickListener = null; private OnHistoryItemClickListener<E> onHistoryItemClickListener = null;
public HistoryEntryAdapter(Context context) { public HistoryEntryAdapter(Context context) {
super(); super();
mContext = context;
mEntries = new ArrayList<>(); mEntries = new ArrayList<>();
mDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, mDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM,
Localization.getPreferredLocale(context)); Localization.getPreferredLocale(context));
@ -51,6 +54,10 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
return mDateFormat.format(date); return mDateFormat.format(date);
} }
protected String getFormattedViewString(final long viewCount) {
return Localization.shortViewCount(mContext, viewCount);
}
@Override @Override
public int getItemCount() { public int getItemCount() {
return mEntries.size(); return mEntries.size();

View file

@ -62,7 +62,16 @@ public class HistoryRecordManager {
final Date currentTime = new Date(); final Date currentTime = new Date();
return Maybe.fromCallable(() -> database.runInTransaction(() -> { return Maybe.fromCallable(() -> database.runInTransaction(() -> {
final long streamId = streamTable.upsert(new StreamEntity(info)); final long streamId = streamTable.upsert(new StreamEntity(info));
return streamHistoryTable.insert(new StreamHistoryEntity(streamId, currentTime)); StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry();
if (latestEntry != null && latestEntry.getStreamUid() == streamId) {
streamHistoryTable.delete(latestEntry);
latestEntry.setAccessDate(currentTime);
latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1);
return streamHistoryTable.insert(latestEntry);
} else {
return streamHistoryTable.insert(new StreamHistoryEntity(streamId, currentTime));
}
})).subscribeOn(Schedulers.io()); })).subscribeOn(Schedulers.io());
} }

View file

@ -14,6 +14,8 @@ import android.widget.TextView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import java.util.Collection; import java.util.Collection;
@ -99,12 +101,12 @@ public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
private static class ViewHolder extends RecyclerView.ViewHolder { private static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView search; private final TextView search;
private final TextView time; private final TextView info;
public ViewHolder(View itemView) { public ViewHolder(View itemView) {
super(itemView); super(itemView);
search = itemView.findViewById(R.id.search); search = itemView.findViewById(R.id.search);
time = itemView.findViewById(R.id.time); info = itemView.findViewById(R.id.info);
} }
} }
@ -125,7 +127,11 @@ public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
@Override @Override
void onBindViewHolder(ViewHolder holder, SearchHistoryEntry entry, int position) { void onBindViewHolder(ViewHolder holder, SearchHistoryEntry entry, int position) {
holder.search.setText(entry.getSearch()); holder.search.setText(entry.getSearch());
holder.time.setText(getFormattedDate(entry.getCreationDate()));
final String info = Localization.concatenateStrings(
getFormattedDate(entry.getCreationDate()),
NewPipe.getNameOfService(entry.getServiceId()));
holder.info.setText(info);
} }
} }
} }

View file

@ -123,7 +123,16 @@ public class WatchedHistoryFragment extends HistoryFragment<StreamHistoryEntry>
@Override @Override
void onBindViewHolder(ViewHolder holder, StreamHistoryEntry entry, int position) { void onBindViewHolder(ViewHolder holder, StreamHistoryEntry entry, int position) {
holder.date.setText(getFormattedDate(entry.accessDate)); final String formattedDate = getFormattedDate(entry.accessDate);
final String info;
if (entry.repeatCount > 1) {
info = Localization.concatenateStrings(formattedDate,
getFormattedViewString(entry.repeatCount));
} else {
info = formattedDate;
}
holder.info.setText(info);
holder.streamTitle.setText(entry.title); holder.streamTitle.setText(entry.title);
holder.uploader.setText(entry.uploader); holder.uploader.setText(entry.uploader);
holder.duration.setText(Localization.getDurationString(entry.duration)); holder.duration.setText(Localization.getDurationString(entry.duration));
@ -133,7 +142,7 @@ public class WatchedHistoryFragment extends HistoryFragment<StreamHistoryEntry>
} }
private static class ViewHolder extends RecyclerView.ViewHolder { private static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView date; private final TextView info;
private final TextView streamTitle; private final TextView streamTitle;
private final ImageView thumbnailView; private final ImageView thumbnailView;
private final TextView uploader; private final TextView uploader;
@ -142,7 +151,7 @@ public class WatchedHistoryFragment extends HistoryFragment<StreamHistoryEntry>
public ViewHolder(View itemView) { public ViewHolder(View itemView) {
super(itemView); super(itemView);
thumbnailView = itemView.findViewById(R.id.itemThumbnailView); thumbnailView = itemView.findViewById(R.id.itemThumbnailView);
date = itemView.findViewById(R.id.itemAdditionalDetails); info = itemView.findViewById(R.id.itemAdditionalDetails);
streamTitle = itemView.findViewById(R.id.itemVideoTitleView); streamTitle = itemView.findViewById(R.id.itemVideoTitleView);
uploader = itemView.findViewById(R.id.itemUploaderView); uploader = itemView.findViewById(R.id.itemUploaderView);
duration = itemView.findViewById(R.id.itemDurationView); duration = itemView.findViewById(R.id.itemDurationView);

View file

@ -1,20 +0,0 @@
package org.schabi.newpipe.info_list.stored;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import static org.schabi.newpipe.util.Constants.NO_SERVICE_ID;
import static org.schabi.newpipe.util.Constants.NO_URL;
public final class LocalPlaylistInfoItem extends PlaylistInfoItem {
private final long playlistId;
public LocalPlaylistInfoItem(final long playlistId, final String name) {
super(NO_SERVICE_ID, NO_URL, name);
this.playlistId = playlistId;
}
public long getPlaylistId() {
return playlistId;
}
}

View file

@ -12,5 +12,4 @@ public class Constants {
public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change"; public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change";
public static final int NO_SERVICE_ID = -1; public static final int NO_SERVICE_ID = -1;
public static final String NO_URL = "";
} }

View file

@ -13,7 +13,7 @@
android:paddingTop="8dp"> android:paddingTop="8dp">
<TextView <TextView
android:id="@+id/time" android:id="@+id/info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:text="10/11/2017 11:32"/> tools:text="10/11/2017 11:32"/>