1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-11-12 21:22:59 +00:00

SubscriptionImportWorker: inputs as sealed class

This commit is contained in:
Profpatsch
2025-01-22 15:34:04 +01:00
parent 21973b362a
commit dbd11a6a8d
4 changed files with 96 additions and 57 deletions

View File

@@ -11,7 +11,6 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.work.Constraints; import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy; import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType; import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest;
@@ -22,31 +21,19 @@ import com.evernote.android.state.State;
import com.livefront.bridge.Bridge; import com.livefront.bridge.Bridge;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker; import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker;
import org.schabi.newpipe.util.Constants;
public class ImportConfirmationDialog extends DialogFragment { public class ImportConfirmationDialog extends DialogFragment {
@State @State
protected int mode; protected SubscriptionImportInput input;
@State
protected String value;
@State
protected int serviceId;
public static void show(@NonNull final Fragment fragment, final int mode, public static void show(@NonNull final Fragment fragment, final SubscriptionImportInput input) {
@Nullable final String value, final int serviceId) {
final var confirmationDialog = new ImportConfirmationDialog(); final var confirmationDialog = new ImportConfirmationDialog();
confirmationDialog.setData(mode, value, serviceId); confirmationDialog.input = input;
confirmationDialog.show(fragment.getParentFragmentManager(), null); confirmationDialog.show(fragment.getParentFragmentManager(), null);
} }
@SuppressWarnings("HiddenField")
public void setData(final int mode, final String value, final int serviceId) {
this.mode = mode;
this.value = value;
this.serviceId = serviceId;
}
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
@@ -57,17 +44,12 @@ public class ImportConfirmationDialog extends DialogFragment {
.setCancelable(true) .setCancelable(true)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, (dialogInterface, i) -> { .setPositiveButton(R.string.ok, (dialogInterface, i) -> {
final var inputData = new Data.Builder()
.putString(SubscriptionImportWorker.KEY_VALUE, value)
.putInt(SubscriptionImportWorker.KEY_MODE, mode)
.putInt(Constants.KEY_SERVICE_ID, serviceId)
.build();
final var constraints = new Constraints.Builder() final var constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) .setRequiredNetworkType(NetworkType.CONNECTED)
.build(); .build();
final var req = new OneTimeWorkRequest.Builder(SubscriptionImportWorker.class) final var req = new OneTimeWorkRequest.Builder(SubscriptionImportWorker.class)
.setInputData(inputData) .setInputData(input.toData())
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setConstraints(constraints) .setConstraints(constraints)
.build(); .build();
@@ -85,10 +67,6 @@ public class ImportConfirmationDialog extends DialogFragment {
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (mode == 0 && value == null && serviceId == 0) {
throw new IllegalStateException("Input data not provided");
}
Bridge.restoreInstanceState(this, savedInstanceState); Bridge.restoreInstanceState(this, savedInstanceState);
} }

View File

@@ -49,11 +49,10 @@ import org.schabi.newpipe.local.subscription.item.GroupsHeader
import org.schabi.newpipe.local.subscription.item.Header import org.schabi.newpipe.local.subscription.item.Header
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.streams.io.StoredFileHelper
import org.schabi.newpipe.ui.emptystate.setEmptyStateComposable import org.schabi.newpipe.ui.emptystate.setEmptyStateComposable
import org.schabi.newpipe.util.NO_SERVICE_ID
import org.schabi.newpipe.util.NavigationHelper import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.OnClickGesture import org.schabi.newpipe.util.OnClickGesture
import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.ServiceHelper
@@ -231,7 +230,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
val data = result.data?.dataString val data = result.data?.dataString
if (data != null && result.resultCode == Activity.RESULT_OK) { if (data != null && result.resultCode == Activity.RESULT_OK) {
ImportConfirmationDialog.show( ImportConfirmationDialog.show(
this, SubscriptionImportWorker.PREVIOUS_EXPORT_MODE, data, NO_SERVICE_ID this, SubscriptionImportInput.PreviousExportMode(data)
) )
} }
} }

View File

@@ -33,7 +33,7 @@ import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportWorker; import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput;
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
import org.schabi.newpipe.streams.io.StoredFileHelper; import org.schabi.newpipe.streams.io.StoredFileHelper;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
@@ -164,8 +164,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
} }
public void onImportUrl(final String value) { public void onImportUrl(final String value) {
ImportConfirmationDialog.show(this, SubscriptionImportWorker.CHANNEL_URL_MODE, value, ImportConfirmationDialog.show(this,
currentServiceId); new SubscriptionImportInput.ChannelUrlMode(currentServiceId, value));
} }
public void onImportFile() { public void onImportFile() {
@@ -182,8 +182,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
private void requestImportFileResult(final ActivityResult result) { private void requestImportFileResult(final ActivityResult result) {
final String data = result.getData() != null ? result.getData().getDataString() : null; final String data = result.getData() != null ? result.getData().getDataString() : null;
if (result.getResultCode() == Activity.RESULT_OK && data != null) { if (result.getResultCode() == Activity.RESULT_OK && data != null) {
ImportConfirmationDialog.show(this, SubscriptionImportWorker.INPUT_STREAM_MODE, ImportConfirmationDialog.show(this,
data, currentServiceId); new SubscriptionImportInput.InputStreamMode(currentServiceId, data));
} }
} }

View File

@@ -3,11 +3,13 @@ package org.schabi.newpipe.local.subscription.workers
import android.content.Context import android.content.Context
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.os.Build import android.os.Build
import android.os.Parcelable
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.ForegroundInfo import androidx.work.ForegroundInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
@@ -18,12 +20,11 @@ import kotlinx.coroutines.rx3.await
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.local.subscription.SubscriptionManager import org.schabi.newpipe.local.subscription.SubscriptionManager
import org.schabi.newpipe.util.ExtractorHelper import org.schabi.newpipe.util.ExtractorHelper
import org.schabi.newpipe.util.KEY_SERVICE_ID
import org.schabi.newpipe.util.NO_SERVICE_ID
class SubscriptionImportWorker( class SubscriptionImportWorker(
appContext: Context, appContext: Context,
@@ -35,27 +36,29 @@ class SubscriptionImportWorker(
} }
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
val mode = inputData.getInt(KEY_MODE, CHANNEL_URL_MODE) val input = SubscriptionImportInput.fromData(inputData)
val serviceId = inputData.getInt(KEY_SERVICE_ID, NO_SERVICE_ID)
val value = inputData.getString(KEY_VALUE)!!
val subscriptions = withContext(Dispatchers.IO) { val subscriptions = withContext(Dispatchers.IO) {
if (mode == CHANNEL_URL_MODE) { when (input) {
NewPipe.getService(serviceId).subscriptionExtractor is SubscriptionImportInput.ChannelUrlMode ->
.fromChannelUrl(value) NewPipe.getService(input.serviceId).subscriptionExtractor
.map { SubscriptionItem(it.serviceId, it.url, it.name) } .fromChannelUrl(input.url)
} else { .map { SubscriptionItem(it.serviceId, it.url, it.name) }
applicationContext.contentResolver.openInputStream(value.toUri())?.use {
if (mode == INPUT_STREAM_MODE) { is SubscriptionImportInput.InputStreamMode ->
val contentType = MimeTypeMap.getFileExtensionFromUrl(value).ifEmpty { DEFAULT_MIME } applicationContext.contentResolver.openInputStream(input.url.toUri())?.use {
NewPipe.getService(serviceId).subscriptionExtractor val contentType =
MimeTypeMap.getFileExtensionFromUrl(input.url).ifEmpty { DEFAULT_MIME }
NewPipe.getService(input.serviceId).subscriptionExtractor
.fromInputStream(it, contentType) .fromInputStream(it, contentType)
.map { SubscriptionItem(it.serviceId, it.url, it.name) } .map { SubscriptionItem(it.serviceId, it.url, it.name) }
} else { }
is SubscriptionImportInput.PreviousExportMode ->
applicationContext.contentResolver.openInputStream(input.url.toUri())?.use {
ImportExportJsonHelper.readFrom(it) ImportExportJsonHelper.readFrom(it)
} }
} ?: emptyList() } ?: emptyList()
}
} }
val mutex = Mutex() val mutex = Mutex()
@@ -146,10 +149,69 @@ class SubscriptionImportWorker(
private const val BUFFER_COUNT_BEFORE_INSERT = 50 private const val BUFFER_COUNT_BEFORE_INSERT = 50
const val WORK_NAME = "SubscriptionImportWorker" const val WORK_NAME = "SubscriptionImportWorker"
const val CHANNEL_URL_MODE = 0 }
const val INPUT_STREAM_MODE = 1 }
const val PREVIOUS_EXPORT_MODE = 2
const val KEY_MODE = "key_mode" sealed class SubscriptionImportInput : Parcelable {
const val KEY_VALUE = "key_value" @Parcelize
data class ChannelUrlMode(val serviceId: Int, val url: String) : SubscriptionImportInput()
@Parcelize
data class InputStreamMode(val serviceId: Int, val url: String) : SubscriptionImportInput()
@Parcelize
data class PreviousExportMode(val url: String) : SubscriptionImportInput()
fun toData(): Data {
return when (this) {
is ChannelUrlMode -> Data.Builder()
.putInt("mode", CHANNEL_URL_MODE)
.putInt("service_id", serviceId)
.putString("url", url)
.build()
is InputStreamMode ->
Data.Builder()
.putInt("mode", INPUT_STREAM_MODE)
.putInt("service_id", serviceId)
.putString("url", url)
.build()
is PreviousExportMode ->
Data.Builder()
.putInt("mode", PREVIOUS_EXPORT_MODE)
.putString("url", url)
.build()
}
}
companion object {
private const val CHANNEL_URL_MODE = 0
private const val INPUT_STREAM_MODE = 1
private const val PREVIOUS_EXPORT_MODE = 2
fun fromData(data: Data): SubscriptionImportInput {
val mode = data.getInt("mode", PREVIOUS_EXPORT_MODE)
when (mode) {
CHANNEL_URL_MODE -> {
val serviceId = data.getInt("service_id", -1)
if (serviceId == -1) {
throw IllegalArgumentException("No service id provided")
}
val url = data.getString("url")!!
return ChannelUrlMode(serviceId, url)
}
INPUT_STREAM_MODE -> {
val serviceId = data.getInt("service_id", -1)
if (serviceId == -1) {
throw IllegalArgumentException("No service id provided")
}
val url = data.getString("url")!!
return InputStreamMode(serviceId, url)
}
PREVIOUS_EXPORT_MODE -> {
val url = data.getString("url")!!
return PreviousExportMode(url)
}
else -> throw IllegalArgumentException("Unknown mode: $mode")
}
}
} }
} }