Added config import/export (fixes #143).

This commit is contained in:
Felix Ableitner 2015-03-10 21:47:32 +01:00
parent 97a324b4b5
commit 527d40e670
5 changed files with 154 additions and 6 deletions

View File

@ -7,6 +7,7 @@ import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.Pair;
import com.nutomic.syncthingandroid.syncthing.DeviceStateHolder;
@ -14,10 +15,12 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
import com.nutomic.syncthingandroid.syncthing.SyncthingServiceBinder;
import com.nutomic.syncthingandroid.test.MockContext;
import com.nutomic.syncthingandroid.test.Util;
import com.nutomic.syncthingandroid.util.ConfigXml;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -66,7 +69,7 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
latch.countDown();
}
});
latch.await(1, TimeUnit.SECONDS);
latch.await(5, TimeUnit.SECONDS);
assertNotNull(getService().getApi());
assertNotNull(getService().getWebGuiUrl());
}
@ -125,4 +128,33 @@ public class SyncthingServiceTest extends ServiceTestCase<SyncthingService> {
}
public void testImportExportConfig() {
setContext(mContext);
SyncthingServiceBinder binder = (SyncthingServiceBinder)
bindService(new Intent(getContext(), SyncthingService.class));
SyncthingService service = binder.getService();
File config = new File(mContext.getFilesDir(), ConfigXml.CONFIG_FILE);
File privateKey = new File(mContext.getFilesDir(), SyncthingService.PRIVATE_KEY_FILE);
File publicKey = new File(mContext.getFilesDir(), SyncthingService.PUBLIC_KEY_FILE);
try {
config.createNewFile();
privateKey.createNewFile();
publicKey.createNewFile();
} catch (IOException e) {
fail();
}
service.exportConfig();
config.delete();
privateKey.delete();
publicKey.delete();
service.importConfig();
assertTrue(config.exists());
assertTrue(privateKey.exists());
assertTrue(publicKey.exists());
}
}

View File

@ -12,6 +12,7 @@ import android.support.v4.preference.PreferenceFragment;
import android.text.InputType;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
@ -20,17 +21,17 @@ import com.nutomic.syncthingandroid.syncthing.SyncthingService;
public class SettingsFragment extends PreferenceFragment
implements SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener {
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener {
private static final String TAG = "SettingsFragment";
private static final String SYNCTHING_OPTIONS_KEY = "syncthing_options";
private static final String SYNCTHING_GUI_KEY = "syncthing_gui";
private static final String DEVICE_NAME_KEY = "DeviceName";
private static final String USAGE_REPORT_ACCEPTED = "URAccepted";
private static final String EXPORT_CONFIG = "export_config";
private static final String IMPORT_CONFIG = "import_config";
private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
@ -134,6 +135,8 @@ public class SettingsFragment extends PreferenceFragment
mAlwaysRunInBackground.setOnPreferenceChangeListener(this);
mSyncOnlyCharging.setOnPreferenceChangeListener(this);
mSyncOnlyWifi.setOnPreferenceChangeListener(this);
screen.findPreference(EXPORT_CONFIG).setOnPreferenceClickListener(this);
screen.findPreference(IMPORT_CONFIG).setOnPreferenceClickListener(this);
// Force summary update and wifi/charging preferences enable/disable.
onPreferenceChange(mAlwaysRunInBackground, mAlwaysRunInBackground.isChecked());
Preference sttrace = findPreference("sttrace");
@ -224,4 +227,27 @@ public class SettingsFragment extends PreferenceFragment
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
switch (preference.getKey()) {
case EXPORT_CONFIG:
mSyncthingService.exportConfig();
Toast.makeText(getActivity(), getString(R.string.config_export_successful,
SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show();
return true;
case IMPORT_CONFIG:
if (mSyncthingService.importConfig()) {
Toast.makeText(getActivity(), getString(R.string.config_imported_successful),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), getString(R.string.config_import_failed,
SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show();
}
return true;
default:
return false;
}
}
}

View File

@ -12,6 +12,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
@ -25,8 +26,15 @@ import com.nutomic.syncthingandroid.util.ConfigXml;
import com.nutomic.syncthingandroid.util.FolderObserver;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.nio.channels.FileChannel;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
@ -56,7 +64,12 @@ public class SyncthingService extends Service {
/**
* Name of the private key file in the data directory.
*/
private static final String PRIVATE_KEY_FILE = "key.pem";
public static final String PRIVATE_KEY_FILE = "key.pem";
/**
* Directory where config is exported to and imported from.
*/
public static final File EXPORT_PATH = Environment.getExternalStorageDirectory();
/**
* Path to the native, integrated syncthing binary, relative to the data folder
@ -432,4 +445,60 @@ public class SyncthingService extends Service {
return sp.getBoolean(PREF_ALWAYS_RUN_IN_BACKGROUND, false);
}
/**
* Exports the local config and keys to {@link #EXPORT_PATH}.
*/
public void exportConfig() {
copyFile(new File(getFilesDir(), ConfigXml.CONFIG_FILE),
new File(EXPORT_PATH, ConfigXml.CONFIG_FILE));
copyFile(new File(getFilesDir(), PRIVATE_KEY_FILE),
new File(EXPORT_PATH, PRIVATE_KEY_FILE));
copyFile(new File(getFilesDir(), PUBLIC_KEY_FILE),
new File(EXPORT_PATH, PUBLIC_KEY_FILE));
}
/**
* Imports config and keys from {@link #EXPORT_PATH}.
*
* @return True if the import was successful, false otherwise (eg if files aren't found).
*/
public boolean importConfig() {
File config = new File(EXPORT_PATH, ConfigXml.CONFIG_FILE);
File privateKey = new File(EXPORT_PATH, PRIVATE_KEY_FILE);
File publicKey = new File(EXPORT_PATH, PUBLIC_KEY_FILE);
if (!config.exists() || !privateKey.exists() || !publicKey.exists())
return false;
copyFile(config, new File(getFilesDir(), ConfigXml.CONFIG_FILE));
copyFile(privateKey, new File(getFilesDir(), PRIVATE_KEY_FILE));
copyFile(publicKey, new File(getFilesDir(), PUBLIC_KEY_FILE));
startService(new Intent(this, SyncthingService.class)
.setAction(SyncthingService.ACTION_RESTART));
return true;
}
/**
* Copies files between different storage devices.
*/
private void copyFile(File source, File dest) {
FileChannel is = null;
FileChannel os = null;
try {
is = new FileInputStream(source).getChannel();
os = new FileOutputStream(dest).getChannel();
is.transferTo(0, is.size(), os);
} catch (IOException e) {
Log.w(TAG, "Failed to copy file", e);
} finally {
try {
is.close();
os.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close stream", e);
}
}
}
}

View File

@ -250,6 +250,19 @@ Please report any problems you encounter via Github.</string>
<string name="gui_https_enabled">Use HTTPS for GUI</string>
<string name="export_config">Export Configuration</string>
<!-- Toast shown after config was successfully exported -->
<string name="config_export_successful">Config was exported to %1$s</string>
<string name="import_config">Import Configuration</string>
<!-- Toast shown after config was successfully imported -->
<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>
<!-- Title for the preference to set STTRACE parameters -->
<string name="sttrace_title">Debug Options</string>

View File

@ -92,6 +92,14 @@
</PreferenceScreen>
<Preference
android:key="export_config"
android:title="@string/export_config" />
<Preference
android:key="import_config"
android:title="@string/import_config" />
<EditTextPreference
android:key="sttrace"
android:title="@string/sttrace_title"