diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt index 432771323..9a817362c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt @@ -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() { private lateinit var viewModel: SubscriptionViewModel private lateinit var subscriptionManager: SubscriptionManager + private lateinit var importExportHelper: SubscriptionsImportExportHelper private val disposables: CompositeDisposable = CompositeDisposable() private val groupAdapter = GroupAdapter>() @@ -76,11 +67,6 @@ class SubscriptionFragment : BaseStateFragment() { 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() { 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() { // -- 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() { // -- 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() { 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 // //////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportExportHelper.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportExportHelper.kt new file mode 100644 index 000000000..b853dcd41 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportExportHelper.kt @@ -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 + ) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java index baaa93e44..b5d303288 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java @@ -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 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) { diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 7875c1e84..d01709d27 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -413,6 +413,8 @@ import_export_data_path import_data export_data + import_subscriptions_key + export_subscriptions_key clear_cookie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb03a3bcf..3ec84bfff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -520,6 +520,11 @@ Exporting… Import file Previous export + Import subscriptions" + Export subscriptions + Import subscriptions from a previous .json export" + Export your subscriptions to a .json file + Import from previous export Could not import subscriptions Could not export subscriptions Import YouTube subscriptions from Google takeout: diff --git a/app/src/main/res/xml/backup_restore_settings.xml b/app/src/main/res/xml/backup_restore_settings.xml index ef6a3cde3..ff52948f0 100644 --- a/app/src/main/res/xml/backup_restore_settings.xml +++ b/app/src/main/res/xml/backup_restore_settings.xml @@ -22,4 +22,18 @@ android:summary="@string/reset_settings_summary" app:singleLineTitle="false" app:iconSpaceReserved="false" /> + + + + \ No newline at end of file