Skip to content

Commit 941edd7

Browse files
committed
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
1 parent c2b6984 commit 941edd7

6 files changed

Lines changed: 130 additions & 55 deletions

File tree

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

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

3-
import android.app.Activity
43
import android.content.Context
54
import android.content.DialogInterface
65
import android.os.Bundle
@@ -14,8 +13,6 @@ import android.view.View
1413
import android.view.ViewGroup
1514
import android.webkit.MimeTypeMap
1615
import android.widget.Toast
17-
import androidx.activity.result.ActivityResult
18-
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
1916
import androidx.annotation.StringRes
2017
import androidx.appcompat.app.AlertDialog
2118
import androidx.lifecycle.ViewModelProvider
@@ -26,9 +23,6 @@ import com.xwray.groupie.GroupAdapter
2623
import com.xwray.groupie.Section
2724
import com.xwray.groupie.viewbinding.GroupieViewHolder
2825
import io.reactivex.rxjava3.disposables.CompositeDisposable
29-
import java.text.SimpleDateFormat
30-
import java.util.Date
31-
import java.util.Locale
3226
import org.schabi.newpipe.R
3327
import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.GROUP_ALL_ID
3428
import org.schabi.newpipe.databinding.DialogTitleBinding
@@ -52,10 +46,6 @@ import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem
5246
import org.schabi.newpipe.local.subscription.item.GroupsHeader
5347
import org.schabi.newpipe.local.subscription.item.Header
5448
import org.schabi.newpipe.local.subscription.item.ImportSubscriptionsHintPlaceholderItem
55-
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
56-
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
57-
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
58-
import org.schabi.newpipe.streams.io.StoredFileHelper
5949
import org.schabi.newpipe.ui.emptystate.setEmptyStateComposable
6050
import org.schabi.newpipe.util.NavigationHelper
6151
import org.schabi.newpipe.util.OnClickGesture
@@ -69,6 +59,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
6959

7060
private lateinit var viewModel: SubscriptionViewModel
7161
private lateinit var subscriptionManager: SubscriptionManager
62+
private lateinit var importExportHelper: SubscriptionsImportExportHelper
7263
private val disposables: CompositeDisposable = CompositeDisposable()
7364

7465
private val groupAdapter = GroupAdapter<GroupieViewHolder<FeedItemCarouselBinding>>()
@@ -77,11 +68,6 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
7768
private lateinit var feedGroupsSortMenuItem: GroupsHeader
7869
private val subscriptionsSection = Section()
7970

80-
private val requestExportLauncher =
81-
registerForActivityResult(StartActivityForResult(), this::requestExportResult)
82-
private val requestImportLauncher =
83-
registerForActivityResult(StartActivityForResult(), this::requestImportResult)
84-
8571
@State
8672
@JvmField
8773
var itemsListState: Parcelable? = null
@@ -101,6 +87,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
10187
override fun onAttach(context: Context) {
10288
super.onAttach(context)
10389
subscriptionManager = SubscriptionManager(requireContext())
90+
importExportHelper = SubscriptionsImportExportHelper(this)
10491
}
10592

10693
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -141,7 +128,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
141128
// -- Import --
142129
val importSubMenu = menu.addSubMenu(R.string.import_from)
143130

144-
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { onImportPreviousSelected() }
131+
addMenuItemToSubmenu(importSubMenu, R.string.previous_export) { importExportHelper.onImportPreviousSelected() }
145132
.setIcon(R.drawable.ic_backup)
146133

147134
for (service in ServiceList.all()) {
@@ -159,7 +146,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
159146
// -- Export --
160147
val exportSubMenu = menu.addSubMenu(R.string.export_to)
161148

162-
addMenuItemToSubmenu(exportSubMenu, R.string.file) { onExportSelected() }
149+
addMenuItemToSubmenu(exportSubMenu, R.string.file) { importExportHelper.onExportSelected() }
163150
.setIcon(R.drawable.ic_save)
164151
}
165152

@@ -195,48 +182,10 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
195182
NavigationHelper.openSubscriptionsImportFragment(fragmentManager, serviceId)
196183
}
197184

