mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-30 00:01:19 +00:00
Add import/export for app settings (SharedPreferences) (#61)
* Add export of SharedPreferences app settings Add import/export to drawer * Add import for app settings (SharedPreferences) Move last_sync_id pref to Constants.java * Add drawer icon for import / export feature * Start or stay stopped according to run conditions after import * Close SettingsActivity after sharedPref import
This commit is contained in:
parent
3bb227379c
commit
38db4d9c32
14 changed files with 219 additions and 44 deletions
|
@ -122,8 +122,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
private CheckBoxPreference mRestartOnWakeup;
|
private CheckBoxPreference mRestartOnWakeup;
|
||||||
private CheckBoxPreference mUrAccepted;
|
private CheckBoxPreference mUrAccepted;
|
||||||
|
|
||||||
private Preference mCategoryBackup;
|
|
||||||
|
|
||||||
/* Experimental options */
|
/* Experimental options */
|
||||||
private CheckBoxPreference mUseRoot;
|
private CheckBoxPreference mUseRoot;
|
||||||
private CheckBoxPreference mUseWakelock;
|
private CheckBoxPreference mUseWakelock;
|
||||||
|
@ -213,7 +211,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
mRestartOnWakeup = (CheckBoxPreference) findPreference("restartOnWakeup");
|
mRestartOnWakeup = (CheckBoxPreference) findPreference("restartOnWakeup");
|
||||||
mUrAccepted = (CheckBoxPreference) findPreference("urAccepted");
|
mUrAccepted = (CheckBoxPreference) findPreference("urAccepted");
|
||||||
|
|
||||||
mCategoryBackup = findPreference("category_backup");
|
|
||||||
Preference exportConfig = findPreference("export_config");
|
Preference exportConfig = findPreference("export_config");
|
||||||
Preference importConfig = findPreference("import_config");
|
Preference importConfig = findPreference("import_config");
|
||||||
|
|
||||||
|
@ -319,7 +316,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
mApi.isConfigLoaded() &&
|
mApi.isConfigLoaded() &&
|
||||||
(currentState == SyncthingService.State.ACTIVE);
|
(currentState == SyncthingService.State.ACTIVE);
|
||||||
mCategorySyncthingOptions.setEnabled(isSyncthingRunning);
|
mCategorySyncthingOptions.setEnabled(isSyncthingRunning);
|
||||||
mCategoryBackup.setEnabled(isSyncthingRunning);
|
|
||||||
|
|
||||||
if (!isSyncthingRunning)
|
if (!isSyncthingRunning)
|
||||||
return;
|
return;
|
||||||
|
@ -539,10 +535,15 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setMessage(R.string.dialog_confirm_export)
|
.setMessage(R.string.dialog_confirm_export)
|
||||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||||
mSyncthingService.exportConfig();
|
if (mSyncthingService.exportConfig()) {
|
||||||
Toast.makeText(getActivity(),
|
Toast.makeText(getActivity(),
|
||||||
getString(R.string.config_export_successful,
|
getString(R.string.config_export_successful,
|
||||||
Constants.EXPORT_PATH), Toast.LENGTH_LONG).show();
|
Constants.EXPORT_PATH), Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getActivity(),
|
||||||
|
getString(R.string.config_export_failed),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(android.R.string.no, null)
|
.setNegativeButton(android.R.string.no, null)
|
||||||
.show();
|
.show();
|
||||||
|
@ -551,17 +552,21 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setMessage(R.string.dialog_confirm_import)
|
.setMessage(R.string.dialog_confirm_import)
|
||||||
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
|
||||||
if (mSyncthingService.importConfig()) {
|
// Shutdown syncthing, import config, if run conditions applied restart syncthing.
|
||||||
Toast.makeText(getActivity(),
|
if (!mSyncthingService.importConfig()) {
|
||||||
getString(R.string.config_imported_successful),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
// No need to restart, as we shutdown to import the config, and
|
|
||||||
// then have to start Syncthing again.
|
|
||||||
} else {
|
|
||||||
Toast.makeText(getActivity(),
|
Toast.makeText(getActivity(),
|
||||||
getString(R.string.config_import_failed,
|
getString(R.string.config_import_failed,
|
||||||
Constants.EXPORT_PATH), Toast.LENGTH_LONG).show();
|
Constants.EXPORT_PATH), Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Toast.makeText(getActivity(),
|
||||||
|
getString(R.string.config_imported_successful),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
// We don't have to send the config via REST on leaving activity.
|
||||||
|
mPendingConfig = false;
|
||||||
|
// We have to evaluate run conditions, they may have changed by the imported prefs.
|
||||||
|
mPendingRunConditions = true;
|
||||||
|
getActivity().finish();
|
||||||
})
|
})
|
||||||
.setNegativeButton(android.R.string.no, null)
|
.setNegativeButton(android.R.string.no, null)
|
||||||
.show();
|
.show();
|
||||||
|
|
|
@ -39,6 +39,7 @@ public class DrawerFragment extends Fragment implements SyncthingService.OnServi
|
||||||
private TextView mVersion = null;
|
private TextView mVersion = null;
|
||||||
private TextView mDrawerActionShowQrCode;
|
private TextView mDrawerActionShowQrCode;
|
||||||
private TextView mDrawerActionWebGui;
|
private TextView mDrawerActionWebGui;
|
||||||
|
private TextView mDrawerActionImportExport;
|
||||||
private TextView mDrawerActionRestart;
|
private TextView mDrawerActionRestart;
|
||||||
private TextView mDrawerActionSettings;
|
private TextView mDrawerActionSettings;
|
||||||
private TextView mExitButton;
|
private TextView mExitButton;
|
||||||
|
@ -80,6 +81,7 @@ public class DrawerFragment extends Fragment implements SyncthingService.OnServi
|
||||||
mVersion = view.findViewById(R.id.version);
|
mVersion = view.findViewById(R.id.version);
|
||||||
mDrawerActionShowQrCode = view.findViewById(R.id.drawerActionShowQrCode);
|
mDrawerActionShowQrCode = view.findViewById(R.id.drawerActionShowQrCode);
|
||||||
mDrawerActionWebGui = view.findViewById(R.id.drawerActionWebGui);
|
mDrawerActionWebGui = view.findViewById(R.id.drawerActionWebGui);
|
||||||
|
mDrawerActionImportExport = view.findViewById(R.id.drawerActionImportExport);
|
||||||
mDrawerActionRestart = view.findViewById(R.id.drawerActionRestart);
|
mDrawerActionRestart = view.findViewById(R.id.drawerActionRestart);
|
||||||
mDrawerActionSettings = view.findViewById(R.id.drawerActionSettings);
|
mDrawerActionSettings = view.findViewById(R.id.drawerActionSettings);
|
||||||
mExitButton = view.findViewById(R.id.drawerActionExit);
|
mExitButton = view.findViewById(R.id.drawerActionExit);
|
||||||
|
@ -87,6 +89,7 @@ public class DrawerFragment extends Fragment implements SyncthingService.OnServi
|
||||||
// Add listeners to buttons.
|
// Add listeners to buttons.
|
||||||
mDrawerActionShowQrCode.setOnClickListener(this);
|
mDrawerActionShowQrCode.setOnClickListener(this);
|
||||||
mDrawerActionWebGui.setOnClickListener(this);
|
mDrawerActionWebGui.setOnClickListener(this);
|
||||||
|
mDrawerActionImportExport.setOnClickListener(this);
|
||||||
mDrawerActionRestart.setOnClickListener(this);
|
mDrawerActionRestart.setOnClickListener(this);
|
||||||
mDrawerActionSettings.setOnClickListener(this);
|
mDrawerActionSettings.setOnClickListener(this);
|
||||||
mExitButton.setOnClickListener(this);
|
mExitButton.setOnClickListener(this);
|
||||||
|
@ -155,6 +158,7 @@ public class DrawerFragment extends Fragment implements SyncthingService.OnServi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
Intent intent;
|
||||||
switch (v.getId()) {
|
switch (v.getId()) {
|
||||||
case R.id.drawerActionWebGui:
|
case R.id.drawerActionWebGui:
|
||||||
startActivity(new Intent(mActivity, WebGuiActivity.class));
|
startActivity(new Intent(mActivity, WebGuiActivity.class));
|
||||||
|
@ -164,6 +168,12 @@ public class DrawerFragment extends Fragment implements SyncthingService.OnServi
|
||||||
startActivity(new Intent(mActivity, SettingsActivity.class));
|
startActivity(new Intent(mActivity, SettingsActivity.class));
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
break;
|
||||||
|
case R.id.drawerActionImportExport:
|
||||||
|
intent = new Intent(mActivity, SettingsActivity.class);
|
||||||
|
intent.putExtra(SettingsActivity.EXTRA_OPEN_SUB_PREF_SCREEN, "category_import_export");
|
||||||
|
startActivity(intent);
|
||||||
|
mActivity.closeDrawer();
|
||||||
|
break;
|
||||||
case R.id.drawerActionRestart:
|
case R.id.drawerActionRestart:
|
||||||
mActivity.showRestartDialog();
|
mActivity.showRestartDialog();
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class PollWebGuiAvailableTask extends ApiRequest {
|
||||||
* Interval in ms, at which connections to the web gui are performed on first start
|
* Interval in ms, at which connections to the web gui are performed on first start
|
||||||
* to find out if it's online.
|
* to find out if it's online.
|
||||||
*/
|
*/
|
||||||
private static final long WEB_GUI_POLL_INTERVAL = 100;
|
private static final long WEB_GUI_POLL_INTERVAL = 150;
|
||||||
|
|
||||||
private final Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
public class Constants {
|
public class Constants {
|
||||||
|
|
||||||
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so";
|
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so";
|
||||||
public static final String PREF_LAST_BINARY_VERSION = "lastBinaryVersion";
|
|
||||||
|
|
||||||
// Preferences - Run conditions
|
// Preferences - Run conditions
|
||||||
public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background";
|
public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background";
|
||||||
|
@ -33,6 +32,16 @@ public class Constants {
|
||||||
public static final String PREF_SOCKS_PROXY_ADDRESS = "socks_proxy_address";
|
public static final String PREF_SOCKS_PROXY_ADDRESS = "socks_proxy_address";
|
||||||
public static final String PREF_HTTP_PROXY_ADDRESS = "http_proxy_address";
|
public static final String PREF_HTTP_PROXY_ADDRESS = "http_proxy_address";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached information which is not available on SettingsActivity.
|
||||||
|
*/
|
||||||
|
public static final String PREF_LAST_BINARY_VERSION = "lastBinaryVersion";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link EventProcessor}
|
||||||
|
*/
|
||||||
|
public static final String PREF_EVENT_PROCESSOR_LAST_SYNC_ID = "last_sync_id";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available options cache for preference {@link app_settings#debug_facilities_enabled}
|
* Available options cache for preference {@link app_settings#debug_facilities_enabled}
|
||||||
* Read via REST API call in {@link RestApi#updateDebugFacilitiesCache} after first successful binary startup.
|
* Read via REST API call in {@link RestApi#updateDebugFacilitiesCache} after first successful binary startup.
|
||||||
|
@ -106,6 +115,11 @@ public class Constants {
|
||||||
return new File(context.getFilesDir(), "https-cert.pem");
|
return new File(context.getFilesDir(), "https-cert.pem");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the export file holding the SharedPreferences backup.
|
||||||
|
*/
|
||||||
|
static final String SHARED_PREFS_EXPORT_FILE = "sharedpreferences.dat";
|
||||||
|
|
||||||
static File getSyncthingBinary(Context context) {
|
static File getSyncthingBinary(Context context) {
|
||||||
return new File(context.getApplicationInfo().nativeLibraryDir, FILENAME_SYNCTHING_BINARY);
|
return new File(context.getApplicationInfo().nativeLibraryDir, FILENAME_SYNCTHING_BINARY);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import javax.inject.Inject;
|
||||||
public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener {
|
public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener {
|
||||||
|
|
||||||
private static final String TAG = "EventProcessor";
|
private static final String TAG = "EventProcessor";
|
||||||
private static final String PREF_LAST_SYNC_ID = "last_sync_id";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum interval in seconds at which the events are polled from syncthing and processed.
|
* Minimum interval in seconds at which the events are polled from syncthing and processed.
|
||||||
|
@ -68,7 +67,7 @@ public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener
|
||||||
public void run() {
|
public void run() {
|
||||||
// Restore the last event id if the event processor may have been restarted.
|
// Restore the last event id if the event processor may have been restarted.
|
||||||
if (mLastEventId == 0) {
|
if (mLastEventId == 0) {
|
||||||
mLastEventId = mPreferences.getLong(PREF_LAST_SYNC_ID, 0);
|
mLastEventId = mPreferences.getLong(Constants.PREF_EVENT_PROCESSOR_LAST_SYNC_ID, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check if the event number ran backwards.
|
// First check if the event number ran backwards.
|
||||||
|
@ -178,7 +177,7 @@ public class EventProcessor implements Runnable, RestApi.OnReceiveEventListener
|
||||||
mLastEventId = id;
|
mLastEventId = id;
|
||||||
|
|
||||||
// Store the last EventId in case we get killed
|
// Store the last EventId in case we get killed
|
||||||
mPreferences.edit().putLong(PREF_LAST_SYNC_ID, mLastEventId).apply();
|
mPreferences.edit().putLong(Constants.PREF_EVENT_PROCESSOR_LAST_SYNC_ID, mLastEventId).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mMainThreadHandler) {
|
synchronized (mMainThreadHandler) {
|
||||||
|
|
|
@ -23,12 +23,18 @@ import com.nutomic.syncthingandroid.model.Folder;
|
||||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -626,7 +632,11 @@ public class SyncthingService extends Service {
|
||||||
/**
|
/**
|
||||||
* Exports the local config and keys to {@link Constants#EXPORT_PATH}.
|
* Exports the local config and keys to {@link Constants#EXPORT_PATH}.
|
||||||
*/
|
*/
|
||||||
public void exportConfig() {
|
public boolean exportConfig() {
|
||||||
|
Boolean failSuccess = true;
|
||||||
|
Log.v(TAG, "exportConfig BEGIN");
|
||||||
|
|
||||||
|
// Copy config, privateKey and/or publicKey to export path.
|
||||||
Constants.EXPORT_PATH.mkdirs();
|
Constants.EXPORT_PATH.mkdirs();
|
||||||
try {
|
try {
|
||||||
Files.copy(Constants.getConfigFile(this),
|
Files.copy(Constants.getConfigFile(this),
|
||||||
|
@ -637,7 +647,39 @@ public class SyncthingService extends Service {
|
||||||
new File(Constants.EXPORT_PATH, Constants.PUBLIC_KEY_FILE));
|
new File(Constants.EXPORT_PATH, Constants.PUBLIC_KEY_FILE));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Failed to export config", e);
|
Log.w(TAG, "Failed to export config", e);
|
||||||
|
failSuccess = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export SharedPreferences.
|
||||||
|
File file;
|
||||||
|
FileOutputStream fileOutputStream = null;
|
||||||
|
ObjectOutputStream objectOutputStream = null;
|
||||||
|
try {
|
||||||
|
file = new File(Constants.EXPORT_PATH, Constants.SHARED_PREFS_EXPORT_FILE);
|
||||||
|
fileOutputStream = new FileOutputStream(file);
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
objectOutputStream = new ObjectOutputStream(fileOutputStream);
|
||||||
|
objectOutputStream.writeObject(mPreferences.getAll());
|
||||||
|
objectOutputStream.flush();
|
||||||
|
fileOutputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "exportConfig: Failed to export SharedPreferences #1", e);
|
||||||
|
failSuccess = false;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (objectOutputStream != null) {
|
||||||
|
objectOutputStream.close();
|
||||||
|
}
|
||||||
|
if (fileOutputStream != null) {
|
||||||
|
fileOutputStream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "exportConfig: Failed to export SharedPreferences #2", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -646,21 +688,116 @@ public class SyncthingService extends Service {
|
||||||
* @return True if the import was successful, false otherwise (eg if files aren't found).
|
* @return True if the import was successful, false otherwise (eg if files aren't found).
|
||||||
*/
|
*/
|
||||||
public boolean importConfig() {
|
public boolean importConfig() {
|
||||||
|
Boolean failSuccess = true;
|
||||||
|
Log.v(TAG, "importConfig BEGIN");
|
||||||
|
|
||||||
|
// Shutdown synchronously.
|
||||||
|
shutdown(State.DISABLED, () -> {});
|
||||||
|
|
||||||
|
// Import config, privateKey and/or publicKey.
|
||||||
|
try {
|
||||||
File config = new File(Constants.EXPORT_PATH, Constants.CONFIG_FILE);
|
File config = new File(Constants.EXPORT_PATH, Constants.CONFIG_FILE);
|
||||||
File privateKey = new File(Constants.EXPORT_PATH, Constants.PRIVATE_KEY_FILE);
|
File privateKey = new File(Constants.EXPORT_PATH, Constants.PRIVATE_KEY_FILE);
|
||||||
File publicKey = new File(Constants.EXPORT_PATH, Constants.PUBLIC_KEY_FILE);
|
File publicKey = new File(Constants.EXPORT_PATH, Constants.PUBLIC_KEY_FILE);
|
||||||
if (!config.exists() || !privateKey.exists() || !publicKey.exists())
|
|
||||||
return false;
|
// Check if necessary files for import are available.
|
||||||
shutdown(State.INIT, () -> {
|
if (config.exists() && privateKey.exists() && publicKey.exists()) {
|
||||||
try {
|
|
||||||
Files.copy(config, Constants.getConfigFile(this));
|
Files.copy(config, Constants.getConfigFile(this));
|
||||||
Files.copy(privateKey, Constants.getPrivateKeyFile(this));
|
Files.copy(privateKey, Constants.getPrivateKeyFile(this));
|
||||||
Files.copy(publicKey, Constants.getPublicKeyFile(this));
|
Files.copy(publicKey, Constants.getPublicKeyFile(this));
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "importConfig: config, privateKey and/or publicKey files missing");
|
||||||
|
failSuccess = false;
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Failed to import config", e);
|
Log.w(TAG, "importConfig: Failed to import config", e);
|
||||||
|
failSuccess = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import SharedPreferences.
|
||||||
|
File file;
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
ObjectInputStream objectInputStream = null;
|
||||||
|
Map<String, Object> sharedPrefsMap = null;
|
||||||
|
try {
|
||||||
|
file = new File(Constants.EXPORT_PATH, Constants.SHARED_PREFS_EXPORT_FILE);
|
||||||
|
if (file.exists()) {
|
||||||
|
// Read, deserialize shared preferences.
|
||||||
|
fileInputStream = new FileInputStream(file);
|
||||||
|
objectInputStream = new ObjectInputStream(fileInputStream);
|
||||||
|
sharedPrefsMap = (Map) objectInputStream.readObject();
|
||||||
|
|
||||||
|
// Prepare a SharedPreferences commit.
|
||||||
|
SharedPreferences.Editor editor = mPreferences.edit();
|
||||||
|
editor.clear();
|
||||||
|
for (Map.Entry<String, Object> e : sharedPrefsMap.entrySet()) {
|
||||||
|
String prefKey = e.getKey();
|
||||||
|
switch (prefKey) {
|
||||||
|
// Preferences that are no longer used and left-overs from previous versions of the app.
|
||||||
|
case "first_start":
|
||||||
|
case "notify_crashes":
|
||||||
|
// Cached information which is not available on SettingsActivity.
|
||||||
|
case Constants.PREF_DEBUG_FACILITIES_AVAILABLE:
|
||||||
|
case Constants.PREF_EVENT_PROCESSOR_LAST_SYNC_ID:
|
||||||
|
case Constants.PREF_LAST_BINARY_VERSION:
|
||||||
|
Log.v(TAG, "importConfig: Ignoring pref \"" + prefKey + "\".");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "importConfig: Adding pref \"" + prefKey + "\" to commit ...");
|
||||||
|
|
||||||
|
// The editor only provides typed setters.
|
||||||
|
if (e.getValue() instanceof Boolean) {
|
||||||
|
editor.putBoolean(prefKey, (Boolean) e.getValue());
|
||||||
|
} else if (e.getValue() instanceof String) {
|
||||||
|
editor.putString(prefKey, (String) e.getValue());
|
||||||
|
} else if (e.getValue() instanceof Integer) {
|
||||||
|
editor.putInt(prefKey, (int) e.getValue());
|
||||||
|
} else if (e.getValue() instanceof Float) {
|
||||||
|
editor.putFloat(prefKey, (float) e.getValue());
|
||||||
|
} else if (e.getValue() instanceof Long) {
|
||||||
|
editor.putLong(prefKey, (Long) e.getValue());
|
||||||
|
} else if (e.getValue() instanceof Set) {
|
||||||
|
editor.putStringSet(prefKey, (Set<String>) e.getValue());
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "importConfig: SharedPref type " + e.getValue().getClass().getName() + " is unknown");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If all shared preferences have been added to the commit successfully,
|
||||||
|
* apply the commit.
|
||||||
|
*/
|
||||||
|
failSuccess = failSuccess && editor.commit();
|
||||||
|
} else {
|
||||||
|
// File not found.
|
||||||
|
Log.w(TAG, "importConfig: SharedPreferences file missing. This is expected if you migrate from the official app to the forked app.");
|
||||||
|
/**
|
||||||
|
* Don't fail as the file might be expectedly missing when users migrate
|
||||||
|
* to the forked app.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
|
Log.e(TAG, "importConfig: Failed to import SharedPreferences #1", e);
|
||||||
|
failSuccess = false;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (objectInputStream != null) {
|
||||||
|
objectInputStream.close();
|
||||||
|
}
|
||||||
|
if (fileInputStream != null) {
|
||||||
|
fileInputStream.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "importConfig: Failed to import SharedPreferences #2", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start syncthing after successful import if run conditions apply.
|
||||||
|
if (mLastDeterminedShouldRun) {
|
||||||
launchStartupTask(SyncthingRunnable.Command.main);
|
launchStartupTask(SyncthingRunnable.Command.main);
|
||||||
});
|
}
|
||||||
return true;
|
return failSuccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_import_export_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_import_export_black_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 B |
BIN
app/src/main/res/drawable-mdpi/ic_import_export_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_import_export_black_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 B |
BIN
app/src/main/res/drawable-xhdpi/ic_import_export_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_import_export_black_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_import_export_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_import_export_black_24dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 215 B |
Binary file not shown.
After Width: | Height: | Size: 218 B |
|
@ -114,6 +114,17 @@
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true" />
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/drawerActionImportExport"
|
||||||
|
style="@style/Widget.Syncthing.TextView.Label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:drawableLeft="@drawable/ic_import_export_black_24dp"
|
||||||
|
android:drawableStart="@drawable/ic_import_export_black_24dp"
|
||||||
|
android:text="@string/category_backup"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/drawerActionRestart"
|
android:id="@+id/drawerActionRestart"
|
||||||
style="@style/Widget.Syncthing.TextView.Label"
|
style="@style/Widget.Syncthing.TextView.Label"
|
||||||
|
|
|
@ -294,7 +294,7 @@ Please report any problems you encounter via Github.</string>
|
||||||
<string name="category_run_conditions">Run Conditions</string>
|
<string name="category_run_conditions">Run Conditions</string>
|
||||||
<string name="category_behaviour">Behaviour</string>
|
<string name="category_behaviour">Behaviour</string>
|
||||||
<string name="category_syncthing_options">Syncthing Options</string>
|
<string name="category_syncthing_options">Syncthing Options</string>
|
||||||
<string name="category_backup">Backup</string>
|
<string name="category_backup">Import and Export</string>
|
||||||
<string name="category_debug">Debug</string>
|
<string name="category_debug">Debug</string>
|
||||||
<string name="category_experimental">Experimental</string>
|
<string name="category_experimental">Experimental</string>
|
||||||
|
|
||||||
|
@ -466,15 +466,14 @@ Please report any problems you encounter via Github.</string>
|
||||||
<!-- Dialog shown before config import -->
|
<!-- Dialog shown before config import -->
|
||||||
<string name="dialog_confirm_import">Do you really want to import a new configuration? Existing files will be overwritten.</string>
|
<string name="dialog_confirm_import">Do you really want to import a new configuration? Existing files will be overwritten.</string>
|
||||||
|
|
||||||
<!-- Toast shown after config was successfully exported -->
|
<!-- Toast shown after config was exported -->
|
||||||
<string name="config_export_successful">Config was exported to %1$s</string>
|
<string name="config_export_successful">Config was exported to %1$s</string>
|
||||||
|
<string name="config_export_failed">Config export failed, check logcat output.</string>
|
||||||
|
|
||||||
<string name="import_config">Import Configuration</string>
|
<string name="import_config">Import Configuration</string>
|
||||||
|
|
||||||
<!-- Toast shown after config was successfully imported -->
|
<!-- Toast shown after config was imported -->
|
||||||
<string name="config_imported_successful">Config was imported</string>
|
<string name="config_imported_successful">Config was imported</string>
|
||||||
|
|
||||||
<!-- Toast shown after config was successfully imported -->
|
|
||||||
<string name="config_import_failed">Config import failed, make sure files are in %1$s</string>
|
<string name="config_import_failed">Config import failed, make sure files are in %1$s</string>
|
||||||
|
|
||||||
<!-- Title for the preference to set STTRACE parameters -->
|
<!-- Title for the preference to set STTRACE parameters -->
|
||||||
|
|
|
@ -193,7 +193,7 @@
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="category_backup"
|
android:key="category_import_export"
|
||||||
android:title="@string/category_backup">
|
android:title="@string/category_backup">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
|
Loading…
Reference in a new issue