Skip to content

Commit b4b422b

Browse files
Isira-Seneviratnetheimpulson
authored andcommitted
Merge pull request TeamNewPipe#11759 from Isira-Seneviratne/Import-export-worker
Rewrite import and export subscriptions functionality using coroutines
1 parent 460cadf commit b4b422b

21 files changed

Lines changed: 563 additions & 1030 deletions

app/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
alias(libs.plugins.android.application)
88
alias(libs.plugins.jetbrains.kotlin.android)
99
alias(libs.plugins.jetbrains.kotlin.kapt)
10+
alias(libs.plugins.jetbrains.kotlinx.serialization)
1011
alias(libs.plugins.google.ksp)
1112
alias(libs.plugins.jetbrains.kotlin.parcelize)
1213
alias(libs.plugins.sonarqube)
@@ -244,6 +245,12 @@ dependencies {
244245
implementation(libs.google.android.material)
245246
implementation(libs.androidx.webkit)
246247

248+
// Coroutines interop
249+
implementation(libs.kotlinx.coroutines.rx3)
250+
251+
// Kotlinx Serialization
252+
implementation(libs.kotlinx.serialization.json)
253+
247254
/** Third-party libraries **/
248255
implementation(libs.livefront.bridge)
249256
implementation(libs.evernote.statesaver.core)

app/proguard-rules.pro

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

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

app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1111
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
1212
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
13-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
1413
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
1514

1615
<!-- We need to be able to open links in the browser on API 30+ -->
@@ -96,14 +95,6 @@
9695
android:exported="false"
9796
android:label="@string/title_activity_about" />
9897

99-
<service
100-
android:name=".local.subscription.services.SubscriptionsImportService"
101-
android:foregroundServiceType="dataSync" />
102-
103-
<service
104-
android:name=".local.subscription.services.SubscriptionsExportService"
105-
android:foregroundServiceType="dataSync" />
106-
10798
<service
10899
android:name=".local.feed.service.FeedLoadService"
109100
android:foregroundServiceType="dataSync" />

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
}
Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,63 @@
11
package org.schabi.newpipe.local.subscription;
22

33
import android.app.Dialog;
4-
import android.content.Intent;
54
import android.os.Bundle;
65

76
import androidx.annotation.NonNull;
87
import androidx.annotation.Nullable;
98
import androidx.appcompat.app.AlertDialog;
9+
import androidx.core.os.BundleCompat;
1010
import androidx.fragment.app.DialogFragment;
1111
import androidx.fragment.app.Fragment;
12-
13-
import com.livefront.bridge.Bridge;
12+
import androidx.work.Constraints;
13+
import androidx.work.ExistingWorkPolicy;
14+
import androidx.work.NetworkType;
15+
import androidx.work.OneTimeWorkRequest;
16+
import androidx.work.OutOfQuotaPolicy;
17+
import androidx.work.WorkManager;
1418

1519
import org.schabi.newpipe.R;
20+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
21+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker;
1622

1723
public class ImportConfirmationDialog extends DialogFragment {
18-
protected Intent resultServiceIntent;
19-
private static final String EXTRA_RESULT_SERVICE_INTENT = "extra_result_service_intent";
20-
21-
public static void show(@NonNull final Fragment fragment,
22-
@NonNull final Intent resultServiceIntent) {
23-
final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog();
24-
final Bundle args = new Bundle();
25-
args.putParcelable(EXTRA_RESULT_SERVICE_INTENT, resultServiceIntent);
26-
confirmationDialog.setArguments(args);
24+
private static final String INPUT = "input";
25+
26+
public static void show(@NonNull final Fragment fragment, final SubscriptionImportInput input) {
27+
final var confirmationDialog = new ImportConfirmationDialog();
28+
final var arguments = new Bundle();
29+
arguments.putParcelable(INPUT, input);
30+
confirmationDialog.setArguments(arguments);
2731
confirmationDialog.show(fragment.getParentFragmentManager(), null);
2832
}
2933

3034
@NonNull
3135
@Override
3236
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
33-
return new AlertDialog.Builder(requireContext())
37+
final var context = requireContext();
38+
return new AlertDialog.Builder(context)
3439
.setMessage(R.string.import_network_expensive_warning)
3540
.setCancelable(true)
3641
.setNegativeButton(R.string.cancel, null)
3742
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
38-
requireContext().startService(resultServiceIntent);
43+
final var constraints = new Constraints.Builder()
44+
.setRequiredNetworkType(NetworkType.CONNECTED)
45+
.build();
46+
final var input = BundleCompat.getParcelable(requireArguments(), INPUT,
47+
SubscriptionImportInput.class);
48+
49+
final var req = new OneTimeWorkRequest.Builder(SubscriptionImportWorker.class)
50+
.setInputData(input.toData())
51+
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
52+
.setConstraints(constraints)
53+
.build();
54+
55+
WorkManager.getInstance(context)
56+
.enqueueUniqueWork(SubscriptionImportWorker.WORK_NAME,
57+
ExistingWorkPolicy.APPEND_OR_REPLACE, req);
58+
3959
dismiss();
4060
})
4161
.create();
4262
}
43-
44-
@Override
45-
public void onCreate(@Nullable final Bundle savedInstanceState) {
46-
super.onCreate(savedInstanceState);
47-
48-
resultServiceIntent = requireArguments().getParcelable(EXTRA_RESULT_SERVICE_INTENT);
49-
}
50-
51-
@Override
52-
public void onSaveInstanceState(@NonNull final Bundle outState) {
53-
super.onSaveInstanceState(outState);
54-
Bridge.saveInstanceState(this, outState);
55-
}
5663
}

