Streamline SyncthingRunnable

Conflicts:
	src/androidTest/java/com/nutomic/syncthingandroid/test/syncthing/RestApiTest.java
	src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java
	src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingRunnable.java
	src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java
	src/main/res/values/strings.xml
This commit is contained in:
Lode Hoste 2015-03-22 22:30:49 +01:00
parent 09331780b3
commit 5d5cf0b903
10 changed files with 114 additions and 23 deletions

View File

@ -30,8 +30,7 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase {
} }
public void testPolling() throws InterruptedException { public void testPolling() throws InterruptedException {
new SyncthingRunnable(new MockContext(null), mSyncthing = new SyncthingRunnable(new MockContext(null), SyncthingRunnable.Command.main);
getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME);
String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE; String httpsCertPath = getContext().getFilesDir() + "/" + SyncthingService.HTTPS_CERT_FILE;

View File

@ -23,8 +23,7 @@ public class RestApiTest extends AndroidTestCase {
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
new SyncthingRunnable(new MockContext(null), mSyncthing = new SyncthingRunnable(new MockContext(null), SyncthingRunnable.Command.main);
getContext().getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME);
ConfigXml config = new ConfigXml(new MockContext(getContext())); ConfigXml config = new ConfigXml(new MockContext(getContext()));
config.changeDefaultFolder(); config.changeDefaultFolder();

View File

@ -17,9 +17,9 @@ public class SyncthingRunnableTest extends AndroidTestCase {
@SmallTest @SmallTest
public void testRunning() throws InterruptedException { public void testRunning() throws InterruptedException {
MockContext context = new MockContext(getContext()); MockContext context = new MockContext(getContext());
File testFile = new File(context.getFilesDir(), "was_running"); File testFile = new File(context.getFilesDir(), SyncthingRunnable.UNIT_TEST_PATH);
assertFalse(testFile.exists()); assertFalse(testFile.exists());
// Inject a differenct command instead of the syncthing binary for testing. // Inject a different command instead of the Syncthing binary for testing.
new SyncthingRunnable(context, "touch " + testFile.getAbsolutePath() + "; exit\n").run(); new SyncthingRunnable(context, "touch " + testFile.getAbsolutePath() + "; exit\n").run();
assertTrue(testFile.exists()); assertTrue(testFile.exists());
testFile.delete(); testFile.delete();

View File

@ -37,6 +37,7 @@ public class SettingsFragment extends PreferenceFragment
private static final String EXPORT_CONFIG = "export_config"; private static final String EXPORT_CONFIG = "export_config";
private static final String IMPORT_CONFIG = "import_config"; private static final String IMPORT_CONFIG = "import_config";
private static final String STTRACE = "sttrace"; private static final String STTRACE = "sttrace";
private static final String SYNCTHING_RESET = "streset";
private static final String SYNCTHING_VERSION_KEY = "syncthing_version"; private static final String SYNCTHING_VERSION_KEY = "syncthing_version";
@ -139,12 +140,13 @@ public class SettingsFragment extends PreferenceFragment
mSyncOnlyWifi.setOnPreferenceChangeListener(this); mSyncOnlyWifi.setOnPreferenceChangeListener(this);
screen.findPreference(EXPORT_CONFIG).setOnPreferenceClickListener(this); screen.findPreference(EXPORT_CONFIG).setOnPreferenceClickListener(this);
screen.findPreference(IMPORT_CONFIG).setOnPreferenceClickListener(this); screen.findPreference(IMPORT_CONFIG).setOnPreferenceClickListener(this);
screen.findPreference(SYNCTHING_RESET).setOnPreferenceClickListener(this);
user.setOnPreferenceChangeListener(this); user.setOnPreferenceChangeListener(this);
password.setOnPreferenceChangeListener(this); password.setOnPreferenceChangeListener(this);
// Force summary update and wifi/charging preferences enable/disable.
onPreferenceChange(mAlwaysRunInBackground, mAlwaysRunInBackground.isChecked());
sttrace.setOnPreferenceChangeListener(this); sttrace.setOnPreferenceChangeListener(this);
// Force summary update and wifi/charging preferences enable/disable.
onPreferenceChange(mAlwaysRunInBackground, mAlwaysRunInBackground.isChecked());
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
user.setSummary(sp.getString("gui_user", "")); user.setSummary(sp.getString("gui_user", ""));
sttrace.setSummary(sp.getString("sttrace", "")); sttrace.setSummary(sp.getString("sttrace", ""));
@ -272,6 +274,9 @@ public class SettingsFragment extends PreferenceFragment
SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show(); SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show();
} }
return true; return true;
case SYNCTHING_RESET:
((SyncthingActivity) getActivity()).getApi().resetSyncthing(getActivity());
return true;
default: default:
return false; return false;
} }

View File

@ -379,6 +379,33 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
.show(); .show();
} }
/**
* Reset Syncthing's indexes when confirmed by a dialog.
*/
@TargetApi(11)
public void resetSyncthing(final Activity activity) {
final Intent intent = new Intent(mContext, SyncthingService.class)
.setAction(SyncthingService.ACTION_RESET);
AlertDialog.Builder builder = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
? new AlertDialog.Builder(activity, AlertDialog.THEME_HOLO_LIGHT)
: new AlertDialog.Builder(activity);
builder.setTitle(R.string.streset_title);
builder.setMessage(R.string.streset_question)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mContext.startService(intent);
Toast.makeText(activity, R.string.streset_done, Toast.LENGTH_LONG).show();
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) { }
})
.show();
}
/** /**
* Creates a notification prompting the user to restart the app. * Creates a notification prompting the user to restart the app.
*/ */

