Implement new feed and subscriptions groups

- Introduce Groupie for easier lists implementations
- Use some of the new components of the Android Architecture libraries
- Add a bunch of icons for groups, using vectors, which still is
compatible with older APIs through the compatibility layer
This commit is contained in:
Mauricio Colli 2019-04-28 17:43:54 -03:00
parent e8ab5aacc7
commit 20a4bb0936
No known key found for this signature in database
GPG key ID: F200BFD6F29DDD85
143 changed files with 4099 additions and 1370 deletions

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.database;
import androidx.room.TypeConverter;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.local.subscription.FeedGroupIcon;
import java.util.Date;
@ -37,4 +38,18 @@ public class Converters {
public static String stringOf(StreamType streamType) {
return streamType.name();
}
@TypeConverter
public static Integer integerOf(FeedGroupIcon feedGroupIcon) {
return feedGroupIcon.getId();
}
@TypeConverter
public static FeedGroupIcon feedGroupIconOf(Integer id) {
for (FeedGroupIcon icon : FeedGroupIcon.values()) {
if (icon.getId() == id) return icon;
}
throw new IllegalArgumentException("There's no feed group icon with the id \"" + id + "\"");
}
}

View file

@ -7,6 +7,7 @@ import androidx.room.Query
import io.reactivex.Flowable
import org.schabi.newpipe.database.feed.model.FeedEntity
import org.schabi.newpipe.database.stream.model.StreamEntity
import java.util.*
@Dao
abstract class FeedDAO {
@ -19,7 +20,9 @@ abstract class FeedDAO {
INNER JOIN feed f
ON s.uid = f.stream_id
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
LIMIT 500
""")
abstract fun getAllStreams(): Flowable<List<StreamEntity>>
@ -36,12 +39,45 @@ abstract class FeedDAO {
ON fg.uid = fgs.group_id
WHERE fgs.group_id = :groupId
ORDER BY s.upload_date IS NULL DESC, s.upload_date DESC, s.uploader ASC
LIMIT 500
""")
abstract fun getAllStreamsFromGroup(groupId: Long): Flowable<List<StreamEntity>>
@Insert(onConflict = OnConflictStrategy.FAIL)
@Query("""
DELETE FROM feed WHERE
feed.stream_id IN (
SELECT s.uid FROM streams s
INNER JOIN feed f
ON s.uid = f.stream_id
WHERE s.upload_date < :date
)
""")
abstract fun unlinkStreamsOlderThan(date: Date)
@Query("""
DELETE FROM feed
WHERE feed.subscription_id = :subscriptionId
AND feed.stream_id IN (
SELECT s.uid FROM streams s
INNER JOIN feed f
ON s.uid = f.stream_id
WHERE s.stream_type = "LIVE_STREAM" OR s.stream_type = "AUDIO_LIVE_STREAM"
)
""")
abstract fun unlinkOldLivestreams(subscriptionId: Long)
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(feedEntity: FeedEntity)
@Insert(onConflict = OnConflictStrategy.FAIL)
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insertAll(entities: List<FeedEntity>): List<Long>
}

View file

@ -2,16 +2,43 @@ package org.schabi.newpipe.database.feed.dao
import androidx.room.*
import io.reactivex.Flowable
import io.reactivex.Maybe
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity
@Dao
abstract class FeedGroupDAO {
@Query("DELETE FROM feed_group")
abstract fun deleteAll(): Int
@Query("SELECT * FROM feed_group")
abstract fun getAll(): Flowable<List<FeedGroupEntity>>
@Query("SELECT * FROM feed_group WHERE uid = :groupId")
abstract fun getGroup(groupId: Long): Maybe<FeedGroupEntity>
@Insert(onConflict = OnConflictStrategy.ABORT)
abstract fun insert(feedEntity: FeedGroupEntity)
abstract fun insert(feedEntity: FeedGroupEntity): Long
@Update(onConflict = OnConflictStrategy.IGNORE)
abstract fun update(feedGroupEntity: FeedGroupEntity): Int
@Query("DELETE FROM feed_group")
abstract fun deleteAll(): Int
@Query("DELETE FROM feed_group WHERE uid = :groupId")
abstract fun delete(groupId: Long): Int
@Query("SELECT subscription_id FROM feed_group_subscription_join WHERE group_id = :groupId")
abstract fun getSubscriptionIdsFor(groupId: Long): Flowable<List<Long>>
@Query("DELETE FROM feed_group_subscription_join WHERE group_id = :groupId")
abstract fun deleteSubscriptionsFromGroup(groupId: Long): Int
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insertSubscriptionsToGroup(entities: List<FeedGroupSubscriptionEntity>): List<Long>
@Transaction
open fun updateSubscriptionsForGroup(groupId: Long, subscriptionIds: List<Long>) {
deleteSubscriptionsFromGroup(groupId)
insertSubscriptionsToGroup(subscriptionIds.map { FeedGroupSubscriptionEntity(groupId, it) })
}
}

View file

