1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2026-03-18 05:39:44 +00:00

BackupRestoreSettingsFragment: add UI options to import/export subscriptions

* create SubscriptionsImportExportHelper to share common code used in
  SubscriptionFragment and BackupRestoreSettingsFragment
* Add UI options for import/export in BackupRestoreSettingsFragment
This commit is contained in:
evermind
2026-02-13 01:40:11 +01:00
committed by Aayush Gupta
parent 6fa97e17f5
commit bfcc31ec89
6 changed files with 130 additions and 55 deletions

View File

@@ -1,6 +1,5 @@
package org.schabi.newpipe.local.subscription
import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
@@ -14,8 +13,6 @@ import android.view.View
import android.view.ViewGroup
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProvider
@@ -26,9 +23,6 @@ import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.Section
import com.xwray.groupie.viewbinding.GroupieViewHolder
import io.reactivex.rxjava3.disposables.CompositeDisposable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.GROUP_ALL_ID
import org.schabi.newpipe.databinding.DialogTitleBinding
@@ -52,10 +46,6 @@ import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
import org.schabi.newpipe.local.subscription.item.GroupsHeader
import org.schabi.newpipe.local.subscription.item.Header
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
import org.schabi.newpipe.streams.io.StoredFileHelper
import org.schabi.newpipe.util.NavigationHelper
import org.schabi.newpipe.util.OnClickGesture
import org.schabi.newpipe.util.ServiceHelper
@@ -68,6 +58,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
private lateinit var viewModel: SubscriptionViewModel
private lateinit var subscriptionManager: SubscriptionManager
private lateinit var importExportHelper: SubscriptionsImportExportHelper
private val disposables: CompositeDisposable = CompositeDisposable()
private val groupAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
@@ -76,11 +67,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
private lateinit var feedGroupsSortMenuItem: GroupsHeader
private val subscriptionsSection = Section()
private val requestExportLauncher =
registerForActivityResult(StartActivityForResult(), this::requestExportResult)
private val requestImportLauncher =
registerForActivityResult(StartActivityForResult(), this::requestImportResult)
@State
@JvmField
var itemsListState: Parcelable? = null
@@ -100,6 +86,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
override fun onAttach(context: Context) {
super.onAttach(context)
subscriptionManager = SubscriptionManager(requireContext())
importExportHelper = SubscriptionsImportExportHelper(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -139,7 +126,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
// -- Import --
val importSubMenu = menu.addSubMenu(R.string.import_from)
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { onImportPreviousSelected() }
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { importExportHelper.onImportPreviousSelected() }
.setIcon(R.drawable.ic_backup)
for (service in ServiceList.all()) {
@@ -157,7 +144,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
// -- Export --
val exportSubMenu = menu.addSubMenu(R.string.export_to)
addMenuItemToSubmenu(exportSubMenu, R.string.file) { onExportSelected() }
addMenuItemToSubmenu(exportSubMenu, R.string.file) { importExportHelper.onExportSelected() }
.setIcon(R.drawable.ic_save)
}
@@ -193,48 +180,10 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
NavigationHelper.openSubscriptionsImportFragment(fragmentManager, serviceId)
}
private fun onImportPreviousSelected() {
NoFileManagerSafeGuard.launchSafe(
requestImportLauncher,
StoredFileHelper.getPicker(activity, JSON_MIME_TYPE),
TAG,
requireContext()
)
}
private fun onExportSelected() {
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
val exportName = "newpipe_subscriptions_$date.json"
NoFileManagerSafeGuard.launchSafe(
requestExportLauncher,
StoredFileHelper.getNewPicker(activity, exportName, JSON_MIME_TYPE, null),
TAG,
requireContext()
)
}
private fun openReorderDialog() {
FeedGroupReorderDialog().show(parentFragmentManager, null)
}
private fun requestExportResult(result: ActivityResult) {
val data = result.data?.data
if (data != null && result.resultCode == Activity.RESULT_OK) {
SubscriptionExportWorker.schedule(activity, data)
}
}
private fun requestImportResult(result: ActivityResult) {
val data = result.data?.dataString
if (data != null && result.resultCode == Activity.RESULT_OK) {
ImportConfirmationDialog.show(
this,
SubscriptionImportInput.PreviousExportMode(data)
)
}
}
// ////////////////////////////////////////////////////////////////////////
// Fragment Views
// ////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,82 @@
package org.schabi.newpipe.local.subscription
import android.app.Activity
import android.content.Context
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.fragment.app.Fragment
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import org.schabi.newpipe.local.subscription.SubscriptionFragment.Companion.JSON_MIME_TYPE
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
import org.schabi.newpipe.streams.io.StoredFileHelper
/**
* This class has to be created in onAttach() or onCreate().
*
* It contains registerForActivityResult calls and those
* calls are only allowed before a fragment/activity is created.
*/
class SubscriptionsImportExportHelper(
val fragment: Fragment
) {
val context: Context = fragment.requireContext()
companion object {
val TAG: String =
SubscriptionsImportExportHelper::class.java.simpleName + "@" + Integer.toHexString(
hashCode()
)
}
private val requestExportLauncher =
fragment.registerForActivityResult(StartActivityForResult(), this::requestExportResult)
private val requestImportLauncher =
fragment.registerForActivityResult(StartActivityForResult(), this::requestImportResult)
private fun requestExportResult(result: ActivityResult) {
val data = result.data?.data
if (data != null && result.resultCode == Activity.RESULT_OK) {
SubscriptionExportWorker.schedule(context, data)
}
}
private fun requestImportResult(result: ActivityResult) {
val data = result.data?.dataString
if (data != null && result.resultCode == Activity.RESULT_OK) {
ImportConfirmationDialog.show(
fragment,
SubscriptionImportInput.PreviousExportMode(data)
)
}
}
fun onExportSelected() {
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
val exportName = "newpipe_subscriptions_$date.json"
NoFileManagerSafeGuard.launchSafe(
requestExportLauncher,
StoredFileHelper.getNewPicker(
context,
exportName,
JSON_MIME_TYPE,
null
),
TAG,
context
)
}
fun onImportPreviousSelected() {
NoFileManagerSafeGuard.launchSafe(
requestImportLauncher,
StoredFileHelper.getPicker(context, JSON_MIME_TYPE),
TAG,
context
)
}
}

View File

@@ -27,6 +27,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.local.subscription.SubscriptionsImportExportHelper;
import org.schabi.newpipe.settings.export.BackupFileLocator;
import org.schabi.newpipe.settings.export.ImportExportManager;
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
@@ -57,8 +58,15 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
private final ActivityResultLauncher<Intent> requestExportPathLauncher =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
this::requestExportPathResult);
private SubscriptionsImportExportHelper importExportHelper;
@Override
public void onAttach(@NonNull final Context context) {
super.onAttach(context);
importExportHelper = new SubscriptionsImportExportHelper(this);
}
@Override
public void onCreatePreferences(@Nullable final Bundle savedInstanceState,
@Nullable final String rootKey) {
@@ -123,6 +131,21 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
alertDialog.show();
return true;
});
final Preference exportSubsPreference =
requirePreference(R.string.export_subscriptions_key);
exportSubsPreference.setOnPreferenceClickListener(reference -> {
importExportHelper.onExportSelected();
return true;
});
final Preference importSubsPreference =
requirePreference(R.string.import_subscriptions_key);
importSubsPreference.setOnPreferenceClickListener(preference -> {
importExportHelper.onImportPreviousSelected();
return true;
});
}
private void requestExportPathResult(final ActivityResult result) {

View File

@@ -413,6 +413,8 @@
<string name="import_export_data_path">import_export_data_path</string>
<string name="import_data">import_data</string>
<string name="export_data">export_data</string>
<string name="import_subscriptions_key">import_subscriptions_key</string>
<string name="export_subscriptions_key">export_subscriptions_key</string>
<string name="clear_cookie_key">clear_cookie</string>

View File

@@ -520,6 +520,11 @@
<string name="export_ongoing">Exporting…</string>
<string name="import_file_title">Import file</string>
<string name="previous_export">Previous export</string>
<string name="import_subscriptions_title">Import subscriptions"</string>
<string name="export_subscriptions_title">Export subscriptions</string>
<string name="import_subscriptions_summary">Import subscriptions from a previous .json export"</string>
<string name="export_subscriptions_summary">Export your subscriptions to a .json file</string>
<string name="import_from_previous_export">Import from previous export</string>
<string name="subscriptions_import_unsuccessful">Could not import subscriptions</string>
<string name="subscriptions_export_unsuccessful">Could not export subscriptions</string>
<string name="import_youtube_instructions">Import YouTube subscriptions from Google takeout:

View File

@@ -22,4 +22,18 @@
android:summary="@string/reset_settings_summary"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<Preference
android:key="@string/export_subscriptions_key"
android:title="@string/export_subscriptions_title"
android:summary="@string/export_subscriptions_summary"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
<Preference
android:key="@string/import_subscriptions_key"
android:title="@string/import_subscriptions_title"
android:summary="@string/import_subscriptions_summary"
app:singleLineTitle="false"
app:iconSpaceReserved="false" />
</PreferenceScreen>