Skip to content

Commit

Permalink
Export/import preferences to/from file
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyPavlenko committed Nov 17, 2021
1 parent edd155e commit 3fe4b30
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 19 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ext {
def abi = project.properties['ABI']
VERSION_CODE = 173
VERSION_CODE = 174
VERSION_NAME = "1.8.7"
SDK_MIN_VERSION = 23
SDK_TARGET_VERSION = 30
Expand Down
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ build_apk() {
abi='armeabi-v7a'
fi

./gradlew clean fermata:bundleRelease -PABI=$abi -PAPP_ID_SFX=$APP_ID_SFX
./gradlew clean fermata:bundleAutoRelease -PABI=$abi -PAPP_ID_SFX=$APP_ID_SFX
bundletool_universal ./fermata/build/outputs/bundle/autoRelease/fermata-*-release.aab -universal-$sfx -release
mv ./fermata/build/outputs/bundle/autoRelease/fermata-*.apk "$DEST_DIR"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import androidx.annotation.NonNull;

import me.aap.utils.function.BooleanSupplier;
import me.aap.utils.function.Supplier;

import me.aap.utils.pref.PreferenceStore;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package me.aap.fermata.ui.fragment;

import static android.content.Context.UI_MODE_SERVICE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
import static me.aap.fermata.util.Utils.isSafSupported;
import static me.aap.fermata.vfs.FermataVfsManager.GDRIVE_ID;
import static me.aap.fermata.vfs.FermataVfsManager.M3U_ID;
import static me.aap.fermata.vfs.FermataVfsManager.SFTP_ID;
import static me.aap.fermata.vfs.FermataVfsManager.SMB_ID;
import static me.aap.utils.async.Completed.completed;
import static me.aap.utils.function.ResultConsumer.Cancel.isCancellation;

import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
Expand Down Expand Up @@ -130,7 +127,7 @@ public void addFolder() {
b.setTitle(R.string.add_folder);
b.setSelectionHandler(this::addFolder);
b.addItem(R.id.vfs_file_system, R.string.vfs_file_system);
if (isContentSupported()) b.addItem(R.id.vfs_content, R.string.vfs_content);
if (isSafSupported(a)) b.addItem(R.id.vfs_content, R.string.vfs_content);
b.addItem(R.id.vfs_sftp, R.string.vfs_sftp);
b.addItem(R.id.vfs_smb, R.string.vfs_smb);
b.addItem(R.id.vfs_gdrive, R.string.vfs_gdrive);
Expand Down Expand Up @@ -164,16 +161,6 @@ private boolean addFolder(OverlayMenuItem item) {
return false;
}

private boolean isContentSupported() {
MainActivityDelegate a = getMainActivity();
if (a.isCarActivity()) return false;
UiModeManager umm = (UiModeManager) a.getContext().getSystemService(UI_MODE_SERVICE);
if (umm.getCurrentModeType() != UI_MODE_TYPE_NORMAL) return false;
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
Context ctx = getContext();
return (ctx != null) && (i.resolveActivity(ctx.getPackageManager()) != null);
}

private void addFolderIntent() {
try {
getMainActivity().startActivityForResult(() -> new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE))
Expand Down
141 changes: 141 additions & 0 deletions fermata/src/main/java/me/aap/fermata/ui/fragment/SettingsFragment.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package me.aap.fermata.ui.fragment;

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.N;
import static java.nio.charset.StandardCharsets.UTF_8;
import static me.aap.fermata.media.pref.MediaPrefs.MEDIA_ENG_EXO;
import static me.aap.fermata.media.pref.MediaPrefs.MEDIA_ENG_MP;
import static me.aap.fermata.media.pref.MediaPrefs.MEDIA_ENG_VLC;
Expand All @@ -14,6 +17,10 @@
import static me.aap.fermata.ui.activity.MainActivityPrefs.VOICE_CONTROl_M;
import static me.aap.utils.ui.UiUtils.ID_NULL;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
Expand All @@ -22,11 +29,20 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.documentfile.provider.DocumentFile;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import me.aap.fermata.BuildConfig;
import me.aap.fermata.FermataApplication;
Expand All @@ -41,17 +57,24 @@
import me.aap.fermata.ui.activity.MainActivityDelegate;
import me.aap.fermata.ui.activity.MainActivityListener;
import me.aap.fermata.ui.activity.MainActivityPrefs;
import me.aap.fermata.util.Utils;
import me.aap.utils.app.App;
import me.aap.utils.function.BooleanSupplier;
import me.aap.utils.function.Consumer;
import me.aap.utils.function.DoubleSupplier;
import me.aap.utils.function.IntSupplier;
import me.aap.utils.holder.BooleanHolder;
import me.aap.utils.log.Log;
import me.aap.utils.misc.ChangeableCondition;
import me.aap.utils.pref.PrefCondition;
import me.aap.utils.pref.PrefUtils;
import me.aap.utils.pref.PreferenceSet;
import me.aap.utils.pref.PreferenceStore;
import me.aap.utils.pref.PreferenceStore.Pref;
import me.aap.utils.pref.PreferenceView;
import me.aap.utils.pref.PreferenceViewAdapter;
import me.aap.utils.ui.UiUtils;
import me.aap.utils.ui.fragment.FilePickerFragment;

/**
* @author Andrey Pavlenko
Expand Down Expand Up @@ -499,6 +522,25 @@ private PreferenceViewAdapter createAdapter() {
addAddons(set);

sub1 = set.subSet(o -> o.title = R.string.other);
if (!a.isCarActivity() && Utils.isSafSupported(a)) {
BooleanHolder reqPerm = new BooleanHolder(true);
sub1.addButton(o -> {
o.title = R.string.export_prefs;
o.subtitle = R.string.export_prefs_sub;
o.onClick = this::exportPrefs;
});
sub1.addButton(o -> {
o.title = R.string.import_prefs;
o.subtitle = R.string.import_prefs_sub;
o.onClick = () -> {
if (reqPerm.value) {
reqPerm.value = false;
if (FilePickerFragment.requestManageAllFilesPerm(a.getContext())) return;
}
importPrefs();
};
});
}
sub1.addBooleanPref(o -> {
o.store = a.getPrefs();
o.pref = MainActivityPrefs.CHECK_UPDATES;
Expand Down Expand Up @@ -619,6 +661,105 @@ private void addAddons(PreferenceSet set) {
}
}

private void exportPrefs() {
MainActivityDelegate a = getMainActivity();
a.startActivityForResult(() -> new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE))
.onCompletion((d, err) -> {
Context ctx = a.getContext();

if (err != null) {
UiUtils.showAlert(ctx, ctx.getString(R.string.export_prefs_failed, err));
return;
}

if (d == null) return;
Uri uri = d.getData();
if (uri == null) return;

try {
DocumentFile dir = DocumentFile.fromTreeUri(ctx, uri);
if (dir == null) return;

File prefsDir = PrefUtils.getSharedPrefsFile(ctx, "fermata").getParentFile();
File[] files = (prefsDir == null) ? null
: prefsDir.listFiles(n -> !n.getName().equals("image-cache.xml"));

if ((files == null) || (files.length == 0)) {
UiUtils.showAlert(ctx, R.string.prefs_not_found);
return;
}

SimpleDateFormat fmt = new SimpleDateFormat("ddMMyy", Locale.getDefault());
String pattern = "Fermata_prefs_" + fmt.format(new Date());
String name = pattern + ".zip";
for (int i = 1; dir.findFile(name) != null; i++) name = pattern + '_' + i + ".zip";
DocumentFile f = dir.createFile("application/zip", name);

if (f == null) {
UiUtils.showAlert(ctx, ctx.getString(R.string.export_prefs_failed,
"Failed to create file"));
return;
}

List<String> names = new ArrayList<>(files.length);

for (File pf : files) {
String n = pf.getName();
if (n.endsWith(".xml")) names.add(n.substring(0, n.length() - 4));
}

try (OutputStream os = ctx.getContentResolver().openOutputStream(f.getUri());
ZipOutputStream zos = (SDK_INT >= N) ? new ZipOutputStream(os, UTF_8)
: new ZipOutputStream(os)) {
PrefUtils.exportSharedPrefs(ctx, names, zos);
}

UiUtils.showInfo(ctx, ctx.getString(R.string.export_prefs_ok, f.getName()));
} catch (Exception ex) {
Log.e(ex, "Failed to export preferences");
UiUtils.showAlert(ctx, ctx.getString(R.string.export_prefs_failed, ex));
}
});
}

private void importPrefs() {
MainActivityDelegate a = getMainActivity();
a.startActivityForResult(() -> new Intent(Intent.ACTION_OPEN_DOCUMENT)
.setType("application/zip"))
.onCompletion((d, err) -> {
Context ctx = a.getContext();

if (err != null) {
UiUtils.showAlert(ctx, ctx.getString(R.string.import_prefs_failed, err));
return;
}

if (d == null) return;
Uri uri = d.getData();
if (uri == null) return;

try {
DocumentFile f = DocumentFile.fromSingleUri(ctx, uri);
if (f == null) return;

try (InputStream is = ctx.getContentResolver().openInputStream(f.getUri());
ZipInputStream zis = (SDK_INT >= N) ? new ZipInputStream(is, UTF_8)
: new ZipInputStream(is)) {
PrefUtils.importSharedPrefs(ctx, zis);
}

UiUtils.showInfo(ctx, ctx.getString(R.string.import_prefs_ok)).thenRun(() -> {
App.get().getHandler().postDelayed(() -> System.exit(0), 1000);
if (ctx instanceof Activity) ((Activity) ctx).finishAffinity();
else System.exit(0);
});
} catch (Exception ex) {
Log.e(ex, "Failed to export preferences");
UiUtils.showAlert(ctx, ctx.getString(R.string.import_prefs_failed, ex));
}
});
}

private static final class AddonPrefsBuilder implements Consumer<PreferenceView.Opts>, AddonManager.Listener {
private final AddonManager amgr;
private final AddonInfo info;
Expand Down
15 changes: 15 additions & 0 deletions fermata/src/main/java/me/aap/fermata/util/Utils.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package me.aap.fermata.util;

import static android.content.Context.UI_MODE_SERVICE;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;

import android.app.UiModeManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;

import com.google.android.play.core.splitcompat.SplitCompat;

import me.aap.fermata.BuildConfig;
import me.aap.fermata.R;
import me.aap.fermata.ui.activity.MainActivityDelegate;
import me.aap.utils.io.FileUtils;
import me.aap.utils.net.http.HttpFileDownloader;
import me.aap.utils.ui.notif.HttpDownloadStatusListener;
Expand Down Expand Up @@ -51,4 +57,13 @@ public static Context dynCtx(Context ctx) {
if (!BuildConfig.AUTO) SplitCompat.install(ctx);
return ctx;
}

public static boolean isSafSupported(MainActivityDelegate a) {
if (a.isCarActivity()) return false;
Context ctx = a.getContext();
UiModeManager umm = (UiModeManager) a.getContext().getSystemService(UI_MODE_SERVICE);
if (umm.getCurrentModeType() != UI_MODE_TYPE_NORMAL) return false;
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
return i.resolveActivity(ctx.getPackageManager()) != null;
}
}
9 changes: 9 additions & 0 deletions fermata/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@
<string name="check_updates">Проверять наличие обновлений при старте</string>
<string name="update">Обновление</string>
<string name="update_question">Доступна новая версия %1$s.\nХотите обновить приложение?</string>
<string name="export_prefs">Экспортировать настройки в файл</string>
<string name="export_prefs_sub">Экспортировать настройки, папки, плейлисты и т.д. в файл</string>
<string name="export_prefs_ok">Настройки сохранены в файле %1$s.</string>
<string name="export_prefs_failed">Не удалось экспортировать настройки: %1$s</string>
<string name="import_prefs">Импортировать настройки из файла</string>
<string name="import_prefs_sub">Импортировать, ранее экспортированные, настройки из файла</string>
<string name="import_prefs_ok">Настройки успешно импортированы.\nПожалуйста, перезапустите приложение.</string>
<string name="import_prefs_failed">Не удалось импортировать настройки: %1$s</string>
<string name="prefs_not_found">Настройки не найдены</string>

<string name="about">О приложении</string>
<string name="about_html"><![CDATA[<H1 align="center">Fermata Media Player</H1>
Expand Down
9 changes: 9 additions & 0 deletions fermata/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,15 @@
<string name="check_updates">Check for updates on startup</string>
<string name="update">Update</string>
<string name="update_question">New version %1$s is available.\nWould you like to update the app?</string>
<string name="export_prefs">Export preferences to file</string>
<string name="export_prefs_sub">Export settings, folders, playlists etc. to file</string>
<string name="export_prefs_ok">Preferences have been saved in file %1$s.</string>
<string name="export_prefs_failed">Failed to export preferences: %1$s</string>
<string name="import_prefs">Import preferences from file</string>
<string name="import_prefs_sub">Import, previously exported, preferences from file</string>
<string name="import_prefs_ok">Preferences have been successfully imported.\nPlease, restart the application.</string>
<string name="import_prefs_failed">Failed to import preferences: %1$s</string>
<string name="prefs_not_found">No preferences found</string>

<string name="about">About</string>
<string name="about_html"><![CDATA[<H1 align="center">Fermata Media Player</H1>
Expand Down

0 comments on commit 3fe4b30

Please sign in to comment.