Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate watch history to Jetpack Compose #11947

Open
wants to merge 23 commits into
base: refactor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
169eff8
Rewrite history fragment using Compose
Isira-Seneviratne Jan 14, 2025
77e254b
Remove unused classes
Isira-Seneviratne Jan 14, 2025
3d9394e
Reuse stream composables
Isira-Seneviratne Jan 14, 2025
f99fc13
Reuse ItemList composable
Isira-Seneviratne Jan 15, 2025
4ec5492
Update dependencies
Isira-Seneviratne Jan 15, 2025
38a533f
Combine ItemList code
Isira-Seneviratne Jan 15, 2025
3f1bd58
Rm unused file
Isira-Seneviratne Jan 15, 2025
48ad123
Implement clear history functionality
Isira-Seneviratne Jan 15, 2025
38645b0
Implement playback action buttons
Isira-Seneviratne Jan 20, 2025
fa98a92
Cache history in view model
Isira-Seneviratne Jan 20, 2025
6621b7f
Fix NPE
Isira-Seneviratne Jan 20, 2025
aeb4548
Add delete button for history items
Isira-Seneviratne Jan 21, 2025
3943a87
Fix preview issue
Isira-Seneviratne Jan 21, 2025
bf06923
Reuse dropdown composable
Isira-Seneviratne Jan 21, 2025
59a748d
Merge branch 'refactor' into History-Compose
Isira-Seneviratne Jan 24, 2025
21caa6c
Update Compose BOM
Isira-Seneviratne Jan 25, 2025
5abc03c
Extract thumbnail into common composable
Isira-Seneviratne Jan 27, 2025
6de5ce7
Merge branch 'refactor' into History-Compose
Isira-Seneviratne Jan 31, 2025
61bb81f
Fix black theme, remove manual background definition
Isira-Seneviratne Jan 31, 2025
212c678
Remove manual text color specification
Isira-Seneviratne Jan 31, 2025
ac34ada
Make DropdownTextMenuItem non-restartable
Isira-Seneviratne Feb 1, 2025
695c05b
Add confirmation dialog for clearing watch history
Isira-Seneviratne Feb 5, 2025
dbe3011
Remove Unknown class
Isira-Seneviratne Feb 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ plugins {
}