198-
private fun onImportPreviousSelected() {
199-
NoFileManagerSafeGuard.launchSafe(
200-
requestImportLauncher,
201-
StoredFileHelper.getPicker(activity, JSON_MIME_TYPE),
202-
TAG,
203-
requireContext()
204-
)
205-
}
206-
207-
private fun onExportSelected() {
208-
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
209-
val exportName = "newpipe_subscriptions_$date.json"
210-
211-
NoFileManagerSafeGuard.launchSafe(
212-
requestExportLauncher,
213-
StoredFileHelper.getNewPicker(activity, exportName, JSON_MIME_TYPE, null),
214-
TAG,
215-
requireContext()
216-
)
217-
}
218-
219185
private fun openReorderDialog() {
220186
FeedGroupReorderDialog().show(parentFragmentManager, null)
221187
}
222188

223-
private fun requestExportResult(result: ActivityResult) {
224-
val data = result.data?.data
225-
if (data != null && result.resultCode == Activity.RESULT_OK) {
226-
SubscriptionExportWorker.schedule(activity, data)
227-
}
228-
}
229-
230-
private fun requestImportResult(result: ActivityResult) {
231-
val data = result.data?.dataString
232-
if (data != null && result.resultCode == Activity.RESULT_OK) {
233-
ImportConfirmationDialog.show(
234-
this,
235-
SubscriptionImportInput.PreviousExportMode(data)
236-
)
237-
}
238-
}
239-
240189
// ////////////////////////////////////////////////////////////////////////
241190
// Fragment Views
242191
// ////////////////////////////////////////////////////////////////////////
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package org.schabi.newpipe.local.subscription
2+
3+
import android.app.Activity
4+
import android.content.Context
5+
import androidx.activity.result.ActivityResult
6+
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
7+
import androidx.fragment.app.Fragment
8+
import java.text.SimpleDateFormat
9+
import java.util.Date
10+
import java.util.Locale
11+
import org.schabi.newpipe.local.subscription.SubscriptionFragment.Companion.JSON_MIME_TYPE
12+
import org.schabi.newpipe.local.subscription.workers.SubscriptionExportWorker
13+
import org.schabi.newpipe.local.subscription.workers.SubscriptionImportInput
14+
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard
15+
import org.schabi.newpipe.streams.io.StoredFileHelper
16+
17+
/**
18+
* This class has to be created in onAttach() or onCreate().
19+
*
20+
* It contains registerForActivityResult calls and those
21+
* calls are only allowed before a fragment/activity is created.
22+
*/
23+
class SubscriptionsImportExportHelper(
24+
val fragment: Fragment
25+
) {
26+
val context: Context = fragment.requireContext()
27+
28+
companion object {
29+
val TAG: String =
30+
SubscriptionsImportExportHelper::class.java.simpleName + "@" + Integer.toHexString(
31+
hashCode()
32+
)
33+
}
34+
35+
private val requestExportLauncher =
36+
fragment.registerForActivityResult(StartActivityForResult(), this::requestExportResult)
37+
private val requestImportLauncher =
38+
fragment.registerForActivityResult(StartActivityForResult(), this::requestImportResult)
39+
40+
private fun requestExportResult(result: ActivityResult) {
41+
val data = result.data?.data
42+
if (data != null && result.resultCode == Activity.RESULT_OK) {
43+
SubscriptionExportWorker.schedule(context, data)
44+
}
45+
}
46+
47+
private fun requestImportResult(result: ActivityResult) {
48+
val data = result.data?.dataString
49+
if (data != null && result.resultCode == Activity.RESULT_OK) {
50+
ImportConfirmationDialog.show(
51+
fragment,
52+
SubscriptionImportInput.PreviousExportMode(data)
53+
)
54+
}
55+
}
56+
57+
fun onExportSelected() {
58+
val date = SimpleDateFormat("yyyyMMddHHmm", Locale.ENGLISH).format(Date())
59+
val exportName = "newpipe_subscriptions_$date.json"
60+
61+
NoFileManagerSafeGuard.launchSafe(
62+
requestExportLauncher,
63+
StoredFileHelper.getNewPicker(
64+
context,
65+
exportName,
66+
JSON_MIME_TYPE,
67+
null
68+
),
69+
TAG,
70+
context
71+
)
72+
}
73+
74+
fun onImportPreviousSelected() {
75+
NoFileManagerSafeGuard.launchSafe(
76+
requestImportLauncher,
77+
StoredFileHelper.getPicker(context, JSON_MIME_TYPE),
78+
TAG,
79+
context
80+
)
81+
}
82+
}

app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.schabi.newpipe.error.ErrorInfo;
2727
import org.schabi.newpipe.error.ErrorUtil;
2828
import org.schabi.newpipe.error.UserAction;
29+
import org.schabi.newpipe.local.subscription.SubscriptionsImportExportHelper;
2930
import org.schabi.newpipe.settings.export.BackupFileLocator;
3031
import org.schabi.newpipe.settings.export.ImportExportManager;
3132
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
@@ -54,8 +55,15 @@ public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
5455
private final ActivityResultLauncher<Intent> requestExportPathLauncher =
5556
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
5657
this::requestExportPathResult);
58+
private SubscriptionsImportExportHelper importExportHelper;
5759

5860

61+
@Override
62+
public void onAttach(@NonNull final Context context) {
63+
super.onAttach(context);
64+
importExportHelper = new SubscriptionsImportExportHelper(this);
65+
}
66+
5967
@Override
6068
public void onCreatePreferences(@Nullable final Bundle savedInstanceState,
6169
@Nullable final String rootKey) {
@@ -117,6 +125,21 @@ ZIP_MIME_TYPE, getImportExportDataUri()),
117125
alertDialog.show();
118126
return true;
119127
});
128+
129+
final Preference exportSubsPreference =
130+
requirePreference(R.string.export_subscriptions_key);
131+
exportSubsPreference.setOnPreferenceClickListener(reference -> {
132+
importExportHelper.onExportSelected();
133+
return true;
134+
});
135+
136+
final Preference importSubsPreference =
137+
requirePreference(R.string.import_subscriptions_key);
138+
importSubsPreference.setOnPreferenceClickListener(preference -> {
139+
importExportHelper.onImportPreviousSelected();
140+
return true;
141+
});
142+
120143
}
121144

122145
private void requestExportPathResult(final ActivityResult result) {

app/src/main/res/values/settings_keys.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@
412412
<string name="import_export_data_path">import_export_data_path</string>
413413
<string name="import_data">import_data</string>
414414
<string name="export_data">export_data</string>
415+
<string name="import_subscriptions_key">import_subscriptions_key</string>
416+
<string name="export_subscriptions_key">export_subscriptions_key</string>
415417

416418
<string name="clear_cookie_key">clear_cookie</string>
417419

app/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,11 @@
509509
<string name="export_ongoing">Exporting…</string>
510510
<string name="import_file_title">Import file</string>
511511
<string name="previous_export">Previous export</string>
512+
<string name="import_subscriptions_title">Import subscriptions"</string>
513+
<string name="export_subscriptions_title">Export subscriptions</string>
514+
<string name="import_subscriptions_summary">Import subscriptions from a previous .json export"</string>
515+
<string name="export_subscriptions_summary">Export your subscriptions to a .json file</string>
516+
<string name="import_from_previous_export">Import from previous export</string>
512517
<string name="subscriptions_import_unsuccessful">Could not import subscriptions</string>
513518
<string name="subscriptions_export_unsuccessful">Could not export subscriptions</string>
514519
<string name="import_youtube_instructions">Import YouTube subscriptions from Google takeout:

app/src/main/res/xml/backup_restore_settings.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,18 @@
2222
android:summary="@string/reset_settings_summary"
2323
app:singleLineTitle="false"
2424
app:iconSpaceReserved="false" />
25+
26+
<Preference
27+
android:key="@string/export_subscriptions_key"
28+
android:title="@string/export_subscriptions_title"
29+
android:summary="@string/export_subscriptions_summary"
30+
app:singleLineTitle="false"
31+
app:iconSpaceReserved="false" />
32+
33+
<Preference
34+
android:key="@string/import_subscriptions_key"
35+
android:title="@string/import_subscriptions_title"
36+
android:summary="@string/import_subscriptions_summary"
37+
app:singleLineTitle="false"
38+
app:iconSpaceReserved="false" />
2539
</PreferenceScreen>

0 commit comments

Comments
 (0)