Skip to content

Commit a9e3b16

Browse files
Merge branch 'refactor' into Video-description-compose
2 parents 41773de + f41b34c commit a9e3b16

File tree

63 files changed

+831
-1153
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+831
-1153
lines changed

.github/workflows/image-minimizer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ module.exports = async ({github, context}) => {
3333

3434
// Regex for finding images (simple variant) ![ALT_TEXT](https://*.githubusercontent.com/<number>/<variousHexStringsAnd->.<fileExtension>)
3535
const REGEX_USER_CONTENT_IMAGE_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/[-a-z0-9]+\.githubusercontent\.com\/\d+\/[-0-9a-f]{32,512}\.(jpg|gif|png))\)/gm;
36-
const REGEX_ASSETS_IMAGE_LOCKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/[-\w\d]+\/[-\w\d]+\/assets\/\d+\/[\-0-9a-f]{32,512})\)/gm;
36+
const REGEX_ASSETS_IMAGE_LOOKUP = /\!\[([^\]]*)\]\((https:\/\/github\.com\/(?:user-attachments\/assets|[-\w\d]+\/[-\w\d]+\/assets\/\d+)\/[\-0-9a-f]{32,512})\)/gm;
3737

3838
// Check if we found something
3939
let foundSimpleImages = REGEX_USER_CONTENT_IMAGE_LOOKUP.test(initialBody)
40-
|| REGEX_ASSETS_IMAGE_LOCKUP.test(initialBody);
40+
|| REGEX_ASSETS_IMAGE_LOOKUP.test(initialBody);
4141
if (!foundSimpleImages) {
4242
console.log('Found no simple images to process');
4343
return;
@@ -52,7 +52,7 @@ module.exports = async ({github, context}) => {
5252

5353
// Try to find and replace the images with minimized ones
5454
let newBody = await replaceAsync(initialBody, REGEX_USER_CONTENT_IMAGE_LOOKUP, minimizeAsync);
55-
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOCKUP, minimizeAsync);
55+
newBody = await replaceAsync(newBody, REGEX_ASSETS_IMAGE_LOOKUP, minimizeAsync);
5656

5757
if (!wasMatchModified) {
5858
console.log('Nothing was modified. Skipping update');

app/build.gradle

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ plugins {
99
alias libs.plugins.kotlin.compose
1010
alias libs.plugins.kotlin.kapt
1111
alias libs.plugins.kotlin.parcelize
12+
alias libs.plugins.kotlinx.serialization
1213
alias libs.plugins.checkstyle
1314
alias libs.plugins.sonarqube
1415
alias libs.plugins.hilt
1516
alias libs.plugins.aboutlibraries
1617
}
1718

1819
android {
19-
compileSdk 34
20+
compileSdk 35
2021
namespace 'org.schabi.newpipe'
2122

2223
defaultConfig {
@@ -27,9 +28,9 @@ android {
2728
if (System.properties.containsKey('versionCodeOverride')) {
2829
versionCode System.getProperty('versionCodeOverride') as Integer
2930
} else {
30-
versionCode 1003
31+
versionCode 1004
3132
}
32-
versionName "0.27.6"
33+
versionName "0.27.7"
3334
if (System.properties.containsKey('versionNameSuffix')) {
3435
versionNameSuffix System.getProperty('versionNameSuffix')
3536
}
@@ -226,7 +227,6 @@ dependencies {
226227
implementation libs.androidx.fragment.compose
227228
implementation libs.androidx.lifecycle.livedata
228229
implementation libs.androidx.lifecycle.viewmodel
229-
implementation libs.androidx.localbroadcastmanager
230230
implementation libs.androidx.media
231231
implementation libs.androidx.preference
232232
implementation libs.androidx.recyclerview
@@ -319,6 +319,9 @@ dependencies {
319319
// Scroll
320320
implementation libs.lazycolumnscrollbar
321321

322+
// Kotlinx Serialization
323+
implementation libs.kotlinx.serialization.json
324+
322325
/** Debugging **/
323326
// Memory leak detection
324327
debugImplementation libs.leakcanary.object.watcher

app/proguard-rules.pro

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,18 @@
3434

3535
## For some reason NotificationModeConfigFragment wasn't kept (only referenced in a preference xml)
3636
-keep class org.schabi.newpipe.settings.notifications.** { *; }
37+
38+
## Keep Kotlinx Serialization classes
39+
-keepclassmembers class kotlinx.serialization.json.** {
40+
*** Companion;
41+
}
42+
-keepclasseswithmembers class kotlinx.serialization.json.** {
43+
kotlinx.serialization.KSerializer serializer(...);
44+
}
45+
-keep,includedescriptorclasses class org.schabi.newpipe.**$$serializer { *; }
46+
-keepclassmembers class org.schabi.newpipe.** {
47+
*** Companion;
48+
}
49+
-keepclasseswithmembers class org.schabi.newpipe.** {
50+
kotlinx.serialization.KSerializer serializer(...);
51+
}

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
1010
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1111
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
12+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
1213
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
1314

1415
<!-- We need to be able to open links in the browser on API 30+ -->
@@ -90,8 +91,10 @@
9091
android:exported="false"
9192
android:label="@string/title_activity_about" />
9293

93-
<service android:name=".local.subscription.services.SubscriptionsImportService" />
94-
<service android:name=".local.subscription.services.SubscriptionsExportService" />
94+
<service
95+
android:name="androidx.work.impl.foreground.SystemForegroundService"
96+
android:foregroundServiceType="dataSync"
97+
tools:node="merge" />
9598
<service android:name=".local.feed.service.FeedLoadService" />
9699

97100
<activity

app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionDAO.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
9090
internal abstract fun silentInsertAllInternal(entities: List<SubscriptionEntity>): List<Long>
9191

9292
@Transaction
93-
open fun upsertAll(entities: List<SubscriptionEntity>): List<SubscriptionEntity> {
93+
open fun upsertAll(entities: List<SubscriptionEntity>) {
9494
val insertUidList = silentInsertAllInternal(entities)
9595

9696
insertUidList.forEachIndexed { index: Int, uidFromInsert: Long ->
@@ -106,7 +106,5 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
106106
update(entity)
107107
}
108108
}
109-
110-
return entities
111109
}
112110
}

app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public void onPlayerConnected(@NonNull final Player connectedPlayer,
246246
// It will do nothing if the player is not in fullscreen mode
247247
hideSystemUiIfNeeded();
248248

249-
final Optional<MainPlayerUi> playerUi = player.UIs().get(MainPlayerUi.class);
249+
final Optional<MainPlayerUi> playerUi = player.UIs().getOpt(MainPlayerUi.class);
250250
if (!player.videoPlayerSelected() && !playAfterConnect) {
251251
return;
252252
}
@@ -529,7 +529,7 @@ private void setOnClickListeners() {
529529
binding.overlayPlayPauseButton.setOnClickListener(v -> {
530530
if (playerIsNotStopped()) {
531531
player.playPause();
532-
player.UIs().get(VideoPlayerUi.class).ifPresent(ui -> ui.hideControls(0, 0));
532+
player.UIs().getOpt(VideoPlayerUi.class).ifPresent(ui -> ui.hideControls(0, 0));
533533
showSystemUi();
534534
} else {
535535
autoPlayEnabled = true; // forcefully start playing
@@ -688,7 +688,7 @@ protected void initListeners() {
688688
@Override
689689
public boolean onKeyDown(final int keyCode) {
690690
return isPlayerAvailable()
691-
&& player.UIs().get(VideoPlayerUi.class)
691+
&& player.UIs().getOpt(VideoPlayerUi.class)
692692
.map(playerUi -> playerUi.onKeyDown(keyCode)).orElse(false);
693693
}
694694

@@ -1028,7 +1028,7 @@ private void toggleFullscreenIfInFullscreenMode() {
10281028
// If a user watched video inside fullscreen mode and than chose another player
10291029
// return to non-fullscreen mode
10301030
if (isPlayerAvailable()) {
1031-
player.UIs().get(MainPlayerUi.class).ifPresent(playerUi -> {
1031+
player.UIs().getOpt(MainPlayerUi.class).ifPresent(playerUi -> {
10321032
if (playerUi.isFullscreen()) {
10331033
playerUi.toggleFullscreen();
10341034
}
@@ -1244,7 +1244,7 @@ private void tryAddVideoPlayerView() {
12441244
// setup the surface view height, so that it fits the video correctly
12451245
setHeightThumbnail();
12461246

1247-
player.UIs().get(MainPlayerUi.class).ifPresent(playerUi -> {
1247+
player.UIs().getOpt(MainPlayerUi.class).ifPresent(playerUi -> {
12481248
// sometimes binding would be null here, even though getView() != null above u.u
12491249
if (binding != null) {
12501250
// prevent from re-adding a view multiple times
@@ -1260,7 +1260,7 @@ private void removeVideoPlayerView() {
12601260
makeDefaultHeightForVideoPlaceholder();
12611261

12621262
if (player != null) {
1263-
player.UIs().get(VideoPlayerUi.class).ifPresent(VideoPlayerUi::removeViewFromParent);
1263+
player.UIs().getOpt(VideoPlayerUi.class).ifPresent(VideoPlayerUi::removeViewFromParent);
12641264
}
12651265
}
12661266

@@ -1327,7 +1327,7 @@ private void setHeightThumbnail(final int newHeight, final DisplayMetrics metric
13271327
binding.detailThumbnailImageView.setMinimumHeight(newHeight);
13281328
if (isPlayerAvailable()) {
13291329
final int maxHeight = (int) (metrics.heightPixels * MAX_PLAYER_HEIGHT);
1330-
player.UIs().get(VideoPlayerUi.class).ifPresent(ui ->
1330+
player.UIs().getOpt(VideoPlayerUi.class).ifPresent(ui ->
13311331
ui.getBinding().surfaceView.setHeights(newHeight,
13321332
ui.isFullscreen() ? newHeight : maxHeight));
13331333
}
@@ -1861,7 +1861,7 @@ public void onServiceStopped() {
18611861
public void onFullscreenStateChanged(final boolean fullscreen) {
18621862
setupBrightness();
18631863
if (!isPlayerAndPlayerServiceAvailable()
1864-
|| player.UIs().get(MainPlayerUi.class).isEmpty()
1864+
|| player.UIs().getOpt(MainPlayerUi.class).isEmpty()
18651865
|| getRoot().map(View::getParent).isEmpty()) {
18661866
return;
18671867
}
@@ -1890,7 +1890,7 @@ public void onScreenRotationButtonClicked() {
18901890
final boolean isLandscape = DeviceUtils.isLandscape(requireContext());
18911891
if (DeviceUtils.isTablet(activity)
18921892
&& (!globalScreenOrientationLocked(activity) || isLandscape)) {
1893-
player.UIs().get(MainPlayerUi.class).ifPresent(MainPlayerUi::toggleFullscreen);
1893+
player.UIs().getOpt(MainPlayerUi.class).ifPresent(MainPlayerUi::toggleFullscreen);
18941894
return;
18951895
}
18961896

@@ -1990,7 +1990,7 @@ public void hideSystemUiIfNeeded() {
19901990
}
19911991

19921992
private boolean isFullscreen() {
1993-
return isPlayerAvailable() && player.UIs().get(VideoPlayerUi.class)
1993+
return isPlayerAvailable() && player.UIs().getOpt(VideoPlayerUi.class)
19941994
.map(VideoPlayerUi::isFullscreen).orElse(false);
19951995
}
19961996

@@ -2067,7 +2067,7 @@ private void checkLandscape() {
20672067
setAutoPlay(true);
20682068
}
20692069

2070-
player.UIs().get(MainPlayerUi.class).ifPresent(MainPlayerUi::checkLandscape);
2070+
player.UIs().getOpt(MainPlayerUi.class).ifPresent(MainPlayerUi::checkLandscape);
20712071
// Let's give a user time to look at video information page if video is not playing
20722072
if (globalScreenOrientationLocked(activity) && !player.isPlaying()) {
20732073
player.play();
@@ -2332,7 +2332,7 @@ && isPlayerAvailable()
23322332
&& player.isPlaying()
23332333
&& !isFullscreen()
23342334
&& !DeviceUtils.isTablet(activity)) {
2335-
player.UIs().get(MainPlayerUi.class)
2335+
player.UIs().getOpt(MainPlayerUi.class)
23362336
.ifPresent(MainPlayerUi::toggleFullscreen);
23372337
}
23382338
setOverlayLook(binding.appBarLayout, behavior, 1);
@@ -2346,7 +2346,7 @@ && isPlayerAvailable()
23462346
// Re-enable clicks
23472347
setOverlayElementsClickable(true);
23482348
if (isPlayerAvailable()) {
2349-
player.UIs().get(MainPlayerUi.class)
2349+
player.UIs().getOpt(MainPlayerUi.class)
23502350
.ifPresent(MainPlayerUi::closeItemsList);
23512351
}
23522352
setOverlayLook(binding.appBarLayout, behavior, 0);
@@ -2357,7 +2357,7 @@ && isPlayerAvailable()
23572357
showSystemUi();
23582358
}
23592359
if (isPlayerAvailable()) {
2360-
player.UIs().get(MainPlayerUi.class).ifPresent(ui -> {
2360+
player.UIs().getOpt(MainPlayerUi.class).ifPresent(ui -> {
23612361
if (ui.isControlsVisible()) {
23622362
ui.hideControls(0, 0);
23632363
}
@@ -2454,7 +2454,7 @@ boolean isPlayerAndPlayerServiceAvailable() {
24542454

24552455
public Optional<View> getRoot() {
24562456
return Optional.ofNullable(player)
2457-
.flatMap(player1 -> player1.UIs().get(VideoPlayerUi.class))
2457+
.flatMap(player1 -> player1.UIs().getOpt(VideoPlayerUi.class))
24582458
.map(playerUi -> playerUi.getBinding().getRoot());
24592459
}
24602460

app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,6 @@ public void showLoading() {
194194
if (itemsList != null) {
195195
animateHideRecyclerViewAllowingScrolling(itemsList);
196196
}
197-
if (headerRootBinding != null) {
198-
animate(headerRootBinding.getRoot(), false, 200);
199-
}
200197
}
201198

202199
@Override
@@ -205,9 +202,6 @@ public void hideLoading() {
205202
if (itemsList != null) {
206203
animate(itemsList, true, 200);
207204
}
208-
if (headerRootBinding != null) {
209-
animate(headerRootBinding.getRoot(), true, 200);
210-
}
211205
}
212206

213207
@Override
@@ -253,9 +247,6 @@ public void handleError() {
253247
if (itemsList != null) {
254248
animateHideRecyclerViewAllowingScrolling(itemsList);
255249
}
256-
if (headerRootBinding != null) {
257-
animate(headerRootBinding.getRoot(), false, 200);
258-
}
259250
}
260251

261252
@Override

app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,64 @@
33
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
44

55
import android.app.Dialog;
6-
import android.content.Intent;
76
import android.os.Bundle;
87

98
import androidx.annotation.NonNull;
109
import androidx.annotation.Nullable;
1110
import androidx.appcompat.app.AlertDialog;
11+
import androidx.core.os.BundleCompat;
1212
import androidx.fragment.app.DialogFragment;
1313
import androidx.fragment.app.Fragment;
14+
import androidx.work.Constraints;
15+
import androidx.work.ExistingWorkPolicy;
16+
import androidx.work.NetworkType;
17+
import androidx.work.OneTimeWorkRequest;
18+
import androidx.work.OutOfQuotaPolicy;
19+
import androidx.work.WorkManager;
1420

15-
import com.evernote.android.state.State;
1621
import com.livefront.bridge.Bridge;
1722

1823
import org.schabi.newpipe.R;
24+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
25+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker;
1926

2027
public class ImportConfirmationDialog extends DialogFragment {
21-
@State
22-
protected Intent resultServiceIntent;
28+
private static final String INPUT = "input";
2329

24-
public static void show(@NonNull final Fragment fragment,
25-
@NonNull final Intent resultServiceIntent) {
26-
final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog();
27-
confirmationDialog.setResultServiceIntent(resultServiceIntent);
30+
public static void show(@NonNull final Fragment fragment, final SubscriptionImportInput input) {
31+
final var confirmationDialog = new ImportConfirmationDialog();
32+
final var arguments = new Bundle();
33+
arguments.putParcelable(INPUT, input);
34+
confirmationDialog.setArguments(arguments);
2835
confirmationDialog.show(fragment.getParentFragmentManager(), null);
2936
}
3037

31-
public void setResultServiceIntent(final Intent resultServiceIntent) {
32-
this.resultServiceIntent = resultServiceIntent;
33-
}
34-
3538
@NonNull
3639
@Override
3740
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
38-
assureCorrectAppLanguage(getContext());
39-
return new AlertDialog.Builder(requireContext())
41+
final var context = requireContext();
42+
assureCorrectAppLanguage(context);
43+
return new AlertDialog.Builder(context)
4044
.setMessage(R.string.import_network_expensive_warning)
4145
.setCancelable(true)
4246
.setNegativeButton(R.string.cancel, null)
4347
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
44-
if (resultServiceIntent != null && getContext() != null) {
45-
getContext().startService(resultServiceIntent);
46-
}
48+
final var constraints = new Constraints.Builder()
49+
.setRequiredNetworkType(NetworkType.CONNECTED)
50+
.build();
51+
final var input = BundleCompat.getParcelable(requireArguments(), INPUT,
52+
SubscriptionImportInput.class);
53+
54+
final var req = new OneTimeWorkRequest.Builder(SubscriptionImportWorker.class)
55+
.setInputData(input.toData())
56+
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
57+
.setConstraints(constraints)
58+
.build();
59+
60+
WorkManager.getInstance(context)
61+
.enqueueUniqueWork(SubscriptionImportWorker.WORK_NAME,
62+
ExistingWorkPolicy.APPEND_OR_REPLACE, req);
63+
4764
dismiss();
4865
})
4966
.create();
@@ -53,10 +70,6 @@ public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
5370
public void onCreate(@Nullable final Bundle savedInstanceState) {
5471
super.onCreate(savedInstanceState);
5572

56-
if (resultServiceIntent == null) {
57-
throw new IllegalStateException("Result intent is null");
58-
}
59-
6073
Bridge.restoreInstanceState(this, savedInstanceState);
6174
}
6275

0 commit comments

Comments
 (0)