app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.schabi.newpipe.local.subscription
33
import android.app.Activity
44
import android.content.Context
55
import android.content.DialogInterface
6-
import android.content.Intent
76
import android.os.Bundle
87
import android.os.Parcelable
98
import android.view.LayoutInflater
@@ -53,11 +52,8 @@ import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
5352
import org.schabi.newpipe.local.subscription.item.GroupsHeader
5453
import org.schabi.newpipe.local.subscription.item.Header
5554
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
56-
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService
57-
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService
58-
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE
59-
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE
60-
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE
55+
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
56+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
6157
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
6258
import org.schabi.newpipe.streams.io.StoredFileHelper
6359
import org.schabi.newpipe.util.NavigationHelper
@@ -223,21 +219,18 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
223219
}
224220

225221
private fun requestExportResult(result: ActivityResult) {
226-
if (result.data != null && result.resultCode == Activity.RESULT_OK) {
227-
activity.startService(
228-
Intent(activity, SubscriptionsExportService::class.java)
229-
.putExtra(SubscriptionsExportService.KEY_FILE_PATH, result.data?.data)
230-
)
222+
val data = result.data?.data
223+
if (data != null && result.resultCode == Activity.RESULT_OK) {
224+
SubscriptionExportWorker.schedule(activity, data)
231225
}
232226
}
233227

234228
private fun requestImportResult(result: ActivityResult) {
235-
if (result.data != null && result.resultCode == Activity.RESULT_OK) {
229+
val data = result.data?.dataString
230+
if (data != null && result.resultCode == Activity.RESULT_OK) {
236231
ImportConfirmationDialog.show(
237232
this,
238-
Intent(activity, SubscriptionsImportService::class.java)
239-
.putExtra(KEY_MODE, PREVIOUS_EXPORT_MODE)
240-
.putExtra(KEY_VALUE, result.data?.data)
233+
SubscriptionImportInput.PreviousExportMode(data)
241234
)
242235
}
243236
}

app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.schabi.newpipe.local.subscription
22

33
import android.content.Context
4-
import android.util.Pair
54
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
65
import io.reactivex.rxjava3.core.Completable
76
import io.reactivex.rxjava3.core.Flowable
@@ -51,23 +50,16 @@ class SubscriptionManager(context: Context) {
5150
}
5251
}
5352

54-
fun upsertAll(infoList: List<Pair<ChannelInfo, List<ChannelTabInfo>>>): List<SubscriptionEntity> {
55-
val listEntities = subscriptionTable.upsertAll(
56-
infoList.map { SubscriptionEntity.from(it.first) }
57-
)
53+
fun upsertAll(infoList: List<Pair<ChannelInfo, ChannelTabInfo>>) {
54+
val listEntities = infoList.map { SubscriptionEntity.from(it.first) }
55+
subscriptionTable.upsertAll(listEntities)
5856

5957
database.runInTransaction {
6058
infoList.forEachIndexed { index, info ->
61-
info.second.forEach {
62-
feedDatabaseManager.upsertAll(
63-
listEntities[index].uid,
64-
it.relatedItems.filterIsInstance<StreamInfoItem>()
65-
)
66-
}
59+
val streams = info.second.relatedItems.filterIsInstance<StreamInfoItem>()
60+
feedDatabaseManager.upsertAll(listEntities[index].uid, streams)
6761
}
6862
}
69-
70-
return listEntities
7163
}
7264

7365
fun updateChannelInfo(info: ChannelInfo): Completable = subscriptionTable.getSubscription(info.serviceId, info.url)

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

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package org.schabi.newpipe.local.subscription;
22

33
import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL;
4-
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE;
5-
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE;
6-
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE;
7-
import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE;
84

95
import android.app.Activity;
106
import android.content.Intent;
@@ -37,7 +33,7 @@
3733
import org.schabi.newpipe.extractor.NewPipe;
3834
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
3935
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
40-
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
36+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
4137
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
4238
import org.schabi.newpipe.streams.io.StoredFileHelper;
4339
import org.schabi.newpipe.util.Constants;
@@ -168,10 +164,8 @@ private void onImportClicked() {
168164
}
169165

170166
public void onImportUrl(final String value) {
171-
ImportConfirmationDialog.show(this, new Intent(activity, SubscriptionsImportService.class)
172-
.putExtra(KEY_MODE, CHANNEL_URL_MODE)
173-
.putExtra(KEY_VALUE, value)
174-
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
167+
ImportConfirmationDialog.show(this,
168+
new SubscriptionImportInput.ChannelUrlMode(currentServiceId, value));
175169
}
176170

177171
public void onImportFile() {
@@ -186,16 +180,10 @@ public void onImportFile() {
186180
}
187181

188182
private void requestImportFileResult(final ActivityResult result) {
189-
if (result.getData() == null) {
190-
return;
191-
}
192-
193-
if (result.getResultCode() == Activity.RESULT_OK && result.getData().getData() != null) {
183+
final String data = result.getData() != null ? result.getData().getDataString() : null;
184+
if (result.getResultCode() == Activity.RESULT_OK && data != null) {
194185
ImportConfirmationDialog.show(this,
195-
new Intent(activity, SubscriptionsImportService.class)
196-
.putExtra(KEY_MODE, INPUT_STREAM_MODE)
197-
.putExtra(KEY_VALUE, result.getData().getData())
198-
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
186+
new SubscriptionImportInput.InputStreamMode(currentServiceId, data));
199187
}
200188
}
201189

0 commit comments

Comments
 (0)