@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.FEED_GROUP_TABLE
import org.schabi.newpipe.local.subscription.FeedGroupIcon
@Entity(tableName = FEED_GROUP_TABLE)
data class FeedGroupEntity(
@ -15,7 +16,7 @@ data class FeedGroupEntity(
var name: String,
@ColumnInfo(name = ICON)
var iconId: Int
var icon: FeedGroupIcon
) {
companion object {
const val FEED_GROUP_TABLE = "feed_group"

View file

@ -1,72 +0,0 @@
package org.schabi.newpipe.database.subscription;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import org.schabi.newpipe.database.BasicDAO;
import java.util.List;
import io.reactivex.Flowable;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_UID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_URL;
@Dao
public abstract class SubscriptionDAO implements BasicDAO<SubscriptionEntity> {
@Override
@Query("SELECT * FROM " + SUBSCRIPTION_TABLE)
public abstract Flowable<List<SubscriptionEntity>> getAll();
@Query("SELECT COUNT(*) FROM subscriptions")
public abstract Flowable<Long> rowCount();
@Override
@Query("DELETE FROM " + SUBSCRIPTION_TABLE)
public abstract int deleteAll();
@Override
@Query("SELECT * FROM " + SUBSCRIPTION_TABLE + " WHERE " + SUBSCRIPTION_SERVICE_ID + " = :serviceId")
public abstract Flowable<List<SubscriptionEntity>> listByService(int serviceId);
@Query("SELECT * FROM " + SUBSCRIPTION_TABLE + " WHERE " +
SUBSCRIPTION_URL + " LIKE :url AND " +
SUBSCRIPTION_SERVICE_ID + " = :serviceId")
public abstract Flowable<List<SubscriptionEntity>> getSubscription(int serviceId, String url);
@Query("SELECT " + SUBSCRIPTION_UID + " FROM " + SUBSCRIPTION_TABLE + " WHERE " +
SUBSCRIPTION_URL + " LIKE :url AND " +
SUBSCRIPTION_SERVICE_ID + " = :serviceId")
abstract Long getSubscriptionIdInternal(int serviceId, String url);
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract Long insertInternal(final SubscriptionEntity entities);
@Transaction
public List<SubscriptionEntity> upsertAll(List<SubscriptionEntity> entities) {
for (SubscriptionEntity entity : entities) {
Long uid = insertInternal(entity);
if (uid != -1) {
entity.setUid(uid);
continue;
}
uid = getSubscriptionIdInternal(entity.getServiceId(), entity.getUrl());
entity.setUid(uid);
if (uid == -1) {
throw new IllegalStateException("Invalid subscription id (-1)");
}
update(entity);
}
return entities;
}
}

View file

@ -0,0 +1,60 @@
package org.schabi.newpipe.database.subscription
import androidx.room.*
import io.reactivex.Flowable
import io.reactivex.Maybe
import org.schabi.newpipe.database.BasicDAO
@Dao
abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
@Query("SELECT COUNT(*) FROM subscriptions")
abstract fun rowCount(): Flowable<Long>
@Query("SELECT * FROM subscriptions WHERE service_id = :serviceId")
abstract override fun listByService(serviceId: Int): Flowable<List<SubscriptionEntity>>
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun getSubscription(serviceId: Int, url: String): Maybe<SubscriptionEntity>
@Query("SELECT * FROM subscriptions WHERE uid = :subscriptionId")
abstract fun getSubscription(subscriptionId: Long): SubscriptionEntity
@Query("DELETE FROM subscriptions")
abstract override fun deleteAll(): Int
@Query("DELETE FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
abstract fun deleteSubscription(serviceId: Int, url: String): Int
@Query("SELECT uid FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
internal abstract fun getSubscriptionIdInternal(serviceId: Int, url: String): Long?
@Insert(onConflict = OnConflictStrategy.IGNORE)
internal abstract fun silentInsertAllInternal(entities: List<SubscriptionEntity>): List<Long>
@Transaction
open fun upsertAll(entities: List<SubscriptionEntity>): List<SubscriptionEntity> {
val insertUidList = silentInsertAllInternal(entities)
insertUidList.forEachIndexed { index: Int, uidFromInsert: Long ->
val entity = entities[index]
if (uidFromInsert != -1L) {
entity.uid = uidFromInsert
} else {
val subscriptionIdFromDb = getSubscriptionIdInternal(entity.serviceId, entity.url)
?: throw IllegalStateException("Subscription cannot be null just after insertion.")
entity.uid = subscriptionIdFromDb
update(entity)
}
}
return entities
}
}

View file

@ -19,14 +19,14 @@ import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCR
indices = {@Index(value = {SUBSCRIPTION_SERVICE_ID, SUBSCRIPTION_URL}, unique = true)})
public class SubscriptionEntity {
public final static String SUBSCRIPTION_UID = "uid";
final static String SUBSCRIPTION_TABLE = "subscriptions";
final static String SUBSCRIPTION_SERVICE_ID = "service_id";
final static String SUBSCRIPTION_URL = "url";
final static String SUBSCRIPTION_NAME = "name";
final static String SUBSCRIPTION_AVATAR_URL = "avatar_url";
final static String SUBSCRIPTION_SUBSCRIBER_COUNT = "subscriber_count";
final static String SUBSCRIPTION_DESCRIPTION = "description";
public static final String SUBSCRIPTION_UID = "uid";
public static final String SUBSCRIPTION_TABLE = "subscriptions";
public static final String SUBSCRIPTION_SERVICE_ID = "service_id";
public static final String SUBSCRIPTION_URL = "url";
public static final String SUBSCRIPTION_NAME = "name";
public static final String SUBSCRIPTION_AVATAR_URL = "avatar_url";
public static final String SUBSCRIPTION_SUBSCRIBER_COUNT = "subscriber_count";
public static final String SUBSCRIPTION_DESCRIPTION = "description";
@PrimaryKey(autoGenerate = true)
private long uid = 0;