View File

@ -27,18 +27,52 @@ public class SyncthingRunnable implements Runnable {
private static final String TAG_NATIVE = "SyncthingNativeCode"; private static final String TAG_NATIVE = "SyncthingNativeCode";
public static final String UNIT_TEST_PATH = "was running";
private final Context mContext; private final Context mContext;
private boolean mGenerate;
private String mCommand; private String mCommand;
public enum Command {
generate, // Generate keys, a config file and immediately exit.
main, // Run the main Syncthing application.
reset, // Reset Syncthing's indexes
}
/** /**
* Constructs instance. * Constructs instance.
* *
* @param command The exact command to be executed on the shell. * @param command Which type of Syncthing command to execute.
*/ */
public SyncthingRunnable(Context context, String command) { public SyncthingRunnable(Context context, Command command) {
mContext = context; mContext = context;
mCommand = command; String syncthing = mContext.getApplicationInfo().dataDir + "/" + SyncthingService.BINARY_NAME;
switch (command) {
case generate:
mCommand = syncthing + " -generate='" + mContext.getFilesDir() + "' -no-browser";
break;
case main:
mCommand = syncthing + " -home='" + mContext.getFilesDir() + "' -no-browser";
break;
case reset:
mCommand = syncthing + " -home='" + mContext.getFilesDir() + "' -reset";
break;
default:
Log.w(TAG, "Unknown command option");
mCommand = "";
}
}
/**
* Constructs instance.
*
* @param manualCommand The exact command to be executed on the shell. Used for tests only.
*/
public SyncthingRunnable(Context context, String manualCommand) {
mContext = context;
mCommand = manualCommand;
} }
@Override @Override
@ -48,7 +82,7 @@ public class SyncthingRunnable implements Runnable {
int ret = 1; int ret = 1;
Process process = null; Process process = null;
try { try {
// Loop to handle syncthing restarts (these always have an error code of 3). // Loop to handle Syncthing restarts (these always have an error code of 3).
do { do {
ProcessBuilder pb = new ProcessBuilder("sh"); ProcessBuilder pb = new ProcessBuilder("sh");
Map<String, String> env = pb.environment(); Map<String, String> env = pb.environment();
@ -62,9 +96,13 @@ public class SyncthingRunnable implements Runnable {
process = pb.start(); process = pb.start();
dos = new DataOutputStream(process.getOutputStream()); dos = new DataOutputStream(process.getOutputStream());
// Call syncthing with -home (as it would otherwise use "~/.config/syncthing/". // Set (Android) home directory to data folder for syncthing to use.
dos.writeBytes(mCommand + " -home " + mContext.getFilesDir() + "\n"); dos.writeBytes("HOME=" + Environment.getExternalStorageDirectory() + " ");
dos.writeBytes("exit\n"); dos.writeBytes("STTRACE=" + pm.getString("sttrace", "") + " ");
dos.writeBytes("STNORESTART=1 ");
dos.writeBytes("STNOUPGRADE=1 ");
dos.writeBytes(mCommand);
dos.writeBytes("\nexit\n");
dos.flush(); dos.flush();
log(process.getInputStream(), Log.INFO); log(process.getInputStream(), Log.INFO);

View File

@ -45,10 +45,16 @@ public class SyncthingService extends Service {
private static final String TAG = "SyncthingService"; private static final String TAG = "SyncthingService";
/** /**
* Intent action to perform a syncthing restart. * Intent action to perform a Syncthing restart.
*/ */
public static final String ACTION_RESTART = "restart"; public static final String ACTION_RESTART = "restart";
/**
* Intent action to reset Syncthing's database.
*/
public static final String ACTION_RESET = "reset";
/** /**
* Interval in ms at which the GUI is updated (eg {@link com.nutomic.syncthingandroid.fragments.DrawerFragment}). * Interval in ms at which the GUI is updated (eg {@link com.nutomic.syncthingandroid.fragments.DrawerFragment}).
*/ */
@ -150,8 +156,12 @@ public class SyncthingService extends Service {
mApi.shutdown(); mApi.shutdown();
mCurrentState = State.INIT; mCurrentState = State.INIT;
updateState(); updateState();
} } else if (ACTION_RESET.equals(intent.getAction())) {
else if (mCurrentState != State.INIT) { mApi.shutdown();
new SyncthingRunnable(this, SyncthingRunnable.Command.reset).run();
mCurrentState = State.INIT;
updateState();
} else if (mCurrentState != State.INIT) {
mDeviceStateHolder.update(intent); mDeviceStateHolder.update(intent);
updateState(); updateState();
} }
@ -200,8 +210,7 @@ public class SyncthingService extends Service {
mCurrentState = State.STARTING; mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi); registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTaskImpl(getFilesDir() + "/" + HTTPS_CERT_FILE).execute(mConfig.getWebGuiUrl()); new PollWebGuiAvailableTaskImpl(getFilesDir() + "/" + HTTPS_CERT_FILE).execute(mConfig.getWebGuiUrl());
new Thread(new SyncthingRunnable( new Thread(new SyncthingRunnable(this, SyncthingRunnable.Command.main)).start();
this, getApplicationInfo().dataDir + "/" + BINARY_NAME)).start();
Notification n = new NotificationCompat.Builder(this) Notification n = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.syncthing_active)) .setContentTitle(getString(R.string.syncthing_active))
.setSmallIcon(R.drawable.ic_stat_notify) .setSmallIcon(R.drawable.ic_stat_notify)

View File

@ -78,9 +78,7 @@ public class ConfigXml {
} }
private void generateKeysConfig(Context context) { private void generateKeysConfig(Context context) {
new SyncthingRunnable(context, context.getApplicationInfo().dataDir + "/" + new SyncthingRunnable(context, SyncthingRunnable.Command.generate).run();
SyncthingService.BINARY_NAME + " -generate='" + context.getFilesDir() + "'")
.run();
} }
public static File getConfigFile(Context context) { public static File getConfigFile(Context context) {

View File

@ -286,6 +286,17 @@ Please report any problems you encounter via Github.</string>
<!-- Toast after entering invalid password --> <!-- Toast after entering invalid password -->
<string name="toast_invalid_password">Characters : and \' are not allowed in password</string> <string name="toast_invalid_password">Characters : and \' are not allowed in password</string>
<!-- Title for the preference to reset Syncthing indexes -->
<string name="streset_title">Reset Database</string>
<!-- Syncthing was reset -->
<string name="streset_question">This action should only be performed based on a recommendation from our support team.
\nAre you sure you want to clear Syncthing\'s index database?
</string>
<!-- Syncthing was reset -->
<string name="streset_done">Successfully reset Syncthing\'s database</string>
<string name="category_about">About</string> <string name="category_about">About</string>
<!-- Settings item that opens the log activity --> <!-- Settings item that opens the log activity -->

View File

@ -115,6 +115,11 @@
android:title="@string/sttrace_title" android:title="@string/sttrace_title"
android:singleLine="true" /> android:singleLine="true" />
<Preference
android:key="streset"
android:title="@string/streset_title"
android:singleLine="true" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory