Skip to content

Commit 2af95cc

Browse files
authored
Merge pull request #9236 from vincetzr/Option-to-reset-settings
Option to reset settings
2 parents cefdefd + 37f7fa7 commit 2af95cc

9 files changed

Lines changed: 311 additions & 231 deletions

File tree

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package org.schabi.newpipe.settings;
2+
3+
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
4+
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
5+
6+
import android.app.Activity;
7+
import android.app.AlertDialog;
8+
import android.content.Context;
9+
import android.content.Intent;
10+
import android.content.SharedPreferences;
11+
import android.net.Uri;
12+
import android.os.Bundle;
13+
import android.widget.Toast;
14+
15+
import androidx.activity.result.ActivityResult;
16+
import androidx.activity.result.ActivityResultLauncher;
17+
import androidx.activity.result.contract.ActivityResultContracts;
18+
import androidx.annotation.NonNull;
19+
import androidx.annotation.Nullable;
20+
import androidx.core.content.ContextCompat;
21+
import androidx.preference.Preference;
22+
import androidx.preference.PreferenceManager;
23+
24+
import org.schabi.newpipe.NewPipeDatabase;
25+
import org.schabi.newpipe.R;
26+
import org.schabi.newpipe.error.ErrorUtil;
27+
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
28+
import org.schabi.newpipe.streams.io.StoredFileHelper;
29+
import org.schabi.newpipe.util.NavigationHelper;
30+
import org.schabi.newpipe.util.ZipHelper;
31+
32+
import java.io.File;
33+
import java.io.IOException;
34+
import java.text.SimpleDateFormat;
35+
import java.util.Date;
36+
import java.util.Locale;
37+
import java.util.Objects;
38+
39+
public class BackupRestoreSettingsFragment extends BasePreferenceFragment {
40+
41+
private static final String ZIP_MIME_TYPE = "application/zip";
42+
43+
private final SimpleDateFormat exportDateFormat =
44+
new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
45+
private ContentSettingsManager manager;
46+
private String importExportDataPathKey;
47+
private final ActivityResultLauncher<Intent> requestImportPathLauncher =
48+
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
49+
this::requestImportPathResult);
50+
private final ActivityResultLauncher<Intent> requestExportPathLauncher =
51+
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
52+
this::requestExportPathResult);
53+
54+
55+
@Override
56+
public void onCreatePreferences(@Nullable final Bundle savedInstanceState,
57+
@Nullable final String rootKey) {
58+
final File homeDir = ContextCompat.getDataDir(requireContext());
59+
Objects.requireNonNull(homeDir);
60+
manager = new ContentSettingsManager(new NewPipeFileLocator(homeDir));
61+
manager.deleteSettingsFile();
62+
63+
importExportDataPathKey = getString(R.string.import_export_data_path);
64+
65+
66+
addPreferencesFromResourceRegistry();
67+
68+
final Preference importDataPreference = requirePreference(R.string.import_data);
69+
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
70+
NoFileManagerSafeGuard.launchSafe(
71+
requestImportPathLauncher,
72+
StoredFileHelper.getPicker(requireContext(),
73+
ZIP_MIME_TYPE, getImportExportDataUri()),
74+
TAG,
75+
getContext()
76+
);
77+
78+
return true;
79+
});
80+
81+
final Preference exportDataPreference = requirePreference(R.string.export_data);
82+
exportDataPreference.setOnPreferenceClickListener((final Preference p) -> {
83+
NoFileManagerSafeGuard.launchSafe(
84+
requestExportPathLauncher,
85+
StoredFileHelper.getNewPicker(requireContext(),
86+
"NewPipeData-" + exportDateFormat.format(new Date()) + ".zip",
87+
ZIP_MIME_TYPE, getImportExportDataUri()),
88+
TAG,
89+
getContext()
90+
);
91+
92+
return true;
93+
});
94+
95+
final Preference resetSettings = findPreference(getString(R.string.reset_settings));
96+
// Resets all settings by deleting shared preference and restarting the app
97+
// A dialogue will pop up to confirm if user intends to reset all settings
98+
assert resetSettings != null;
99+
resetSettings.setOnPreferenceClickListener(preference -> {
100+
// Show Alert Dialogue
101+
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
102+
builder.setMessage(R.string.reset_all_settings);
103+
builder.setCancelable(true);
104+
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
105+
// Deletes all shared preferences xml files.
106+
final SharedPreferences sharedPreferences =
107+
PreferenceManager.getDefaultSharedPreferences(requireContext());
108+
sharedPreferences.edit().clear().apply();
109+
// Restarts the app
110+
if (getActivity() == null) {
111+
return;
112+
}
113+
NavigationHelper.restartApp(getActivity());
114+
});
115+
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> {
116+
});
117+
final AlertDialog alertDialog = builder.create();
118+
alertDialog.show();
119+
return true;
120+
});
121+
}
122+
123+
private void requestExportPathResult(final ActivityResult result) {
124+
assureCorrectAppLanguage(requireContext());
125+
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
126+
// will be saved only on success
127+
final Uri lastExportDataUri = result.getData().getData();
128+
129+
final StoredFileHelper file = new StoredFileHelper(
130+
requireContext(), result.getData().getData(), ZIP_MIME_TYPE);
131+
132+
exportDatabase(file, lastExportDataUri);
133+
}
134+
}
135+
136+
private void requestImportPathResult(final ActivityResult result) {
137+
assureCorrectAppLanguage(requireContext());
138+
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
139+
// will be saved only on success
140+
final Uri lastImportDataUri = result.getData().getData();
141+
142+
final StoredFileHelper file = new StoredFileHelper(
143+
requireContext(), result.getData().getData(), ZIP_MIME_TYPE);
144+
145+
new androidx.appcompat.app.AlertDialog.Builder(requireActivity())
146+
.setMessage(R.string.override_current_data)
147+
.setPositiveButton(R.string.ok, (d, id) ->
148+
importDatabase(file, lastImportDataUri))
149+
.setNegativeButton(R.string.cancel, (d, id) ->
150+
d.cancel())
151+
.show();
152+
}
153+
}
154+
155+
private void exportDatabase(final StoredFileHelper file, final Uri exportDataUri) {
156+
try {
157+
//checkpoint before export
158+
NewPipeDatabase.checkpoint();
159+
160+
final SharedPreferences preferences = PreferenceManager
161+
.getDefaultSharedPreferences(requireContext());
162+
manager.exportDatabase(preferences, file);
163+
164+
saveLastImportExportDataUri(exportDataUri); // save export path only on success
165+
Toast.makeText(requireContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT)
166+
.show();
167+
} catch (final Exception e) {
168+
ErrorUtil.showUiErrorSnackbar(this, "Exporting database", e);
169+
}
170+
}
171+
172+
private void importDatabase(final StoredFileHelper file, final Uri importDataUri) {
173+
// check if file is supported
174+
if (!ZipHelper.isValidZipFile(file)) {
175+
Toast.makeText(requireContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
176+
.show();
177+
return;
178+
}
179+
180+
try {
181+
if (!manager.ensureDbDirectoryExists()) {
182+
throw new IOException("Could not create databases dir");
183+
}
184+
185+
if (!manager.extractDb(file)) {
186+
Toast.makeText(requireContext(), R.string.could_not_import_all_files,
187+
Toast.LENGTH_LONG)
188+
.show();
189+
}
190+
191+
// if settings file exist, ask if it should be imported.
192+
if (manager.extractSettings(file)) {
193+
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
194+
.setTitle(R.string.import_settings)
195+
.setNegativeButton(R.string.cancel, (dialog, which) -> {
196+
dialog.dismiss();
197+
finishImport(importDataUri);
198+
})
199+
.setPositiveButton(R.string.ok, (dialog, which) -> {
200+
dialog.dismiss();
201+
final Context context = requireContext();
202+
final SharedPreferences prefs = PreferenceManager
203+
.getDefaultSharedPreferences(context);
204+
manager.loadSharedPreferences(prefs);
205+
cleanImport(context, prefs);
206+
finishImport(importDataUri);
207+
})
208+
.show();
209+
} else {
210+
finishImport(importDataUri);
211+
}
212+
} catch (final Exception e) {
213+
ErrorUtil.showUiErrorSnackbar(this, "Importing database", e);
214+
}
215+
}
216+
217+
/**
218+
* Remove settings that are not supposed to be imported on different devices
219+
* and reset them to default values.
220+
* @param context the context used for the import
221+
* @param prefs the preferences used while running the import
222+
*/
223+
private void cleanImport(@NonNull final Context context,
224+
@NonNull final SharedPreferences prefs) {
225+
// Check if media tunnelling needs to be disabled automatically,
226+
// if it was disabled automatically in the imported preferences.
227+
final String tunnelingKey = context.getString(R.string.disable_media_tunneling_key);
228+
final String automaticTunnelingKey =
229+
context.getString(R.string.disabled_media_tunneling_automatically_key);
230+
// R.string.disable_media_tunneling_key should always be true
231+
// if R.string.disabled_media_tunneling_automatically_key equals 1,
232+
// but we double check here just to be sure and to avoid regressions
233+
// caused by possible later modification of the media tunneling functionality.
234+
// R.string.disabled_media_tunneling_automatically_key == 0:
235+
// automatic value overridden by user in settings
236+
// R.string.disabled_media_tunneling_automatically_key == -1: not set
237+
final boolean wasMediaTunnelingDisabledAutomatically =
238+
prefs.getInt(automaticTunnelingKey, -1) == 1
239+
&& prefs.getBoolean(tunnelingKey, false);
240+
if (wasMediaTunnelingDisabledAutomatically) {
241+
prefs.edit()
242+
.putInt(automaticTunnelingKey, -1)
243+
.putBoolean(tunnelingKey, false)
244+
.apply();
245+
NewPipeSettings.setMediaTunneling(context);
246+
}
247+
}
248+
249+
/**
250+
* Save import path and restart system.
251+
*
252+
* @param importDataUri The import path to save
253+
*/
254+
private void finishImport(final Uri importDataUri) {
255+
// save import path only on success
256+
saveLastImportExportDataUri(importDataUri);
257+
// restart app to properly load db
258+
NavigationHelper.restartApp(requireActivity());
259+
}
260+
261+
private Uri getImportExportDataUri() {
262+
final String path = defaultPreferences.getString(importExportDataPathKey, null);
263+
return isBlank(path) ? null : Uri.parse(path);
264+
}
265+
266+
private void saveLastImportExportDataUri(final Uri importExportDataUri) {
267+
final SharedPreferences.Editor editor = defaultPreferences.edit()
268+
.putString(importExportDataPathKey, importExportDataUri.toString());
269+
editor.apply();
270+
}
271+
}

0 commit comments

Comments
 (0)