Skip to content

Commit 8e9503c

Browse files
Isira-SeneviratneProfpatsch
authored andcommitted
Convert subscription export service to a worker
1 parent 82516dd commit 8e9503c

13 files changed

Lines changed: 240 additions & 326 deletions

File tree

app/build.gradle

Lines changed: 5 additions & 1 deletion
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 {
@@ -314,6 +315,9 @@ dependencies {
314315
// Scroll
315316
implementation libs.lazycolumnscrollbar
316317

318+
// Kotlinx Serialization
319+
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
320+
317321
/** Debugging **/
318322
// Memory leak detection
319323
debugImplementation libs.leakcanary.object.watcher

app/proguard-rules.pro

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

2828
## For some reason NotificationModeConfigFragment wasn't kept (only referenced in a preference xml)
2929
-keep class org.schabi.newpipe.settings.notifications.** { *; }
30+
31+
## Keep Kotlinx Serialization classes
32+
-keepclassmembers class kotlinx.serialization.json.** {
33+
*** Companion;
34+
}
35+
-keepclasseswithmembers class kotlinx.serialization.json.** {
36+
kotlinx.serialization.KSerializer serializer(...);
37+
}
38+
-keep,includedescriptorclasses class org.schabi.newpipe.**$$serializer { *; }
39+
-keepclassmembers class org.schabi.newpipe.** {
40+
*** Companion;
41+
}
42+
-keepclasseswithmembers class org.schabi.newpipe.** {
43+
kotlinx.serialization.KSerializer serializer(...);
44+
}

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 1 deletion
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+ -->
@@ -87,8 +88,11 @@
8788
android:exported="false"
8889
android:label="@string/title_activity_about" />
8990

91+
<service
92+
android:name="androidx.work.impl.foreground.SystemForegroundService"
93+
android:foregroundServiceType="dataSync"
94+
tools:node="merge" />
9095
<service android:name=".local.subscription.services.SubscriptionsImportService" />
91-
<service android:name=".local.subscription.services.SubscriptionsExportService" />
9296
<service android:name=".local.feed.service.FeedLoadService" />
9397

9498
<activity

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
4949
import org.schabi.newpipe.local.subscription.item.GroupsHeader
5050
import org.schabi.newpipe.local.subscription.item.Header
5151
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
52-
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService
5352
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService
5453
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE
5554
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE
5655
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE
56+
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
5757
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
5858
import org.schabi.newpipe.streams.io.StoredFileHelper
5959
import org.schabi.newpipe.ui.emptystate.setEmptyStateComposable
@@ -224,11 +224,9 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
224224
}
225225

226226
private fun requestExportResult(result: ActivityResult) {
227-
if (result.data != null && result.resultCode == Activity.RESULT_OK) {
228-
activity.startService(
229-
Intent(activity, SubscriptionsExportService::class.java)
230-
.putExtra(SubscriptionsExportService.KEY_FILE_PATH, result.data?.data)
231-
)
227+
val data = result.data?.data
228+
if (data != null && result.resultCode == Activity.RESULT_OK) {
229+
SubscriptionExportWorker.schedule(activity, data)
232230
}
233231
}
234232

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

Lines changed: 29 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -17,142 +17,58 @@
1717
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20-
package org.schabi.newpipe.local.subscription.services;
21-
22-
import androidx.annotation.Nullable;
23-
24-
import com.grack.nanojson.JsonAppendableWriter;
25-
import com.grack.nanojson.JsonArray;
26-
import com.grack.nanojson.JsonObject;
27-
import com.grack.nanojson.JsonParser;
28-
import com.grack.nanojson.JsonWriter;
29-
30-
import org.schabi.newpipe.BuildConfig;
31-
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.InvalidSourceException;
32-
import org.schabi.newpipe.extractor.subscription.SubscriptionItem;
33-
34-
import java.io.InputStream;
35-
import java.io.OutputStream;
36-
import java.util.ArrayList;
37-
import java.util.List;
20+
package org.schabi.newpipe.local.subscription.services
21+
22+
import kotlinx.serialization.ExperimentalSerializationApi
23+
import kotlinx.serialization.json.Json
24+
import kotlinx.serialization.json.decodeFromStream
25+
import kotlinx.serialization.json.encodeToStream
26+
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.InvalidSourceException
27+
import org.schabi.newpipe.local.subscription.workers.SubscriptionData
28+
import org.schabi.newpipe.local.subscription.workers.SubscriptionItem
29+
import java.io.InputStream
30+
import java.io.OutputStream
3831

3932
/**
4033
* A JSON implementation capable of importing and exporting subscriptions, it has the advantage
4134
* of being able to transfer subscriptions to any device.
4235
*/
43-
public final class ImportExportJsonHelper {
44-
/*//////////////////////////////////////////////////////////////////////////
45-
// Json implementation
46-
//////////////////////////////////////////////////////////////////////////*/
47-
48-
private static final String JSON_APP_VERSION_KEY = "app_version";
49-
private static final String JSON_APP_VERSION_INT_KEY = "app_version_int";
50-
51-
private static final String JSON_SUBSCRIPTIONS_ARRAY_KEY = "subscriptions";
52-
53-
private static final String JSON_SERVICE_ID_KEY = "service_id";
54-
private static final String JSON_URL_KEY = "url";
55-
private static final String JSON_NAME_KEY = "name";
56-
57-
private ImportExportJsonHelper() { }
36+
object ImportExportJsonHelper {
37+
private val json = Json { encodeDefaults = true }
5838

5939
/**
6040
* Read a JSON source through the input stream.
6141
*
6242
* @param in the input stream (e.g. a file)
63-
* @param eventListener listener for the events generated
6443
* @return the parsed subscription items
6544
*/
66-
public static List<SubscriptionItem> readFrom(
67-
final InputStream in, @Nullable final ImportExportEventListener eventListener)
68-
throws InvalidSourceException {
69-
if (in == null) {
70-
throw new InvalidSourceException("input is null");
45+
@JvmStatic
46+
@Throws(InvalidSourceException::class)
47+
fun readFrom(`in`: InputStream?): List<SubscriptionItem> {
48+
if (`in` == null) {
49+
throw InvalidSourceException("input is null")
7150
}
7251

73-
final List<SubscriptionItem> channels = new ArrayList<>();
74-
7552
try {
76-
final JsonObject parentObject = JsonParser.object().from(in);
77-
78-
if (!parentObject.has(JSON_SUBSCRIPTIONS_ARRAY_KEY)) {
79-
throw new InvalidSourceException("Channels array is null");
80-
}
81-
82-
final JsonArray channelsArray = parentObject.getArray(JSON_SUBSCRIPTIONS_ARRAY_KEY);
83-
84-
if (eventListener != null) {
85-
eventListener.onSizeReceived(channelsArray.size());
86-
}
87-
88-
for (final Object o : channelsArray) {
89-
if (o instanceof JsonObject) {
90-
final JsonObject itemObject = (JsonObject) o;
91-
final int serviceId = itemObject.getInt(JSON_SERVICE_ID_KEY, 0);
92-
final String url = itemObject.getString(JSON_URL_KEY);
93-
final String name = itemObject.getString(JSON_NAME_KEY);
94-
95-
if (url != null && name != null && !url.isEmpty() && !name.isEmpty()) {
96-
channels.add(new SubscriptionItem(serviceId, url, name));
97-
if (eventListener != null) {
98-
eventListener.onItemCompleted(name);
99-
}
100-
}
101-
}
102-
}
103-
} catch (final Throwable e) {
104-
throw new InvalidSourceException("Couldn't parse json", e);
53+
@OptIn(ExperimentalSerializationApi::class)
54+
return json.decodeFromStream<SubscriptionData>(`in`).subscriptions
55+
} catch (e: Throwable) {
56+
throw InvalidSourceException("Couldn't parse json", e)
10557
}
106-
107-
return channels;
10858
}
10959

11060
/**
11161
* Write the subscriptions items list as JSON to the output.
11262
*
11363
* @param items the list of subscriptions items
11464
* @param out the output stream (e.g. a file)
115-
* @param eventListener listener for the events generated
11665
*/
117-
public static void writeTo(final List<SubscriptionItem> items, final OutputStream out,
118-
@Nullable final ImportExportEventListener eventListener) {
119-
final JsonAppendableWriter writer = JsonWriter.on(out);
120-
writeTo(items, writer, eventListener);
121-
writer.done();
122-
}
123-
124-
/**
125-
* @see #writeTo(List, OutputStream, ImportExportEventListener)
126-
* @param items the list of subscriptions items
127-
* @param writer the output {@link JsonAppendableWriter}
128-
* @param eventListener listener for the events generated
129-
*/
130-
public static void writeTo(final List<SubscriptionItem> items,
131-
final JsonAppendableWriter writer,
132-
@Nullable final ImportExportEventListener eventListener) {
133-
if (eventListener != null) {
134-
eventListener.onSizeReceived(items.size());
135-
}
136-
137-
writer.object();
138-
139-
writer.value(JSON_APP_VERSION_KEY, BuildConfig.VERSION_NAME);
140-
writer.value(JSON_APP_VERSION_INT_KEY, BuildConfig.VERSION_CODE);
141-
142-
writer.array(JSON_SUBSCRIPTIONS_ARRAY_KEY);
143-
for (final SubscriptionItem item : items) {
144-
writer.object();
145-
writer.value(JSON_SERVICE_ID_KEY, item.getServiceId());
146-
writer.value(JSON_URL_KEY, item.getUrl());
147-
writer.value(JSON_NAME_KEY, item.getName());
148-
writer.end();
149-
150-
if (eventListener != null) {
151-
eventListener.onItemCompleted(item.getName());
152-
}
153-
}
154-
writer.end();
155-
156-
writer.end();
66+
@OptIn(ExperimentalSerializationApi::class)
67+
@JvmStatic
68+
fun writeTo(
69+
items: List<SubscriptionItem>,
70+
out: OutputStream,
71+
) {
72+
json.encodeToStream(SubscriptionData(items), out)
15773
}
15874
}

0 commit comments

Comments
 (0)