android {
compileSdk 34
compileSdk 35
namespace 'org.schabi.newpipe'

defaultConfig {
Expand Down Expand Up @@ -228,6 +228,7 @@ dependencies {
implementation libs.androidx.recyclerview
implementation libs.androidx.room.runtime
implementation libs.androidx.room.rxjava3
implementation libs.androidx.room.paging
kapt libs.androidx.room.compiler
implementation libs.androidx.swiperefreshlayout
implementation libs.androidx.work.runtime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
package org.schabi.newpipe.database.history.dao;

import androidx.annotation.Nullable;
import androidx.room.Dao;
import androidx.room.Query;
import androidx.room.RewriteQueriesToDropUnusedColumns;

import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;

import java.util.List;

import io.reactivex.rxjava3.core.Flowable;

import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE;
Expand All @@ -25,34 +12,33 @@
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;

@Dao
public abstract class StreamHistoryDAO implements HistoryDAO<StreamHistoryEntity> {
@Query("SELECT * FROM " + STREAM_HISTORY_TABLE
+ " WHERE " + STREAM_ACCESS_DATE + " = "
+ "(SELECT MAX(" + STREAM_ACCESS_DATE + ") FROM " + STREAM_HISTORY_TABLE + ")")
@Override
@Nullable
public abstract StreamHistoryEntity getLatestEntry();
import androidx.annotation.Nullable;
import androidx.paging.PagingSource;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.RewriteQueriesToDropUnusedColumns;

@Override
@Query("SELECT * FROM " + STREAM_HISTORY_TABLE)
public abstract Flowable<List<StreamHistoryEntity>> getAll();
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;

@Override
@Query("DELETE FROM " + STREAM_HISTORY_TABLE)
public abstract int deleteAll();
import java.util.List;

@Override
public Flowable<List<StreamHistoryEntity>> listByService(final int serviceId) {
throw new UnsupportedOperationException();
}
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;

@Query("SELECT * FROM " + STREAM_TABLE
+ " INNER JOIN " + STREAM_HISTORY_TABLE
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID
+ " ORDER BY " + STREAM_ACCESS_DATE + " DESC")
public abstract Flowable<List<StreamHistoryEntry>> getHistory();
@Dao
public abstract class StreamHistoryDAO {
@Insert
public abstract long insert(StreamHistoryEntity entity);

@Delete
public abstract void delete(StreamHistoryEntity entity);

@Query("DELETE FROM " + STREAM_HISTORY_TABLE)
public abstract Completable deleteAll();

@Query("SELECT * FROM " + STREAM_TABLE
+ " INNER JOIN " + STREAM_HISTORY_TABLE
Expand All @@ -70,20 +56,45 @@ public Flowable<List<StreamHistoryEntity>> listByService(final int serviceId) {

@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM " + STREAM_TABLE
// Select the latest entry and watch count for each stream id on history table
+ " INNER JOIN "
+ "(SELECT " + JOIN_STREAM_ID + ", "
+ " MAX(" + STREAM_ACCESS_DATE + ") AS " + STREAM_LATEST_DATE + ", "
+ " SUM(" + STREAM_REPEAT_COUNT + ") AS " + STREAM_WATCH_COUNT
+ " FROM " + STREAM_HISTORY_TABLE
+ " GROUP BY " + JOIN_STREAM_ID + ")"

+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID

+ " LEFT JOIN "
+ "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
+ STREAM_PROGRESS_MILLIS
+ " FROM " + STREAM_STATE_TABLE + " )"
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS

+ " ORDER BY " + STREAM_LATEST_DATE + " DESC"
)
public abstract PagingSource<Integer, StreamStatisticsEntry> getHistoryOrderedByLastWatched();

@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM " + STREAM_TABLE
// Select the latest entry and watch count for each stream id on history table
+ " INNER JOIN "
+ "(SELECT " + JOIN_STREAM_ID + ", "
+ " MAX(" + STREAM_ACCESS_DATE + ") AS " + STREAM_LATEST_DATE + ", "
+ " 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

+ " LEFT JOIN "
+ "(SELECT " + JOIN_STREAM_ID + " AS " + JOIN_STREAM_ID_ALIAS + ", "
+ STREAM_PROGRESS_MILLIS
+ " FROM " + STREAM_STATE_TABLE + " )"
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS)
public abstract Flowable<List<StreamStatisticsEntry>> getStatistics();
+ " ON " + STREAM_ID + " = " + JOIN_STREAM_ID_ALIAS

+ " ORDER BY " + STREAM_WATCH_COUNT + " DESC"
)
public abstract PagingSource<Integer, StreamStatisticsEntry> getHistoryOrderedByViewCount();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@
import org.schabi.newpipe.local.holder.LocalPlaylistStreamCardItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamCardItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.local.holder.RemoteBookmarkPlaylistItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistCardItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder;
Expand Down Expand Up @@ -65,10 +62,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
private static final int HEADER_TYPE = 0;
private static final int FOOTER_TYPE = 1;

private static final int STREAM_STATISTICS_HOLDER_TYPE = 0x1000;
private static final int STREAM_PLAYLIST_HOLDER_TYPE = 0x1001;
private static final int STREAM_STATISTICS_GRID_HOLDER_TYPE = 0x1002;
private static final int STREAM_STATISTICS_CARD_HOLDER_TYPE = 0x1003;
private static final int STREAM_PLAYLIST_GRID_HOLDER_TYPE = 0x1004;
private static final int STREAM_PLAYLIST_CARD_HOLDER_TYPE = 0x1005;

Expand Down Expand Up @@ -293,14 +287,6 @@ public int getItemViewType(int position) {
} else {
return STREAM_PLAYLIST_HOLDER_TYPE;
}
case STATISTIC_STREAM_ITEM:
if (itemViewMode == ItemViewMode.CARD) {
return STREAM_STATISTICS_CARD_HOLDER_TYPE;
} else if (itemViewMode == ItemViewMode.GRID) {
return STREAM_STATISTICS_GRID_HOLDER_TYPE;
} else {
return STREAM_STATISTICS_HOLDER_TYPE;
}
default:
Log.e(TAG, "No holder type has been considered for item: ["
+ item.getLocalItemType() + "]");
Expand All @@ -316,43 +302,36 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup paren
Log.d(TAG, "onCreateViewHolder() called with: "
+ "parent = [" + parent + "], type = [" + type + "]");
}
switch (type) {
case HEADER_TYPE:
return new HeaderFooterHolder(header);
case FOOTER_TYPE:
return new HeaderFooterHolder(footer);
case LOCAL_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistGridItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_CARD_HOLDER_TYPE:
return new LocalPlaylistCardItemHolder(localItemBuilder, parent);
case LOCAL_BOOKMARK_PLAYLIST_HOLDER_TYPE:
return new LocalBookmarkPlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_HOLDER_TYPE:
return new RemotePlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_GRID_HOLDER_TYPE:
return new RemotePlaylistGridItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_CARD_HOLDER_TYPE:
return new RemotePlaylistCardItemHolder(localItemBuilder, parent);
case REMOTE_BOOKMARK_PLAYLIST_HOLDER_TYPE:
return new RemoteBookmarkPlaylistItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_HOLDER_TYPE:
return new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_GRID_HOLDER_TYPE:
return new LocalPlaylistStreamGridItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_CARD_HOLDER_TYPE:
return new LocalPlaylistStreamCardItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_HOLDER_TYPE:
return new LocalStatisticStreamItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_GRID_HOLDER_TYPE:
return new LocalStatisticStreamGridItemHolder(localItemBuilder, parent);
case STREAM_STATISTICS_CARD_HOLDER_TYPE:
return new LocalStatisticStreamCardItemHolder(localItemBuilder, parent);
default:
return switch (type) {
case HEADER_TYPE -> new HeaderFooterHolder(header);
case FOOTER_TYPE -> new HeaderFooterHolder(footer);
case LOCAL_PLAYLIST_HOLDER_TYPE ->
new LocalPlaylistItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_GRID_HOLDER_TYPE ->
new LocalPlaylistGridItemHolder(localItemBuilder, parent);
case LOCAL_PLAYLIST_CARD_HOLDER_TYPE ->
new LocalPlaylistCardItemHolder(localItemBuilder, parent);
case LOCAL_BOOKMARK_PLAYLIST_HOLDER_TYPE ->
new LocalBookmarkPlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_HOLDER_TYPE ->
new RemotePlaylistItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_GRID_HOLDER_TYPE ->
new RemotePlaylistGridItemHolder(localItemBuilder, parent);
case REMOTE_PLAYLIST_CARD_HOLDER_TYPE ->
new RemotePlaylistCardItemHolder(localItemBuilder, parent);
case REMOTE_BOOKMARK_PLAYLIST_HOLDER_TYPE ->
new RemoteBookmarkPlaylistItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_HOLDER_TYPE ->
new LocalPlaylistStreamItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_GRID_HOLDER_TYPE ->
new LocalPlaylistStreamGridItemHolder(localItemBuilder, parent);
case STREAM_PLAYLIST_CARD_HOLDER_TYPE ->
new LocalPlaylistStreamCardItemHolder(localItemBuilder, parent);
default -> {
Log.e(TAG, "No view type has been considered for holder: [" + type + "]");
return new FallbackViewHolder(new View(parent.getContext()));
}
yield new FallbackViewHolder(new View(parent.getContext()));
}
};
}

@SuppressWarnings("FinalParameters")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.schabi.newpipe.local.history

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.Surface
import androidx.fragment.app.Fragment
import androidx.fragment.compose.content
import org.schabi.newpipe.R
import org.schabi.newpipe.ui.screens.HistoryScreen
import org.schabi.newpipe.ui.theme.AppTheme

class HistoryFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
) = content {
AppTheme {
Surface {
HistoryScreen()
}
}
}

override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
) {
(activity as AppCompatActivity).supportActionBar?.setTitle(R.string.title_activity_history)
}
}
Loading